Программирование >>  Структура ядра и системные вызовы 

1 ... 27 28 29 [ 30 ] 31 32 33 ... 98


Когда файл устройства будет создан, любой процесс сможет установить с ним соединение, воспользовавшись API open. После этого процесс сможет с помошью API read, write, stat и close выполнять с файлом необходимые операции. API Iseek применим только к блок-ориентированным файлам устройств. Удалить файл устройства можно, воспользовавшись API unlink.

Когда процесс вызывает функцию open для установления соединения с файлом устройства, он может указать флаги 0 NONBLOCK и 0 NOCTTY, определенные в стандарте POSIX. 1. Используются они следующим образом.

Если вызывающий процесс в ОС UNIX не имеет управляющего терминала и открывает байт-ориентированный файл устройства, ядро назначает этот файл устройства управляющим терминалом данного процесса. Если же в вызове open установлен флаг 0 NOCTTY, это назначение подавляется.

Флаг 0 NONBLOCK указывает на то, что вызов open и все последующие вызовы read и write для файла устройства не должны блокировать процесс.

Применять API mknod для создания файлов устройств могут только пользователи с особыми правами (например, пользователь rootb UNIX). Все остальные могут читать и записывать файлы устройств так, как будто это обычные файлы, но с учетом установленных для них прав доступа.

Ниже приведена программа testjnknod. С, которая иллюстрирует использование API mknod, open, read, write и close с байт-ориентированным файлом устройства:

tinclude tinclude tinclude tinclude tinclude tinclude tinclude

<iostream.h>

<stdio.h>

<sys/types.h>

<sys/stat.h>

<unistd.h>

<fcntl.h>

<stdlib.h>

int main( int argc, char* argv[]) {

if (argc !=4) {

; cout usage: argv[0] <file> <major no> <minor no>\n ; return 0;.

-).}/

int major = atoi(argv[2]) , minor = atoi(argv[3]); (void)mknod(argv[1],S IFCHRS IRWXUS IRWXGIS IRWXO,

(major <8) minor); int rc=l, fd = open(argv[1],0 RDWR0 NONBLOCK0 NOCTTY); char buf[256];

while (rc S& fd!=-l)

if ((rc=read(fd,buf,sizeof(buf))) < 0) perror( read );

else if (rc) cout buf endl; close(fd);

return 0; ) /* main */

Программа принимает три аргумента: имя файла устройства, старший номер устройства и младший номер устройства. Используя названные аргументы, программа создает с помощью вызова mknod байт-ориентированный файл устройства. Затем она открывает этот файл для чтения и записи и устанавливает флаги 0 NONBLOCK и 0 NOCTTY. После этого программа читает из файла данные и направляет их на стандартный вывод. Встретив признак конца файла, программа закрывает дескриптор, связанный с файлом устройства, и завершает выполнение.

Пользователям следует помнить, что файлы устройств обрабатываются почти так же, как обычные файлы. Различия заключаются лишь в способе создания файлов устройств и в невозможности применения к байт-ориентированным файлам устройств вызова Iseek.

7.5. API FIFO-файлов

FIFO-файлы называют также именованными каналами. Это особые файлы канальных устройств, используемые для межпроцессного взаимодействия. В частности, процесс может подключиться к FIFO-файлу для чтения, записи и чтения-записи данных. Данные, записываемые в FIFO-файл, хранятся в буфере фиксированного размера (PIPE BUF, определяется в заголовке <limits.h>) и выбираются по алгоритму первым пришел - первым вышел .

Для создания FIFO-файлов в BSD UNIX и POSIX. 1 определен API mkfifo:

#include <sys/types.h> #include <sys/stat.h> #include <unistd.h>

int mkfifo ( const char* path name, mode t mode );

Аргумент pathname - это путевое имя создаваемого FIFO-файла. Аргумент mode задает права доступа к нему для владельца, группы и прочих пользователей, а также устанавливает флаг S IFIFO, который показывает, что это FIFO-файл. Права доступа модифицируются значением umask вызывающего процесса. Идентификаторы владельца и группы для FIFO-файла назначаются так же, как для обычных файлов.

Так, чтобы создать FIFO-файл FIFOS с правами доступа на чтение, запись и вьшолнение для всех пользователей, применяется следующий системный вызов mkfifo:

mknod( FIFOS , S IFIFOS IRWXUS IRWXGS IRWXO );

В случае успешного выполнения mkfifo возвращает О, в случае неудачи - -1. Среди возможных причин неудачи можно назвать ошибку в путевом



имени, отсутствие у процесса права на создание файла устройства, ошибку в аргументе mode.

В UNIX System V.3 для создания FIFO-файлов используется API mknod, а UNIX System V.4 поддерживает API mkfifo.

