阻塞IO与非阻塞IO

1 终端与串口写IO

#include <sys/types.h>

#include <errno.h>

#include <fcntl.h>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>


char buf[1000000];


void set_fl(int fd,int flags);

void clr_fl(int fd,int flags);

int main()

{

int ntowrite,nwrite;

char *ptr;


ntowrite=read(STDIN_FILENO,buf,sizeof(buf));

fprintf(stderr,"read %dbytes\n",ntowrite);


set_fl(STDOUT_FILENO,/*O_NONBLOCK*/O_NDELAY);

for(ptr=buf;ntowrite>0;){

errno=0;

nwrite=write(STDOUT_FILENO,ptr,ntowrite);

fprintf(stderr,"nwrite=%d,errno=%d\n",nwrite,errno);

if(nwrite>0){

ptr+=nwrite;

ntowrite-=nwrite;

}

}

clr_fl(STDOUT_FILENO,/*O_NONBLOCK*/O_NDELAY);

exit(0);

}


void set_fl(int fd,int flags)

{

int val;

if((val=fcntl(fd,F_GETFL,0))<0)

perror("fcntl F_GETFLerror");

val |=flags;

if(fcntl(fd,F_SETFL,val)<0)

perror("fcntl F_SETFLerror");

}

void clr_fl(int fd,int flags)

{

int val;

if((val=fcntl(fd,F_GETFL,0))<0)

perror("fcntl F_GETFLerror");

val &=~flags;

if(fcntl(fd,F_SETFL,val)<0)

perror("fcntl F_SETFLerror");

}

此程序在RedHatLinux9.0上的运行结果为无论是否指定O_NONBLOCK标志都为下图所示:


