哈农第一条钢琴谱:linux进程间通信方式之匿名管道

来源:百度文库 编辑:中财网 时间:2024/04/27 13:42:47

linux的进程间通信方式主要有:匿名管道、有名管道、消息队列、共享内存、信号、信号量及信号灯、socket网络通信。近日由于项目需要,用linux编写arm的应用程序,里面有几个功能模块,若干进程,进程间的通信方式选择了管道、共享内存和信号量的配合。这几天终于把程序的框架搭建好了,而我也对管道通信有了进一步的认识。

 

匿名管道只能用于具有亲缘关系,如父子、兄弟这样的进程间通信。创建方式

 

#include

int pipe(int fd[2]) ;

 

fd为文件描述符数组,数组的两个元素是管道的读写文件描述符,fd[0]是管道读出端,fd[1]是管道的写入端。

创建成功返回0,失败返回-1 并设置全局变量error

 

匿名管道使用实例

#include
#include
#include

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
#include
#include

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,我想这可能与内核或者平台有关系。另外修改内核源代码,也可以改变管道容积。


实际使用中,一般都用自定义的数据结构体,使得每次写入和读出的数据长度一致,防止数据的丢失。