重庆邮电大学移通学院毕业设计(论文) 第2章 Linux多进程编程
读操作序列,则在第一个读操作被唤醒并完成读操作后,其它将要执行的读操作将不再阻塞,即使在执行读操作时,FIFO中没有数据也一样(此时,读操作返回0)。
如果没有进程写打开FIFO,则设置了阻塞标志的读操作会阻塞。 向FIFO中写入数据:
约定:如果一个进程为了向FIFO中写入数据而阻塞打开FIFO,那么称该进程内的写操作为设置了阻塞标志的写操作。
对于设置了阻塞标志的写操作:
当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳要写入的字节数时,才开始进行一次性写操作。
当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。FIFO缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回。
对于没有设置阻塞标志的写操作:
当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。在写满所有FIFO空闲缓冲区后,写操作返回。
当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果当前FIFO空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;如果当前FIFO空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写;
对FIFO读写规则的验证:
下面提供了两个对FIFO的读写程序,适当调节程序中的很少地方或者程序的命令行参数就可以对各种FIFO读写规则进行验证。 程序1:写FIFO的程序
#include
#define FIFO_SERVER \main(int argc,char** argv)
26
重庆邮电大学移通学院毕业设计(论文) 第2章 Linux多进程编程
//参数为即将写入的字节数 {
int fd;
char w_buf[4096*2]; int real_wnum;
memset(w_buf,0,4096*2);
if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
printf(\if(fd==-1) {
if(errno==ENXIO) {
printf(\} }
fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0); //设置非阻塞标志
//fd=open(FIFO_SERVER,O_WRONLY,0); //设置阻塞标志
real_wnum=write(fd,w_buf,2048); if(real_wnum==-1) {
if(errno==EAGAIN)
printf(\} else {
printf(\}
real_wnum=write(fd,w_buf,5000);
//5000用于测试写入字节大于4096时的非原子性 //real_wnum=write(fd,w_buf,4096);
//4096用于测试写入字节不大于4096时的原子性 if(real_wnum==-1) if(errno==EAGAIN) printf(\}
程序2:与程序1一起测试写FIFO的规则,第一个命令行参数是请求从FIFO读出的字节数
#include
27
重庆邮电大学移通学院毕业设计(论文) 第2章 Linux多进程编程
#include
#define FIFO_SERVER \main(int argc,char** argv) {
char r_buf[4096*2]; int fd; int r_size; int ret_size;
r_size=atoi(argv[1]);
printf(\memset(r_buf,0,sizeof(r_buf));
fd=open(FIFO_SERVER,O_RDONLY|O_NONBLOCK,0); //fd=open(FIFO_SERVER,O_RDONLY,0);
//在此处可以把读程序编译成两个不同版本:阻塞版本及非阻塞版本if(fd==-1) {
printf(\exit(); }
while(1) {
memset(r_buf,0,sizeof(r_buf)); ret_size=read(fd,r_buf,r_size); if(ret_size==-1)
if(errno==EAGAIN)
printf(\
printf(\sleep(1); } pause();
unlink(FIFO_SERVER); }
程序应用说明:
把读程序编译成两个不同版本: 阻塞读版本:br 以及非阻塞读版本nbr
把写程序编译成两个四个版本:
非阻塞且请求写的字节数大于PIPE_BUF版本:nbwg
28
重庆邮电大学移通学院毕业设计(论文) 第2章 Linux多进程编程
非阻塞且请求写的字节数不大于PIPE_BUF版本:版本nbw 阻塞且请求写的字节数大于PIPE_BUF版本:bwg 阻塞且请求写的字节数不大于PIPE_BUF版本:版本bw 下面将使用br、nbr、w代替相应程序中的阻塞读、非阻塞读 验证阻塞写操作:
当请求写入的数据量大于PIPE_BUF时的非原子性: nbr 1000 bwg
当请求写入的数据量不大于PIPE_BUF时的原子性: nbr 1000 bw
验证非阻塞写操作:
当请求写入的数据量大于PIPE_BUF时的非原子性: nbr 1000 nbwg
请求写入的数据量不大于PIPE_BUF时的原子性: nbr 1000 nbw
不管写打开的阻塞标志是否设置,在请求写入的字节数大于4096时,都不保证写入的原子性。但二者有本质区别:
对于阻塞写来说,写操作在写满FIFO的空闲区域后,会一直等待,直到写完所有数据为止,请求写入的数据最终都会写入FIFO;
而非阻塞写则在写满FIFO的空闲区域后,就返回(实际写入的字节数),所以有些数据最终不能够写入。
2.3.3 信号[4][6][16]
一、信号及信号来源
29
重庆邮电大学移通学院毕业设计(论文) 第2章 Linux多进程编程
信号本质
信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。
信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。
信号来源
信号事件的发生有两个来源:硬件来源(比如我们按下了键盘或者其它硬件故障);软件来源,最常用发送信号的系统函数是kill, raise, alarm和setitimer以及sigqueue函数,软件来源还包括一些非法运算等操作。
二、信号的种类
可以从两个不同的分类角度对信号进行分类:(1)可靠性方面:可靠信号与不可靠信号;(2)与时间的关系上:实时信号与非实时信号。
1、可靠信号与不可靠信号 \不可靠信号\
Linux信号机制基本上是从Unix系统中继承过来的。早期Unix系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题, 因此,把那些建立在早期机制上的信号叫做\不可靠信号\,信号值小于SIGRTMIN(Red hat 7.2中,SIGRTMIN=32,SIGRTMAX=63)的信号都是不可靠信号。这就是\不可靠信号\的来源。它的主要问题是:
进程每次处理信号后,就将对信号的响应设置为默认动作。在某些情况下,将导致对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用signal(),重新安装该信号。
信号可能丢失,后面将对此详细阐述。
因此,早期unix下的不可靠信号主要指的是进程可能对信号做出错误的反应以及信号可能丢失。
Linux支持不可靠信号,但是对不可靠信号机制做了改进:在调用完信号处
30