|
Программирование >> Структура ядра и системные вызовы
Когда файл устройства будет создан, любой процесс сможет установить с ним соединение, воспользовавшись 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 - максимальная емкость (в байтах)
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |