Linux:进程间通信--命名管道:命名管道
1进程间通信
进程间通信,英文又称IPC(InterProcess Communication),因为每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户地址空间烤到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制就称为进程间通信。
2管道
2.1概念
管道是一种最基本的IPC机制,从本质上讲,管道也是一种文件。在LInux系统中,我们经常通过符号"|"来使用管道,用以连接两个多个命令。实际上,管道是进程与进程间的数据流通管道,它是的数据可以以一种“流”的形式在进程间流动。
管道的特点:
- 单向通信。
- 依赖文件系统。管道的生存周期随进程的退出,管道就被销毁(随进程)。
- 管道只能用于血缘关系的进程,通常用于父子进程。
- 管道在进行读写操作时,以数据流方式。
- 同步访问。
2.2管道的创建
Linux系统下,管道是通过pipe函数创建的,函数原型如下:
#include<inistd.h>
int pipe(int fileds[2]);//成功返回0,失败返回-1
其中,参数fileds[2]是一个长度为2的文件描述符数组,fileds[0]是读端,fileds[1]是写端。
2.3管道的读写
在单个进程中创建管道几乎没有意义的,因为管道存在的意义就是进行进程之间的通信。上面我们介绍了管道的特点,其中一条是,管道只能用于有血缘关系的进程,所以我们通过父子进程之间的通信演示一下管道的使用。
说明:
父进程调用pipe开辟管道,得到两个文件描述符指向管道的两端,父进程调用fork创建了子进程,子进程也有两个文件描述符指向同一管道。
因为管道是单向通信的,所以父进程关闭写端,子进程关闭读端。子进程可以往管道里写,父进程可以在管道里读,数据从写端流入,从写端流出,实现进程通信。
代码:
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
//创建管道,子进程只写,父进程只读
int main()
{
int fds[2];
int ret=pipe(fds);
if(ret==-1)
{
printf("Create pipe error! errno code is:%d\n",errno);
return 1;
}
pid_t id=fork();
if(id<0)
{
printf("fork error!");
return 2;
}
else if(id==0)
{
//child
close(fds[0]);//关闭读
char* msg="I am child!";
while(1)
{
write(fds[1],msg,strlen(msg)+1);
sleep(1);
}
}
else
{
close(fds[1]);//关闭写
char buf[1024];
while(1)
{
read(fds[0],buf,sizeof(buf));
printf("father read :%s\n",buf);
}
}
return 0;
}
从图中可以验证管道的单向通信,如果想实现父进程给子进程法消息,我们该怎么办?
我们可以再新建一个管道,使父进程写,子进程读,就完美解决了这个问题。
但是使用管道还有四种特殊情况要注意(以父进程读,子进程写为例):
1.如果子进程的写端的文件描述符关闭了,此时管道还有数据,此时父进程在读取完管道剩余数据后,在此read会阻塞返回0,就像读到文件尾。
#include<stdio.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
//创建管道,子进程只写,父进程只读
int main()
{
int fds[2];
int ret=pipe(fds);
if(ret==-1)
{
printf("create pipe errro!\n");
return 1;
}
pid_t id=fork();
if(id<0)
{
printf("fork error!\n");
return 2;
}
else if(id==0)
{
//child
close (fds[0]);//关闭读
int i=0;
char* msg="I am child!";
while(i<5)
{
write(fds[1],msg,strlen(msg)+1);
sleep(1);
++i;
}
close(fds[1]);//写入5次,关闭写
}
else
{
//father
close(fds[1]);//关闭写
char buf[1024];
int j=0;
while(j<100)
{
memset(buf,'\0',sizeof(buf));
int ret=read(fds[0],buf,sizeof(buf));
printf("%s; code is:%d\n",buf,ret);
++j;
}
if(waitpid(id,NULL,0)<0)
return 3;
}
return 0;
}
2.如果子进程的写端没有关闭,但是也没有向管道写数据,此时父进程读取完管道的剩余数据,再次read会阻塞,直到管道中有数据读才返回。
#include<stdio.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<sys/wait.h>
#include<string.h>
//创建管道,子进程只写,父进程只读
int main()
{
int fds[2];
int ret=pipe(fds);
if(ret==-1)
{
printf("create pipe errro!\n");
return 1;
}
pid_t id=fork();
if(id<0)
{
printf("fork error!\n");
return 2;
}
else if(id==0)
{
//child
close (fds[0]);//关闭读
int i=0;
char* msg="I am child!";
while(1)
{
if(i<3)
{
write(fds[1],msg,strlen(msg)+1);
}
sleep(1);
}
// close(fds[1]);//写入5次,关闭写
}
else
{
//father
close(fds[1]);//关闭写
char buf[1024];
int j=0;
while(j<20)
{
memset(buf,'\0',sizeof(buf));
int ret=read(fds[0],buf,sizeof(buf));
printf("%s; code is:%d\n",buf,ret);
++j;
}
if(waitpid(id,NULL,0)<0)
return 3;
}
return 0;
}
3.如果父进程的读端关闭了,这时子进程向管道写,那么进程会收到SIGPIPE,通常会导致程序异常终止。
#include<stdio.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<sys/wait.h>
#include<string.h>
//创建管道,子进程只写,父进程只读
int main()
{
int fds[2];
int ret=pipe(fds);
if(ret==-1)
{
printf("create pipe errro!\n");
return 1;
}
pid_t id=fork();
if(id<0)
{
printf("fork error!\n");
return 2;
}
else if(id==0)
{
//child
close (fds[0]);//关闭读
int i=0;
char* msg="I am child!";
while(1)
{
if(i<10)
{
write(fds[1],msg,strlen(msg)+1);
}
sleep(1);
++i;
}
// close(fds[1]);//写入5次,关闭写
}
else
{
//father
close(fds[1]);//关闭写
char buf[1024];
int j=0;
while(j<5)
{
memset(buf,'\0',sizeof(buf));
int ret=read(fds[0],buf,sizeof(buf));
printf("%s; code is:%d\n",buf,ret);
++j;
}
close(fds[0]);
sleep(10);//睡眠10秒,终止
if(waitpid(id,NULL,0)<0)
return 3;
}
return 0;
}
4.父进程的读端口没关闭,但是父进程也没有从管道里读文件,当子进程向管道写数据时,管道被写满时,再次write会阻塞,直到管道有空位置才写入数据并返回。
#include<stdio.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<sys/wait.h>
#include<string.h>
//创建管道,子进程只写,父进程只读
int main()
{
int fds[2];
int ret=pipe(fds);
if(ret==-1)
{
printf("create pipe errro!\n");
return 1;
}
pid_t id=fork();
if(id<0)
{
printf("fork error!\n");
return 2;
}
else if(id==0)
{
//child
close (fds[0]);//关闭读
int i=0;
char* msg="I am child!";
while(1)
{
write(fds[1],msg,strlen(msg)+1);
printf("%s; code is:%d\n",msg,i);
// sleep(1);
++i;
}
}
else
{
//father
close(fds[1]);//关闭写
}
return 0;
}