После того как FIFO-файл создан, любой процесс имеет право установить с ним соединение, воспользовавшись API open. Затем процесс может с помощью интерфейсов read, write, stat и close выполнять над файлом необходимые операции. API Iseek к FIFO-файлам не применим. Удалить FIFO-файл можно посредством API unlink.

Если процесс открывает FIFO-файл только для чтения, то ядро блокирует данный процесс до тех пор, пока другой процесс не откроет этот же файл для записи. Если процесс открывает FIFO-файл для записи, то он будет заблокирован до тех пор, пока другой процесс не откроет его для чтения. Такая схема обеспечивает синхронизацию процессов. Кроме того, если процесс попробует записать данные в заполненный FIFO-файл, этот процесс будет заблокирован до тех пор, пока другой процесс не прочитает из файла данные и не освободит таким образом место для новых данных. Если процесс попытается прочитать данные из пустого FIFO-файла, он будет заблокирован до тех пор, пока другой процесс на запишет в этот файл какие-нибудь данные.

Если процессу необходимо избежать блокировки, он может указать в вызове open для FIFO-файла флаг 0 NONBLOCK. При наличии этого флага API open не блокирует процесс, даже если к другому концу канала не подсоединен ни один процесс. Более того, если затем процесс вызовет для такого FIFO-файла API read или write, а данные для пересылки готовы не будут, эти функции немедленно возвратят значение -1 и установят для кода ошибки значение EAGAIN. В таком случае процесс сможет продолжить выполнение других операций и попробовать вызвать упомянутые функции позже. В UNIX System V определен флаг 0 NDELAY, который похож на флаг 0 NONBLOCK. Различие между ними состоит в том, что при наличии флага 0 NDELAY функции read и write возвращают нулевое значение в тех случаях, когда они должны блокировать процесс. Однако при этом бывает трудно определить является ли такое их значение признаком конца файла, или же причина состоит в том, что файл пуст (в последнем случае для него сохраняется возможность записи). UNIX System V.4 поддерживает и флаг 0 NDELAY, и флаг 0 NONBLOCK.

Следует отметить еще один важный момент, касающийся FIFO-файлов. Если процесс записывает данные в FIFO-файл, с которым не взаимодействует в режиме чтения никакой другой процесс, то ядро посылает в него сигнал SIGPIPE (сигналы рассматриваются в главе 9), с тем чтобы уведомить о недопустимости данной операции. Если процесс попробует прочитать FIFO-файл, с которым ни один процесс не взаимодействует в режиме записи, то он прочитает оставшиеся в файле данные и признак конца файла. Таким образом, при взаимодействии двух процессов через FIFO-файл записывающий процесс после заверщения работы должен закрыть свой дескриптор файла, так как читающий процесс должен увидеть признак конца файла.

Процесс может открыть FIFO-файл для чтения и записи. В стандарте POSIX. 1 не указывается, должно ли ядро при этом блокировать процесс, а в UNIX-системах процесс вызовом open не блокируется. Для чтения данных из FIFO-файла и записи данных в него процесс может воспользоваться дескриптором файла, возвращенным из API open.

На примере программы testJifo.C рассмотрим, как используются API mkfifo, open, read, write и close с FIFO-файлом:

finclude <iostream.h> finclude <stdio.h> finclude <errno.h> ♦include <sys/types.h> finclude <unistd.h> finclude <fcntl.h> finclude <string.h> finclude <sys/stat.h>

int main { int argc, char* argvH)

if (argc !=2 && argc !=3)

{

cout usage: argvtO] <file> {<arg>]\n ; return 0;

] . . ,

int fd;

char buf[256];

(void)mkfifo(argv 11],S IFIFOS IRWXUS IRWXG i SIRWXO);

if (argc==2) /* читающий процесс */

fd = open(argv[1],0 RDONLYI0 NONBLOCK); while (read(fd,buf,sizeof(buf))==-! i& errno==EAGAIN) sleep(1);

while (read(fd,buf,sizeof(buf)) > 0) cout buf endl; J else /* записывающий процесс.*/

fd = open(argv[l],0 WRONbY); write(fd,argv[2],strlen(argv[2]) ) ;

) , . ,

close(fd); return 0;

Эта программа принимает один или два аргумента. Первый аргумент - имя используемого FIFO-файла. Если этот файл не существует, программа создает его, вызывая mkfifo, а затем проверяет, сколько задано аргументов. Если аргумент один, программа открывает FIFO-файл только для чтения, читает из него все данные и направляет их на стандартный вывод. Если у процесса два аргумента, программа открывает FIFO-файл для записи и записывает в него значение второго аргумента. Таким образом, путем двукратного выполнения этой программы можно создать два процесса.



