linux的进程间通信方式主要有:匿名管道、有名管道、消息队列、共享内存、信号、信号量及信号灯、socket网络通信。近日由于项目需要,用linux编写arm的应用程序,里面有几个功能模块,若干进程,进程间的通信方式选择了管道、共享内存和信号量的配合。这几天终于把程序的框架搭建好了,而我也对管道通信有了进一步的认识。
匿名管道只能用于具有亲缘关系,如父子、兄弟这样的进程间通信。创建方式
#include <unistd.h>
int pipe(int fd[2]) ;
fd为文件描述符数组,数组的两个元素是管道的读写文件描述符,fd[0]是管道读出端,fd[1]是管道的写入端。
创建成功返回0,失败返回-1 并设置全局变量error
匿名管道使用实例
#include <unistd.h> #include <stdio.h> #include <stdlib.h>
int main(int argc,char* argv[]) { int fd[2]; char buff[100]; if(pipe(fd)==-1){ perror("failed pipe"); exit(1); } if( !fork()){ /*创建写子进程*/ while(1){ printf("write process/n"); write(fd[1],"hello world/n",13); sleep(1); } } else{ sleep(1); /*父进程是读进程*/ while(1){ read(fd[0],buff,sizeof(buff) ); printf("read process/n"); printf("receive:%s/n",buff); sleep(1); } } return 0; }
程序运行结果:
[root@gylinux test_pipe]# make gcc -c -o main.o main.c gcc -o test_pipe main.o [root@gylinux test_pipe]# ./test_pipe write process write process read process receive:hello world
write process read process receive:hello world
源程序中若设置不同长度的休眠时间,比如写进程休眠时间为5秒,则读进程在读时由于管道是空,发生阻塞,读进程也休眠;若写进程休眠为5秒,写进程不会阻塞,而是一直写入数据。试将程序修改为如下:
#include <unistd.h> #include <stdio.h> #include <stdlib.h>
int main(int argc,char* argv[]) { int fd[2]; char buff[15]; if(pipe(fd)==-1){ perror("failed pipe"); exit(1); } int i=5,nt; char data[]=" :hello world/n"; if( !fork()){ /*创建写子进程*/ while(i){ printf("%d:write process/n",i); data[0]=i+'0'; i--; nt=write(fd[1],data,15); if(nt==-1){/*添加对写入文件成功与否的判断*/ perror("write error"); } else printf("write size:%d/n",nt); //sleep(1); } } else{ sleep(1); /*父进程是读进程*/ while(1){ read(fd[0],buff,sizeof(buff) ); printf("read process/n"); printf("receive:%s/n",buff); //sleep(1); } } return 0; }
程序运行结果:
5:write process write size:15 4:write process write size:15 3:write process write size:15 2:write process write size:15 1:write process write size:15 read process receive:5:hello world 这时写进程连续写入5次,读进程只显示了第一次的内容,这是因为读进程的缓冲区为100,一次就将5次的写入都读出了,并清空管道缓冲区。而printf函数只能显示'/0' 前面的内容,即第一次。
如果把读进程缓冲区大小设置为15,再试一次,程序如下:
int main(int argc,char* argv[]) { int fd[2]; char buff[15]; /*这里改为15*/ if(pipe(fd)==-1){ perror("failed pipe"); exit(1); } int i=5,nt; char data[]=" :hello world/n"; if( !fork()){ /*创建写子进程*/ while(i){ printf("%d:write process/n",i); data[0]=i+'0'; i--; nt=write(fd[1],data,15); if(nt==-1){ perror("write error"); } else printf("write size:%d/n",nt); //sleep(1); } } else{ sleep(1); /*父进程是读进程*/ while(1){ read(fd[0],buff,sizeof(buff) ); printf("read process/n"); printf("receive:%s/n",buff); //sleep(1); } } return 0; }
运行结果:
5:write process write size:15 4:write process write size:15 3:write process write size:15 2:write process write size:15 1:write process write size:15 read process receive:5:hello world
read process receive:4:hello world
read process receive:3:hello world
read process receive:2:hello world
read process receive:1:hello world
可以看到,这时能够正常显示所有的发送数据,然后管道为空时,读进程进入阻塞态。
linux的每个管道空间有限,当管道满时,写进程将无法写入,而进入阻塞态。再做一个例子,实测管道空间大小:
int main(int argc,char* argv[]) { int fd[2]; char buff[15]; if(pipe(fd)==-1){ perror("failed pipe"); exit(1); } int i=0,nt; char data[1024]=" :hello world/n"; /*每次写入1K*/ if( !fork()){ /*创建写子进程*/ while(1){ printf("%d:write process/n",i); data[0]=i+'0'; i++; nt=write(fd[1],data,sizeof(data)); if(nt==-1){ perror("write error"); exit(1); } else printf("write size:%d/n",nt); } } else{ sleep(1); /*父进程是读进程*/ while(1){ } } return 0; }
这个例子的结果是
.......61:write process write size:1024 62:write process write size:1024 63:write process write size:1024 64:write process
这个例子只写不读,程序写到第64次进入阻塞,说明缓冲区最大为64k,写满后进程阻塞。
修改data参数的长度,可以发现随每次写入的长度不同,但是最大缓冲始终为64k,小于64k的数据最少能写一次;如果把data长度修改为大于64k,则无法写入,但是进程进入阻塞态,并不报错。
通过这几个例子,可以得到结论:匿名管道在具有亲缘关系的进程通信中使用很方便。管道为空时读进程进入阻塞,管道数据满时写进程进入阻塞。管道最大的容量为64k,但是网上很多人说最大是4k,我想这可能与内核或者平台有关系。另外修改内核源代码,也可以改变管道容积。
实际使用中,一般都用自定义的数据结构体,使得每次写入和读出的数据长度一致,防止数据的丢失。
|