《UNIXLinux程序设计教程》一3.7 非阻塞IO

    xiaoxiao2022-05-12  135

    3.7 非阻塞I/O

    前面几节已介绍了完成各种I/O的系统调用,如read()、write()、open()等,这些系统调用在默认情形下均是阻塞的,也就是说,调用必须等待操作完成,即读写到数据,才能返回。但在有些应用中往往还有需要非阻塞I/O的情形。本节我们讨论使得这些调用成为非阻塞的方法。UNIX系统调用根据阻塞还是非阻塞分为两类:一类是所谓的“慢”系统调用,其他的则归为另一类。慢系统调用是以下有可能被永久阻塞的调用:调用read()读管道、终端设备或网络设备文件时,如果数据不出现则可能永久阻塞调用者;调用write()写同样的文件,如果数据不能立即被接收也可能永久阻塞调用者。打开一个文件将阻塞直至出现某种条件。例如,打开一个终端设备将等待相连的调制解调器回应;打开一个只写的FIFO,但没有其他进程打开它用于读。读写一个具有强制锁(10.1节)的文件。某些ioctl()操作。某些进程间通信函数(参见第11章)。默认情况下,关于I/O的系统调用总是阻塞的,调用必须等待操作完成,即读写到数据才能返回。但是慢系统调用并不包括所有阻塞I/O,这里包括的只是可能永久阻塞的系统调用。例如,磁盘I/O有关的系统调用就不是慢系统调用,尽管读写磁盘文件也可能临时阻塞调用者。非阻塞I/O可以使得I/O操作如open()、read()或write()不会被永久阻塞,如果操作不能完成,这些调用将立即返回并给出错误码指明操作可能被阻塞。对于给定的文件描述字,有两种方法指定非阻塞I/O:1)在open()时指定O_NONBLOCK文件状态标志。2)对已经打开的描述字,调用fcntl()函数设置O_NONBLOCK文件状态标志。例3-7 在非阻塞I/O情况下传输数据,当读写不能立即完成时会返回–1并置errno为EAGAIN。这时,往往需要再次调用read()或write()。我们来看一个非阻塞I/O的程序例子,这个例子使用了程序3-6给出的函数set_nonblock_flag()来设置和清除O_NONBLOCK标志。程序3-7 非阻塞I/O之例

    #include "ch03.h" #include "p3-6.c" #define B_SIZE 100000 char buf[B_SIZE]; char fmt[] = "%d Hi :( -> :) ---aha!---"; int main(void) { int nbytes=0, j=0, nwrite, ntimes = 1, success=0; char *ptr; if(set_nonblock_flag(STDOUT_FILENO, 1) < 0) /* 设置无阻塞I/O */ err_exit("set nonblock flag failed "); while (nbytes+sizeof(fmt) < B_SIZE) { sprintf(&buf[nbytes],fmt,j++); nbytes += sizeof(fmt); } /* 将buf中的数据写至标准输出,直至全部写出 */ for(ptr=buf; nbytes >0; ntimes++){ errno = 0; nwrite = write(STDOUT_FILENO, ptr, nbytes); if(nwrite < 0){ fprintf(stderr,"\n%d nwrite=%d, error=%d ",ntimes,nwrite,errno); perror(""); } else { fprintf(stderr,"\n%d nwrite=%d, error=%d ",ntimes,nwrite,errno); ptr += nwrite; success++; nbytes -= nwrite; } } printf("success=%d\n",success); exit(0); }

    程序3-7以非阻塞方式将buf中的数据全部写到标准输出文件。因为是非阻塞方式,write()操作有可能由于阻塞而没有完成或者只写了一部分数据就返回。为了保证buf中的数据全部写至标准输出文件,我们将write()放置在一个循环内,并以写完nbytes字节作为循环终止条件。执行这个程序时,若标准输出文件是磁盘文件,则只需一次write()便可写出全部数据:

    %a.out>file 1 nwrite=99996, error=0 %ls 杔 file -rw-r--r-- 1 zkj zkj 99996 Jun 22 21:15 l

    但是,若标准输出文件是终端,则会多次调用write(),其中有一些写出部分数据,有一些则返回错误。这是因为当执行下一个write()时,终端还来不及显示完前面输出的数据而以EAGIN错误返回。

    %a.out 2>output ... 显示大量写出的内容 --3702 Hi :( -> :) ---aha!---success=43
    转载请注明原文地址: https://yun.8miu.com/read-9377.html

    最新回复(0)