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
%cat output
1  nwrite=4095, error=0
2  nwrite=4095, error=0
3  nwrite=2048, error=0
4  nwrite=-1, error=11 Resource temporarily unavailable
5  nwrite=4095, error=0
6  nwrite=-1, error=11 Resource temporarily unavailable
7  nwrite=2048, error=0
...  省略的输出
20  nwrite=2048, error=0
21  nwrite=-1, error=11 Resource temporarily unavailable
22  nwrite=2048, error=0 ...  省略的输出
78  nwrite=1695, error=0
76  nwrite=-1, error=11 Resource temporarily unavailable
77  nwrite=-1, error=11 Resource temporarily unavailable
78  nwrite=1695, error=0 

这次运行过程中调用了78次write(),其中只有43次成功写出了数据,其余为错误返回,错误码为11,代表资源暂时不能满足导致操作受阻。由于程序使用了循环在所写出的字节数不满足时反复调用write(),从而使得buf中的内容最终被全部写出。
程序3-7的这类循环称为“轮询”,它存在两个问题:在多用户系统中它浪费了不少CPU时间,本来这些用于等待的CPU时间可以做其他事情或者让给其他进程。另外,它仍然没有避免永久阻塞的问题,虽然write()能立即返回,但它可能由于总是不能写出数据而导致出现死循环。为了避免死循环,我们必须设置一定的时间限制,以便时间到时能跳出循环。在10.3节介绍多路I/O时,我们可以看到解决这个问题更有效的方法。

《UNIXLinux程序设计教程》一3.7 非阻塞I/O相关推荐

  1. 《UNIXLinux程序设计教程》一2.1 UNIX 输入输出基本概念

    2.1 UNIX 输入输出基本概念 在任何一种操作系统中,程序开始读写一个文件的内容之前,必须首先在程序与文件之间建立连接或通信通道,这一过程称为打开文件.打开一个文件的目的可能是要读其中的数据,也可 ...

  2. 《UNIXLinux程序设计教程》一第2章-2.0 标准输入输出

    第2章-2.0 标准输入输出 输入输出(I/O)是任何一种编程环境中最基本的功能.本章首先讨论UNIX系统中输入输出的基本概念,然后介绍标准输入输出函数.以后各章我们将介绍更深入的输入输出概念.

  3. 5.Verilog的阻塞赋值=和非阻塞赋值<=

    FPGA教程目录 MATLAB教程目录 -------------------------------------------------------------------------------- ...

  4. Verilog初级教程(15)Verilog中的阻塞与非阻塞语句

    文章目录 前言 正文 阻塞赋值 非阻塞赋值 往期回顾 参考资料以及推荐关注 前言 本文通过仿真的方式,形象的说明阻塞赋值以及非阻塞赋值的区别,希望和其他教程相辅相成,共同辅助理解. 正文 阻塞赋值 阻 ...

  5. PHp批量推送数据太慢,PHP非阻塞批量推送数据-php教程

    明天看到论坛外面有人问如PHP何批量非梗阻向效劳器推送数据,这里大略总结下. 相干保举:<PHP教程> 一.最简略的方法: 一个剧本同时跑屡次,用参数来跑指定范畴.如果要推送10000用户 ...

  6. 《Windows网络编程案例教程》-董相志 学习记录 阻塞/非阻塞套接字编程

    <Windows网络编程案例教程>-董相志 学习记录 阻塞/非阻塞套接字编程 2.3 阻塞/非阻塞套接字编程 阻塞套接字编程通信流程图 2.3.1 阻塞套接字客户机编程 1.启动并初始化W ...

  7. 精妙的单片机非阻塞延时程序设计

    http://blog.chinaunix.net/uid-29673749-id-4425603.html 对于每个单片机爱好者及工程开发设计人员,在刚接触单片机的那最初的青葱岁月里,都有过点亮跑马 ...

  8. php 非阻塞post请求,PHP实现的CURL非阻塞调用类

    本文实例讲述了PHP实现的CURL非阻塞调用类.分享给大家供大家参考,具体如下: 前面一篇<PHP实现非阻塞模式的方法>文章讲述了PHP中实现非阻塞模式,其实如果只是HTTP的话,直接用C ...

  9. 【Verilog HDL】赋值语句之阻塞赋值方式与非阻塞赋值方式

    刚开始接触Verilog HDL语言时,这种硬件描述语言有一点与软件的程序设计语言直观上的最大区别大概就是这个赋值语句了(这里只是强调直观上的最大区别,事实上的最大区别并非如此). Verilog H ...

最新文章

  1. 高频开关电源原理_程控开关电源的工作原理
  2. Cocos2d-x游戏中默认的AndroidManifest.xml的解析
  3. java string string_深入理解Java:String
  4. [云炬创业基础笔记]第十一章创业计划书测试4
  5. python去除视频马赛克_马赛克是否无法逆转?Python简单消除,看片无忧!
  6. java map clone_Java中HashMap的clone()方法: java.util.HashMap.clone() - Break易站
  7. js工作笔记001---javascript中(function($){...})(jQuery)写法是什么意思
  8. 高速软件加密锁技术的发展历程
  9. Linux服务器安全加固
  10. python足球数据分析_Python 进行 NBA 比赛数据分析
  11. 各种激活函数求导公式
  12. ubantu14.04搜狗拼音安装
  13. 移动硬盘无法退出终极解决方法
  14. 微信公众帐号开发教程第2篇-开发模式启用及接口配置
  15. RSD的面向任务有何不同——任务目录
  16. ESD静电保护二极管应用行业举例
  17. 使用es6--对象数组的多种去重方式
  18. 合肥市专利申请费用减缓流程是怎样的
  19. 学习日记-----函数
  20. 2021年推荐网址导航提交入口

热门文章

  1. 设计模式之间可以相互功能替换吗?
  2. 生物信息课程学习 --- 比对,BLAST,马尔可夫
  3. Python_note7 文件和数据格式化+wordcloud库
  4. python之dict基础类型
  5. [转]通过脚本添加登陆/注销/开机/关机脚本
  6. C++中的static关键字的总结
  7. Kali Linux Web 渗透测试— 第十二课-websploit
  8. 分支-03. 三天打鱼两天晒网(Switch…case)
  9. 在spring中集成webservice 框架 CXF
  10. HowTO: Create an Event Log Source in code, without the Permission errors