Теория и практика программирования на Си в Unix

       

Не-блокирующие операции


По умолчанию, операции ввода-вывода, осуществляемые над каким-либо файлом являются блокирующими. Необходимо подождать получения или записи требуемого числа байтов, либо события, указывающего на конец операции. Данную ситуацию можно изменить посредством системных вызовов fcntl () или ioctl (). Операции ввода-вывода, в этом случае, не блокируются и, если эти операции не удовлетворены, посылается сообщение об ошибке. Для определения того,как функционируют примитивы ввода-вывода в режиме отсутствия блокировки, настоятельно рекомендуется изучить документацию предприятия-изготовителя.

ПРОГРАММА 8 /*Блокиpующий ввод-вывод */

#include <fcntl.h>

#include <stdio.h>

#include <sys/ioctl.h>

main() { int fd; /*дескpиптоp файла */ int on = 1, off = 0; /*пеpеменная для ioct1()*/ char buf[80]; /*буфеp */

/*используется stdin */ fd = 0;

/*сначала оpганизуется неблокиpующий ввод-вывод, а затем блокиpующий */

#ifdef BSD /*обpаботчик BSD */ /*неблокиpующий ввод-вывод */ fcntl(fd, F_SETFL, FNDELAY|fcntl(fd, F_GETFL, 0)); if (read(fd, buf, sizeof(buf)) < 0) perror("rien a lire"); /*блокиpующий ввод-вывод */ fcntl(fd, F_SETFL,~FNDELAY&fcntl(fd, F_GETFL, 0));

if (read(fd, buf, sizeof(buf)) < 0) perror("erreur"); /*можно также использовать ioct1 */ /*неблокиpующий ввод-вывод */ ioctl(fd, FIONBIO, &on); /*блокиpующий ввод-вывод */ ioctl(fd, FIONBIO, &off); #endif

#ifdef SYS5 /*обpаботчик System V */ /*неблокиpующий ввод-вывод */ fcntl(fd, F_SETFL, O_NDELAY|fcntl(fd, F_GETFL, 0)); if (read(fd, buf, sizeof(buf)) == 0) perror("rien a lire"); /*блокиpующий ввод-вывод */ fcntl(fd, F_SETFL,~O_NDELAY&fcntl(fd, F_GETFL, 0)); if (read(fd, buf, sizeof(buf)) < 0) perror("erreur"); #endif

exit(0); }


Отдельные вызовы сокетов блокируют программу в случае , если удаленный процесс не осуществил ожидаемую операцию: connect (), accept (), write (), read ()... Этой блокировки можно избежать, если объявить сокеты неблокирующими , посредством примитива ioctl () (флаг FIONBIO) или примитива fcntl () (флаг FNDELAY, если речь идет о системе BSD и флаг O_NDELAY в случае System V). В этом случае работа осуществляется следующим образом:
* accept () завершает работу сразу же с ошибкой EWOULDBLOCK;
* connect () завершает работу сразу же немедленно с ошибкой EINPROGRESS;
* recv () или read () или recvfrom возвращают -1 (FIONBIO или FNDELAY) или 0 (O_NDELAY) при отсутствии считываемых данных; в EWOULDBLOCK или EAGAIN выставляется ошибка. Использование неблокирующих операций целесообразно в том случае, когда нет необходимости довести ввода-вывода до конца. В этом случае, конец можно периодически проверять и осущест-влять другую обработку данных до окончания ввода-вывода.

ПРОГРАММА 29 /* неблокирующие операции на сокете клиента */

#include "soct.h" #include <sys/ioctl.h> #include <fcntl.h> #define TAILLEMAXI 1024 client(sock) int sock; /* дескриптор сокета */ { char buf[TAILLEMAXI]; /* буфер */ int i; /* счетчик цикла */ int on = 1, off = 0; /* значение для ioctl() */

/* неблокирующий сокет создается с помощью ioctl() или fcntl() */ /* ioctl(sock, FIONBIO,&on); */ fcntl(sock, F_SETFL, FNDELAY|fcntl(sock, F_GETFL, 0)); /* цикл передачи буферов */ /* обработка ошибок EWOULDBLOCK производится в процедурах

записи */ for (i = 0; i<5; i++) {

writes(sock, buf, TAILLEMAXI); } }

/* запись в сокет буфера, занимающего пос байт ; процедура корректируется, чтобы обеспечить обработку ошибок EWOULDBLOCK */ int writes(sock, pbuf, noc) register int sock; /* дескриптор сокета */ register char *pbuf; /* буфер */ register int noc; /* число байт */ { int nreste, necrit; nreste = noc; while (nreste > 0) { refecriture: necrit = write (sock, pbuf, nreste); if ( (necrit < 0) && (errno = EWOULDBLOCK)) { err_ret("EWOULDBLOCK");

/* повторение при выполнении блокирующей операции */ /* */

fcntl(sock, F_SETFL, ~FNDELAY&fcntl(sock, F_GETFL, 0)); goto refecriture; } if (necrit < 0) return(necrit); nreste -= necrit; pbuf += necrit; } return(noc-nreste); }



Содержание раздела