这是UNIX环境高级编程对此的说明:此程序若在SV R 4中运行,当输出到终端上时,输出整个输入文件只需要一个wr i te。显然,阻塞与非阻塞方式并不构成区别。创建一个更大的输入文件,并且系统为运行该程序增加了程序缓存。程序的这种运行方式(即输出一整个文件,只调用一次wr i t e一直继续到输入文件长度达到约700000(最高字节大的多)字节。达到此长度后,每一个wr i t e都返回出错E A GA I N。(输入文件则决不会再输出到终端上—该程序只是连续地产生出错消息流。)

发生这种情况是因为:在SV R 4中终端驱动程序通过流I/ O系统连接到程序(区别标准库文件IO流)。流系统有它自己的缓存,它一次能从程序接收更多的数据。SV R 4的行为也依赖于终端类型—硬连线终端、控制台设备或伪终端。

把此程序的写终端STDOUT_FILENO改成串口设备(/dev/ttyS0),其结果是一样的。

2 串口读IO

#include <sys/types.h>

#include <errno.h>

#include <fcntl.h>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>


#define BUF_READ 1000

void set_fl(int fd,int flags);

void clr_fl(int fd,int flags);

int main()

{

int nread;


int fw=open("/dev/ttyS0",O_RDONLY| O_NOCTTY /*| O_NDELAY*/);

if(fw==-1){

perror("Can't open/dev/ttyS0\n");

exit(EXIT_FAILURE);

}

printf("Open /dev/ttyS0\n");


set_fl(fw,O_NONBLOCK);

char buf[BUF_READ];

int i;

for(i=0;i<3;i++){

errno=0;

printf("To read/dev/ttyS0\n");

nread=read(fw,buf,BUF_READ);

fprintf(stderr,"nread=%d,errno=%d\n",nread,errno);

perror("Read failed");

}

clr_fl(fw,O_NONBLOCK);

close(fw);

exit(0);

}


void set_fl(int fd,int flags)

{

int val;

if((val=fcntl(fd,F_GETFL,0))<0)

perror("fcntl F_GETFLerror");

val |=flags;

if(fcntl(fd,F_SETFL,val)<0)

perror("fcntl F_SETFLerror");

}

void clr_fl(int fd,int flags)

{

int val;

if((val=fcntl(fd,F_GETFL,0))<0)

perror("fcntl F_GETFLerror");

val &=~flags;

if(fcntl(fd,F_SETFL,val)<0)

perror("fcntl F_SETFLerror");

}

指定O_NONBLOCK标志时,其运行结果如下:


从结果可以看出非阻塞模式下的读操作,当无数据可读时其返回-1,并设置errno11EAGAIN。阻塞模式下,当无数据可读时,调用者将被阻塞,进程进入睡眠状态,直到有数据可读或信号中断,程序重新被唤醒。


3 终端读IO

#include <sys/types.h>

#include <errno.h>

#include <fcntl.h>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>


#define BUF_READ 1000

void set_fl(int fd,int flags);

void clr_fl(int fd,int flags);

int main()

{

int nread;


set_fl(STDIN_FILENO,O_NONBLOCK);

char buf[BUF_READ];

int i;

for(i=0;i<3;i++){

errno=0;

printf("To readSTDIN_FILENO\n");

nread=read(STDIN_FILENO,buf,BUF_READ);

fprintf(stderr,"nread=%d,errno=%d\n",nread,errno);

perror("Read failed");

}

clr_fl(STDIN_FILENO,O_NONBLOCK);

exit(0);

}


void set_fl(int fd,int flags)

{

int val;

if((val=fcntl(fd,F_GETFL,0))<0)

perror("fcntl F_GETFLerror");

val |=flags;

if(fcntl(fd,F_SETFL,val)<0)

perror("fcntl F_SETFLerror");

}

void clr_fl(int fd,int flags)

{

int val;

if((val=fcntl(fd,F_GETFL,0))<0)

perror("fcntl F_GETFLerror");

val &=~flags;

if(fcntl(fd,F_SETFL,val)<0)

perror("fcntl F_SETFLerror");

}

其运行结果与串口读IO一致。下图列出了非阻塞模式下的结构。


4 FIFO命名管道

4.1 open

O_WRONLY标志

Open调用将阻塞,直到有其他进程以读方式打开同一个FIFO文件为止。

O_WRONLY |O_NONBLOCK标志

Open调用总是立刻返回,但如果没有其他进程以读方式打开这个FIFO文件,open将返回一个错误“-1”errno的值为6(ENXIO)

O_RDONLY标志

如果用这个调用来打开FIFO文件,open调用将阻塞,除非有其他进程以写方式打开同一个FIFO文件,否则它将无法返回。

O_RDONLY |O_NONBLOCK标志

即使没有其他进程以写方式打开这个FIFO文件,这个open调用也会成功并立刻返回。

4.2 read

非阻塞IO读时,当有其他进程以写方式打开FIFO文件时,如果无数据可读read

立即返回,返回值为-1errno值为11,即EAGAIN

当没有其他进程以写方式打开这个FIFO文件时,读此FIFO文件将立即返回,返回值为0errno=0。(若某个FI F O的最后一个写进程关闭了该FI F O,则将为该F I FO的读进程产生一个文件结束标志。)

阻塞IO读时,当有其他进程以写方式打开FIFO文件时,如果无数据可读read将立即阻塞。

当没有其他进程以写方式打开这个FIFO文件时,读此FIFO文件和非阻塞IO读时一样将立即返回,返回值为0errno=0。(若某个FI F O的最后一个写进程关闭了该FI F O,则将为该F I FO的读进程产生一个文件结束标志。)

4.3 write

FIFO的缓存大小是有限制的,其最大值为PIPE_BUF=4096。写入缓存的数据若没有被以读方式打开FIFO的进程读出,将一直存在缓存中,直到被读出或者到所有打开此FIFO文件的文件描述符都关闭。

非阻塞IO写时,当有其他进程以读方式打开此FIFO文件时,一次最多可写入FIFO文件4096个字节的数据,若企图以此写入大于4096个字节的数据时,仅可写入4096个字节的数据(前提缓存中没有数据),write返回4096errno=0

Fifo_client进程一直以读方式打开FIFOfifo_server进程第一次写入2000个字节成功,第二次也写入2000个字节仍然成功,但第三次企图写入2000个字节的数据时,write失败并返回-1,此时errno=11,即EAGAIN

Fifo_client进程一直以读方式打开FIFOfifo_server进程第一次写入2000个字节成功,第二次写入5000个字节仍然成功,write成功并返回2096,此时errno=0

总结,非阻塞模式下,如果FIFO文件不能接受写入数据的全部字节,若请求写入的数据其长度等于或小于PIPE_BUF个字节,调用将失败,数据不能写入errno=11;若请求写入的数据其长度大于PIPE_BUF个字节,将部分写入数据,返回值是实际写入的字节数,不可以为0,即当缓冲区已经满的情况下,企图写入即使大于PIPE_BUF个字节的数据,仍将失败errno=11

因此如果写入数据长度等于或小于PIPE_BUF个字节数据时,或全部写入,或一个字节都不写。如果写入数据长度大于PIPE_BUF个字节数据是,只要缓冲区还有空间(大于或等于1个字节)可写,数据就会部分写入,返回实际写入的字节数,实际上也是缓冲区所剩余空间的字节数。

当企图写一个无进程为读而打开的FIFO,将产生信号SIGPIPE

阻塞IO写时,当有其他进程以读方式打开此FIFO文件时,一次可最多写入FIFO文件4096字节的数据。当一次写入数据大于4096个字节时,进程将阻塞。当一次写入数据小于4096个字节时,写入成功并返回成功写入字节数。注意理解一次可写的概念。当写进程首先往FIFO写入X个字节的数据(X<4096),若此间并没有读进程从缓存读数据,而且读写文件描述符都一直打开,写进程下次一次可写入最大字节数就为4096-X字节,当一次要写入数据大于4096-X字节时进程将阻塞。

当企图写一个尚无进程为读而打开的FIFO,则产生信号SIGPIPE

Fifo_client进程以读方式打开FIFO,并Sleep(500);等待fifo_server完成写操作,fifo_server第一次写入3000个字节的数据,第二次企图写入3000个字节数据,而是进程阻塞。若此时发送信号终止进程fifo_client,此时再无进程以读方式打开FIFO。这种情况下fifo_server进程将产生收到信号SIGPIPE并终止。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值