взаимодействующих через FIFO-файл. Предполагая, что программа компилируется в исполняемый файл a.out, получаем следующие результаты:

% a.out FF64

% a.out FF64 Hello world Hello world

# создать читающий процесс

# создать записывающий процесс

# выходная информация читающего процесса

Создавать FIFO-файлы для межпроцессного взаимодействия можно и другим способом - с помощью API ;>/ре:

#include <unistd.h> int pipe ( int fds[2] );

С помощью API pipe можно получить такой же FIFO-файл, как и воспользовавшись mkfifo, однако файл, созданный первым способом, будет нерезидентным: в файловой системе не создается никакого файла, связанного с FIFO-файлом, и ядро уничтожает его как только все процессы закрывают свои дескрипторы файлов, ссылающиеся на этот FIFO. Аргумент fds используется следующим образом: fdsfOJ - это дескриптор файла для чтения данных из FIFO-файла, а fdsflj - дескриптор файла для записи данных в FIFO-файл. Поскольку FIFO-файл нельзя обозначить путевым именем, область его применения ограничена родственными процессами: FIFO-файл создается родительским процессом, который затем порождает другие процессы; процессы-потомки наследуют от родителя дескрипторы FIFO-файла и могут через этот файл взаимодействовать друг с другом и с родительским процессом. Такое ограничение по применению FIFO-файлов, создаваемых API pipe, привело к появлению именованных каналов, которые позволяют общаться через FIFO-файлы неродственным процессам.

7.6. API символических ссылок

Символические ссылки определены в BSD UNIX 4.2 и используются в BSD 4.3, System V.3 и V.4. Символические ссылки дают возможность устранить некоторые недостатки, характерные для жестких ссылок, а именно:

их можно создавать для файлов, находящихся в другой файловой системе;

позволяют ссылаться на файл каталога;

всегда указывают последнюю версию файла.

Возможность узнать последнюю версию файла - это главное преимущество символических ссылок по сравнению с жесткими. Пусть, например, пользователь создаст файл /usr/go/testl и жесткую ссылку на него /usr/joe/hdlnk:

In /usr/go/testl /usr/joe/hdlnk

Если пользователь удалит файл /usr/go/testl, то к нему можно будет обращаться только по имени /usr/joe/hdlnk. Если затем пользователь создаст новый файл /usr/go/testl, который будет полностью отличаться от файла /usr/joe/hdlnk, то кия /usr/joe/hdlnk все равно будет обозначать старый файл, а имя /usr/go/testl - новый. Таким образом, путем удаления одной или нескольких ссылок жесткие ссылки можно разрушать.

Символическая ссылка в отличие от жесткой не разрушается. Если пользователь, создав символическую ссылку /usr/joe/symlnk:

In -s /usr/go/testl /usr/joe/symlink

уддлт /usr/go/testl, то яия/usr/joe/symlnk будет обозначать несуществующий файл и ни одна операция с этой ссылкой {cat, more, sort и т.д.) выполняться не будет. Если же пользователь создаст файл /usr/go/testl заново, то /usr/joe/symlnk автоматически укажет на этот новый файл, т.е. ссылка будет восстановлена.

Символические ссылки предложено включить в стандарт POSIX. 1. В BSD UNIX для манипулирования символическими ссылками определены следующие API:

#i ncl ude <sys/types.h> #include <sys/stat.h> #include <unistd.h>

int symlinlf ( const char* orgjink, const char* symjink ); int readlinlf { const char* sym link, char* buf, int size ); int Istat ( const char* sym link, struct stat* statv );

Аргументы orgjink и symjink в вызове symlink задают путевое имя исходного файла и путевое имя создаваемой символической ссылки. Например, чтобы создать символическую ссылку /usr/joe/lnk для файла /usr/go/testl, следует воспользоваться следующим вызовом:

symlink ( /usr/go/testl , /usr/joe/lnk );

Синтаксис вызова такой же, какой применяется для API link. В случае успешного выполнения symlink возвращает О, а в случае неудачи возвращает -1. Возможные причины сбоя: неверно зацанное путевое имя, наличие файла symjink, отсутствие у вызывающего процесса права на создание нового файла.

Чтобы запросить путевое имя, на которое указывает символическая ссылка, следует использовать API readlink. API open автоматически преобразует символическую ссылку в реальный файл, который она обозначает, и соединяет вызывающий процесс с этим файлом. API readlink принимает следующие аргументы: symjink - пугевое имя символической ссылки; buf - буфер массива символов, в котором хранится возвращаемое путевое имя, обозначенное символической ссылкой; size - максимальная емкость (в байтах)



1 ... 27 28 29 [ 30 ] 31 32 33 ... 98

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика