进程间通信-匿名管道
程序员文章站
2024-03-01 13:03:58
...
匿名管道
一、特点
1.只适合单向通信。(如果需要双向通信,则需要两个匿名管道来进行完成)
2.只适合具有血缘关系的进程进行通信
3.管道是文件,生命周期随进程,进程结束后,文件就不在了
4.管道是基于字节流方式来进行通信的
5.父进程和子进程访问的公共资源叫做临界资源,所有临界资源都是需要被保护起来的,多个进程进行访问时必须要保证原子性。(任一时刻保证只有一个人访问)
6.管道内部自己已经实现同步性,能保证数据的一致性。
另外,进程间的通信的本质是:两个进程看到了一份公共的资源。
二、管道的创建
1.由父进程创建,子进程继承父进程的文件描述符,指向同一个文件,一个用来读,一个用来写,适当的关闭相应的文件读写端。
函数功能:创建管道,使得__pipe[0]指向文件的读端,_pipe[1]指向文件的写端
参数:一个含有两个整型元素的数组
返回值:0正常 -1管道创建失败
2.管道的大小
测试匿名管道的大小:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
int fd[2];
int count=0;
if(pipe(fd)<0)
{
perror("Fail to create pipe");
exit(1);
}
while(1)
{
write(fd[1],"a",sizeof(char));
printf("count=%d.\n",++count);
}
return 0;
}
可知:管道的大小是:65536个字节=2的16次方个字节=64M
三、关于匿名管道读写的四种方式
使⽤用管道需要注意以下4种特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标志):
1. 如果所有指向管道写端的⽂文件描述符都关闭了(管道写端的引⽤用计数等于0),⽽而仍然有进程 从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到⽂文件末尾⼀一样。
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<sys/wait.h>
#include<unistd.h>
int main()
{
int _pipe[2];
int ret=pipe(_pipe);
if(ret==-1)
{
printf("create pipe error ! error code is :%d\n",errno);
}
pid_t id=fork();
if(id<0)
{
printf("fork error\n");
return 2;
}
else if(id==0)//child
{
close(_pipe[0]);
int i=0;
char * _mesg_c=NULL;
while(i<10)
{
sleep(1);
_mesg_c="i am child";
write(_pipe[1],_mesg_c,strlen(_mesg_c)+1);
i++;
}
close(_pipe[1]);
}
else//father
{
close(_pipe[1]);
char _mesg[100];
int j=0;
printf("child id ;%d\n",id);
while(j<100)
{
memset(_mesg,'\0',sizeof(_mesg));
read(_pipe[0],_mesg,sizeof(_mesg)-1);
printf("%s:code count :%d\n",_mesg,j);
j++;
}
}
if(waitpid(id,NULL,0)<0)
{
printf("wait child sucessfully\n");
return 3;
}
return 0;
}
可以看出父进程读取时,发生了阻塞,每隔一秒钟读取一次。
2.如果有指向管道写端的⽂文件描述符没关闭(管道写端的引⽤用计数⼤大于0),⽽而持有管道写端的 进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<sys/wait.h>
#include<unistd.h>
int main()
{
int _pipe[2];
int ret=pipe(_pipe);
if(ret==-1)
{
printf("create pipe error ! error code is :%d\n",errno);
}
pid_t id=fork();
if(id<0)
{
printf("fork error\n");
return 2;
}
else if(id==0)//child
{
close(_pipe[0]);
int i=0;
char * _mesg_c=NULL;
while(i<20)
{
if(i<10)
{
_mesg_c="i am child";
write(_pipe[1],_mesg_c,strlen(_mesg_c)+1);
}
sleep(1);
i++;
}
// close(_pipe[1]);
}
else//father
{
/// sleep(3);
close(_pipe[1]);
char _mesg[100];
int j=0;
printf("child id ;%d\n",id);
while(j<20)
{
memset(_mesg,'\0',sizeof(_mesg));
read(_pipe[0],_mesg,sizeof(_mesg)-1);
printf("%s:code count :%d\n",_mesg,j);
j++;
}
}
if(waitpid(id,NULL,0)<0)
{
printf("wait child sucessfully\n");
return 3;
}
return 0;
}
3.如果所有指向管道读端的⽂文件描述符都关闭了(管道读端的引⽤用计数等于0),这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终⽌止。
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<sys/wait.h>
#include<unistd.h>
int main()
{
int _pipe[2];
int ret=pipe(_pipe);
if(ret==-1)
{
printf("create pipe error ! error code is :%d\n",errno);
}
pid_t id=fork();
if(id<0)
{
printf("fork error\n");
return 2;
}
else if(id==0)//child
{
close(_pipe[0]);
int i=0;
char * _mesg_c=NULL;
while(i<20)
{
if(i<10)
{
_mesg_c="i am child";
write(_pipe[1],_mesg_c,strlen(_mesg_c)+1);
}
// sleep(1);
i++;
}
}
else//father
{
close(_pipe[1]);
char _mesg[100];
int j=0;
printf("child id ;%d\n",id);
while(j<3)
{
memset(_mesg,'\0',sizeof(_mesg));
read(_pipe[0],_mesg,sizeof(_mesg)-1);
printf("%s:code count :%d\n",_mesg,j);
j++;
}
close(_pipe[0]);
}
if(waitpid(id,NULL,0)<0)
{
printf("wait child sucessfully\n");
return 3;
}
return 0;
}
进程异常终止
4.如果有指向管道读端的⽂文件描述符没关闭(管道读端的引⽤用计数⼤大于0),⽽而持有管道读端的 进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再 次write会阻塞,直到管道中有空位置了才写⼊入数据并返回。
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<sys/wait.h>
#include<unistd.h>
int main()
{
int _pipe[2];
int ret=pipe(_pipe);
if(ret==-1)
{
printf("create pipe error ! error code is :%d\n",errno);
}
pid_t id=fork();
if(id<0)
{
printf("fork error\n");
return 2;
}
else if(id==0)//child
{
close(_pipe[0]);
int i=0;
char * _mesg_c=NULL;
while(i<20)
{
_mesg_c="i am child";
write(_pipe[1],_mesg_c,strlen(_mesg_c)+1);
sleep(1);
i++;
}
}
else//father
{
close(_pipe[1]);
char _mesg[100];
int j=0;
printf("child id ;%d\n",id);
while(j<5)
{
memset(_mesg,'\0',sizeof(_mesg));
read(_pipe[0],_mesg,sizeof(_mesg)-1);
printf("%s:code count :%d\n",_mesg,j);
j++;
}
}
if(waitpid(id,NULL,0)<0)
{
printf("wait child sucessfully\n");
return 3;
}
return 0;
}
写入端发生阻塞,一直等待读取。