进程间通讯的目的
- 数据传输:一个进程需要把它的数据发送给另一个数据
- 资源共享:多个进程需要共享同样的资源
- 通知事件:一个进程需要向另一个或者一组进程发送消息,通知它发生了某种事件(如进程终止时要通知父进程)
- 进程控制:有些进程希望控制另一个进程的执行,此时控制进程希望可以拦截另一个进程所有陷入和异常,并能及时知道它的状态改变
简而言之,就是需要多个进程协同共同完成一些事情
进程通讯的方式
- 匿名管道,命名管道
- System V IPC 消息队列,共享内存,信号量
- POSIX IPC 消息队列,共享内存,信号量,互斥量,条件变量,读写锁
匿名管道
pipe函数创建一无名管道
参数:fd文件描述符,其中fd[0]表示读端,fd[1]表示写端
返回值:成功返回0,失败返回返回错误代码
pipe创建的是内存级的文件,匿名文件(管道),匿名管道只能进行血缘关系进程通讯。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int fds[2];
int n = pipe(fds);
char buffer[1024];
if(n<0)//返回值小于0,创建失败
{
perror("pipe error!\n");
exit(1);
}
//fork创建子进程,可以继承父进程的PCB
pid_t pid = fork();
if(pid==0)//子进程
{
close(fds[1]);//关闭写端
while(1)
{
size_t size = read(fds[0],buffer,sizeof(buffer)-1);
if(size<=0)
{
printf("子进程读取退出\n");
exit(1);
}
buffer[size] = 0;
printf("子进程读取到:%s",buffer);
memset(buffer,0,sizeof(buffer));
}
}
//父进程关闭读端
close(fds[0]);
while(fgets(buffer,sizeof(buffer),stdin))
{
size_t len = strlen(buffer);
if(len<=0)
{
continue;
}
size_t size = write(fds[1],buffer,len);
if(size!=len)
{
perror("write error, exit!\n");
exit(2);
}
else
{
printf("父进程写端写入:%s",buffer);
}
memset(buffer,0,sizeof(buffer));
}
return 0;
}
匿名管道的4种情况
- 管道内部没有数据,写端不关闭自己的写端文件符fd,读端就要阻塞等待
- 管道内部被写满,读端不关闭自己的读端文件符fd,写端就要阻塞等待
- 对于写端而言,关闭写端文件符wfd,读端就会把pipe中数据读完,最后就会读到返回值为0,表示读结束,类似读到了文件结尾
- 对于读端而言,关闭读端文件符rfd,写端再写,操作系统就会给写端进程发送信号SIGPIPE,写端进程收到信号,默认动作中止进程。(linux信号详解)
4个注意点
- 匿名管道只能用于父子进程通讯
- pipe是面向字节流的
- 父子进程退出,管道自动释放,文件的生命周期是随进程的
- 管道只能单向通讯
其实匿名管道在内核中是下面这样: