一、进程间进行通信的目的
我们往往需要多个进程协同,共同完成一些事情。
- 数据传输:一个进程需要将它的数据发送给另一个进程
- 资源共享:多个进程之间共享同样的资源。
- 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止 时要通知父进程)。
- 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另 一个进程的所有陷入和异常,并能够及时知道它的状态改变
进程间通信的本质:要让不同的进程看到同一份资源,这份资源一般是由操作系统提供的。操作系统提供的资源不同,就决定了有不同的通信方式。
二、管道通信
2.1、匿名管道通信的原理
基于文件的方式,让不同进程看到同一份资源的通信方式,叫做管道,管道通信只能为单向通信。如果今天我们让父进程以读和写两种方式打开同一个文件,操作系统是会为我们创建两个struct file结构体的,只不过这两个struct file结构体的缓冲区是同一个。如果我们让这个父进程创建一个子进程,子进程的PCB和文件描述符表和父进程一模一样,所以此时子进程也是以读和写两种方式打开了父进程打开的这个文件。这样操作就让父子进程看到了同一份资源。也就是说,struct file对象是允许多个进程通过指针指向它的。
前面也说过,管道通信为单向通信,所以如果想让父进程写子进程读,就关闭父进程的读端关闭子进程的写端,反之亦然。
2.2、pipe系统调用函数
pipe可以帮我们创建一个不需要向磁盘刷新且磁盘中并不存在的文件,也就是管道。这是一个内存级的文件,是匿名文件或叫匿名管道。匿名管道只能让具有血缘关系的进程进行进程通信,常用于父子进程之间进行进程通信。
2.3、匿名管道通信的简单实现
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <stdlib.h> void writer(int wfd) { char buffer[128]; int count = 0; while (1) { snprintf(buffer, sizeof(buffer), "I am your child!, pid:%d, count:%d\n", getpid(), count++); write(wfd, buffer, strlen(buffer)); sleep(1); } } void reader(int rfd) { char buffer[128]; while (1) { read(rfd, buffer, sizeof(buffer)); printf("father get message:%s", buffer); } } int main() { int pipefd[2]; int n = pipe(pipefd); int fd = fork(); if(fd == 0) { //子进程充当写端,从而关闭读端 close(pipefd[0]); writer(pipefd[1]); exit(0); } //父进程充当读端,从而关闭写端 close(pipefd[1]); reader(pipefd[0]); wait(NULL); return 0; }
父进程不断读到从子进程发来的消息: