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,并设置errno为11即EAGAIN。阻塞模式下,当无数据可读时,调用者将被阻塞,进程进入睡眠状态,直到有数据可读或信号中断,程序重新被唤醒。
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将
立即返回,返回值为-1,errno值为11,即EAGAIN。
当没有其他进程以写方式打开这个FIFO文件时,读此FIFO文件将立即返回,返回值为0,errno=0。(若某个FI F O的最后一个写进程关闭了该FI F O,则将为该F I FO的读进程产生一个文件结束标志。)
②阻塞IO读时,当有其他进程以写方式打开FIFO文件时,如果无数据可读read将立即阻塞。
当没有其他进程以写方式打开这个FIFO文件时,读此FIFO文件和非阻塞IO读时一样将立即返回,返回值为0,errno=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返回4096,errno=0。
Fifo_client进程一直以读方式打开FIFO,fifo_server进程第一次写入2000个字节成功,第二次也写入2000个字节仍然成功,但第三次企图写入2000个字节的数据时,write失败并返回-1,此时errno=11,即EAGAIN。
Fifo_client进程一直以读方式打开FIFO,fifo_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并终止。