在Linux下的进程间通信之匿名管道

    每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到。所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2 在从内核缓冲区把数据读走。

  管道是一种最基本的IPC机制,由pipe函数创建:

调用pippe函数时在内核中开辟一块缓冲去(称为管道)用于通信,它有一个读端,一个写端,然后通过fileds参数传出给用户程序两个文件描述符,fileds[0]指向管道的读端,fileds[1]指向管道的写端,所以管道在用户程序看起来就像一个打开的文件,通过read 或者write;向这个文件读写数据实际是在读写内核缓冲区。

管道是实现进程间的通信需要经过以下几步:

1).创建一个管道

2).创建两个进程

3).分别关闭一个进程的读端,另一个的写端

4).实现通信

程序具体如下:

(一)

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <string.h>

#include <sys/types.h>

#include <sys/wait.h>

int main()

{

int _pipe_fd[2]={-1,-1};

int _pipe=pipe(_pipe_fd);

if(_pipe<0){

perror("pipe");

exit(1);

}

int _pid=fork();

if(_pid<0){

perror("fork");

exit(2);

}else if(_pid==0){//child

close(_pipe_fd[0]);

char buf[1024]="hello world\n";

fflush(stdout);

int count =10;

while(count--){

   char buf[1024]="hello world\n";

}


sleep(5);

}

else{//father

close(_pipe_fd[1]);

char buf[1024];

while(1){

memset(buf,'\0',sizeof(buf));

ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf));

if(_size<0){

perror("read");

exit(3);

}else{

printf("%s",buf);

}

}

}

}

return 0;

}

运行结果为:

hello world

hello world

hello world

hello world

hello world

hello world

hello world

hello world

hello world

hello world

              

因为读端一直循环,一直在读,而写端并没有在写,所以读端最终会阻塞。

(二)如果所有指向管道写端的文件描述符都关闭了,而仍然有进程从管道的读端读数据,那么管道剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <string.h>

#include <sys/types.h>

#include <sys/wait.h>

int main()

{

int _pipe_fd[2]={-1,-1};

int _pipe=pipe(_pipe_fd);

if(_pipe<0){

perror("pipe");

exit(1);

}


int _pid=fork();

if(_pid<0){

perror("fork");

exit(2);

}else if(_pid==0){//child

close(_pipe_fd[0]);

//char buf[1024]="hello world\n";

//fflush(stdout);

int count =10;

while(count--){

   char buf[1024]="hello world\n";

    write(_pipe_fd[1],buf,strlen(buf));

    sleep(2);

}

close(_pipe_fd[1]);

}

else{//father

close(_pipe_fd[1]);

char buf[1024];

int j=0;

while(j++ < 15){

memset(buf,'\0',sizeof(buf));

ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf));

if(_size<0){

perror("read");

exit(3);

}else{

printf("%s",buf);

}

                    }

    int status=0;

   if(waitpid(_pid, &status,0)==_pid){

printf("wait sucess,sigcode\n:%d",status & 0xFF);

break;

}

}

return 0;

}

程序运行结果为:

hello world

hello world

hello world

hello world

hello world

hello world

hello world

hello world

hello world

hello world

wait sucess,sigcode:0

(三)

如果有指向管道写端的文件描述符没关闭,而持有管道写端的进程也没有向管道写数据,这时有进程从管道读端读数据,那么管道中剩余的数据被读取后,再次read会被阻塞,直到管道中有数据可读了才读取数据并返回。

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <string.h>

#include <sys/types.h>

#include <sys/wait.h>

int main()

{

int _pipe_fd[2]={-1,-1};

int _pipe=pipe(_pipe_fd);

if(_pipe<0){

perror("pipe");

exit(1);

}

int _pid=fork();

if(_pid<0){

perror("fork");

exit(2);

}else if(_pid==0){//child

close(_pipe_fd[0]);

int count =10;

while(count--){

   char buf[1024]="hello world\n";

                    fflush(stdout);

if(count>5)

{

write(_pipe_fd[1],buf,strlen(buf));

}

sleep(2);

}

close(_pipe_fd[1]);


}

else{//father

close(_pipe_fd[1]);

char buf[1024];

int j=0;

while(j++ < 15){

memset(buf,'\0',sizeof(buf));

ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf));

if(_size<0){

perror("read");

exit(3);

}else{

printf("%s",buf);

}

}

int status=0;

if(waitpid(_pid, &status,0)== _pid){

printf("wait sucess,sigcode:%d\n",status & 0xFF);

}

}

return 0;

}

程序运行结果:

hello world

hello world

hello world

hello world

wait sucess,sigcode:0

(四)如果所有指向管道读端的文件描述符都关闭了,这时有进程向管道的写端write ,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <string.h>

#include <sys/types.h>

#include <sys/wait.h>

int main()

{

int _pipe_fd[2]={-1,-1};

int _pipe=pipe(_pipe_fd);

if(_pipe<0){

perror("pipe");

exit(1);

}

int _pid=fork();

if(_pid<0){

perror("fork");

exit(2);

}else if(_pid==0){//child

close(_pipe_fd[0]);

int count =10;

while(count--){

   char buf[1024]="hello world\n";

                    fflush(stdout);

if(count>5)

{

write(_pipe_fd[1],buf,strlen(buf));

}

sleep(2);

}


}

else{//father

close(_pipe_fd[1]);

char buf[1024];

int j=0;

while(j++ < 3){

memset(buf,'\0',sizeof(buf));

ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf));

if(_size<0){

perror("read");

exit(3);

}else{

printf("%s",buf);

}

}


  close(_pipe_fd[0]);

sleep(10);

int status=0;

if(waitpid(_pid, &status,0)== _pid){

printf("wait sucess,sigcode:%d\n",status & 0xFF);

}

}

return 0;

}

程序运行结果:

hello world

hello world

hello world

wait sucess,sigcode:13

(五)如果有指向管道读端的文件描述符没关闭,而持有管道读端的进程也没有向管道读数据,这时有进程从管道写端写数据,那么管道被写满后,再次write会被阻塞,直到管道中有空位置了才写入并返回。

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <string.h>

#include <sys/types.h>

#include <sys/wait.h>

int main()

{

int _pipe_fd[2]={-1,-1};

int _pipe=pipe(_pipe_fd);

if(_pipe<0){

perror("pipe");

exit(1);

}

int _pid=fork();

if(_pid<0){

perror("fork");

exit(2);

}else if(_pid==0){//child

close(_pipe_fd[0]);

int count =10;

while(1){

   char buf[1024]="hello world\n";

                    fflush(stdout);

                    write(_pipe_fd[1],buf,strlen(buf));

    sleep(2);

}

}

else{//father

close(_pipe_fd[1]);

char buf[1024]

int status=0;

if(waitpid(_pid, &status,0)== _pid){

printf("wait sucess,sigcode:%d\n",status & 0xFF);

}

                 sleep(10);

}

return 0;

}

程序运行结果便是一直被阻塞。


由此可见管道的特点为:

1):主要用于父子进程间的通信,或者是有血缘关系的进程。

2):是单向通信的,且内部有保护机制。

3):其生命周期随进程。

你可能感兴趣的:(linux,pipe)