linux的進程間通信方式主要有:匿名管道、有名管道、消息隊列、共享內存、信號、信號量及信號燈、socket網絡通信。近日由于項目需要,用linux編寫arm的應用程序,里面有幾個功能模塊,若干進程,進程間的通信方式選擇了管道、共享內存和信號量的配合。這幾天終于把程序的框架搭建好了,而我也對管道通信有了進一步的認識。
匿名管道只能用于具有親緣關系,如父子、兄弟這樣的進程間通信。創(chuàng)建方式
#include <unistd.h>
int pipe(int fd[2]) ;
fd為文件描述符數組,數組的兩個元素是管道的讀寫文件描述符,fd[0]是管道讀出端,fd[1]是管道的寫入端。
創(chuàng)建成功返回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()){ /*創(chuàng)建寫子進程*/
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秒,則讀進程在讀時由于管道是空,發(fā)生阻塞,讀進程也休眠;若寫進程休眠為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()){ /*創(chuàng)建寫子進程*/
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
這時寫進程連續(xù)寫入5次,讀進程只顯示了第一次的內容,這是因為讀進程的緩沖區(qū)為100,一次就將5次的寫入都讀出了,并清空管道緩沖區(qū)。而printf函數只能顯示'/0' 前面的內容,即第一次。
如果把讀進程緩沖區(qū)大小設置為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()){ /*創(chuàng)建寫子進程*/
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
可以看到,這時能夠正常顯示所有的發(fā)送數據,然后管道為空時,讀進程進入阻塞態(tài)。
linux的每個管道空間有限,當管道滿時,寫進程將無法寫入,而進入阻塞態(tài)。再做一個例子,實測管道空間大小:
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()){ /*創(chuàng)建寫子進程*/
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次進入阻塞,說明緩沖區(qū)最大為64k,寫滿后進程阻塞。
修改data參數的長度,可以發(fā)現(xiàn)隨每次寫入的長度不同,但是最大緩沖始終為64k,小于64k的數據最少能寫一次;如果把data長度修改為大于64k,則無法寫入,但是進程進入阻塞態(tài),并不報錯。
通過這幾個例子,可以得到結論:匿名管道在具有親緣關系的進程通信中使用很方便。管道為空時讀進程進入阻塞,管道數據滿時寫進程進入阻塞。管道最大的容量為64k,但是網上很多人說最大是4k,我想這可能與內核或者平臺有關系。另外修改內核源代碼,也可以改變管道容積。
實際使用中,一般都用自定義的數據結構體,使得每次寫入和讀出的數據長度一致,防止數據的丟失。