1.进程间通信介绍
数组传输:一个进程需要将它的数据发送给另一个进程
资源共享:多个进程之间共享同样的资源
通知事件:一个进程需要向另一个或者一组进程发送信息,通知发送了某种事件(如进程终止时要通知父进程)
进程控制:有写进程要完全控制另一个进程的执行(如debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
进程间通信发展
管道
System V进程间通信
POSIX进程间通信
进程间通信分类
管道
匿名管道pipe
命名管道
Symtem V IPC
Symtem V 消息队列
Symtem V 共享内存
Symtem V 信号量
POSIX IPC
消息队列
共享内存
信号量
互斥量
条件变量
读写锁
2.管道
什么是管道
管道是Unix中最古老的进程间通信的形式
把从一个进程连接到另一个进程得到一个数据流称为一个“管道”
匿名管道
#include<unistd.h>
//功能:创建一个无名管道
//原型
int pipe(int fd[2]);
//参数
//fd:文件描述符数组,其中fd[0]表示读端,fd[1]表示写端
//返回值:成功返回0,失败返回错误代码
示例代码
fds[2]:用来存储管道的俩个文件描述符。fds[0]是读端,fds[1]是写端。
fgets:从标准键盘读取最多99个字符(加上/0),存储到buf中。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
int fds[2];
char buf[100];
int len;
if (pipe(fds) == -1)
perror("make pipe"), exit(1);
// read from stdin
while (fgets(buf, 100, stdin)) {
len = strlen(buf);
// write into pipe
if (write(fds[1], buf, len) != len) {
perror("write to pipe");
break;
}
memset(buf, 0x00, sizeof(buf));
// read from pipe
if ((len = read(fds[0], buf, 100)) == -1) {
perror("read from pipe");
break;
}
// write to stdout
if (write(1, buf, len) != len) {
perror("write to stdout");
break;
}
}
}
3.用fork来共享管道原理
子进程可以继承父进程,但是仅限于内存级别,管道文件是不会继承的,但之所以共享是,进程地址里结构体有指针指向这个文件,而这个指针是可以被继承的,也就是说子进程也会有指针指向这个管道文件,然后匿名管道是单向的,父进程输入子进程接受,子进程输入父进程接受,读端和写端只能开一个。
深度理解管道
父子进程共享的文件描述表,对于读端和写端,若是父子进程有一个挂掉了,对应的资源也不会释放,因为是共享的,所以会有计数拷贝,只有计数为0才会释放资源,然后inode和ops以及缓冲区是不会继承的,因为这是文件的,不属于内存。
进程通信的前提条件是让不同的进程,先看到同一份资源(内存),因为父子进程有继承的关系,就可以共享文件描述表,这样就可以有同一份资源了,都指向同一个文件,通过文件来进行通信,实现匿名管道通信,比如父进程通知子进程,就把父进程的对应读端文件描述符关闭,然后子进程的对应写端文件描述关闭。
站在内核角度-管道本质
数据页:是管道中实际存储数据的部分。它是一个缓冲区,用于暂存一个进程流向另一个进程的数据。
管道工作的原理
管道写操作(write):当进程1先管道写数据时,数据首先被写入进程1的文件结构中的private_data部分,然后被传到inode,表示管道的当前状态。最后,数据被写入数据页,等待被另一个进程读取。
管道读操作(read):
当进程2从管道读取数据时,数据首先从数据页中读取。然后,数据被传送到进程2的文件结构中的private_data部分。最后,数据被进程2处理或进一步传递。
俩个操作之间的连接,通过内核中的inode和数据页,实际上就代表了管道。管道可以被认为是内核中用于连接一个进程的写操作和一个或多个进程的读操作的缓冲区。
图中并没有直接画出管道对象本身,但是通过进程1和进程2的文件结构以及它们与内核交互的方式,展示了管道操作的本质。管道在这里是抽象的概念,指的是进程间通过文件描述符和内核文件系统进行通信的机制。
4.管道样例
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>
int main(int argc,char* argv[])
{
int pipefd[2];
if(pipe(pipefd)==-1)
{
perror("pipe error");
exit(-1);
}
pid_t pid;
pid=fork();
if(pid==-1)
perror("fork fail");
if(pid==0)
{
close(pipefd[0]);
write(pipefd[1],"hello",5);
close(pipefd[1]);
close(pipefd[1]);
exit(0);
}
close(pipefd[1]);
char buf[10]={0};
read(pipefd[0],buf,10);
printf("buf=%s\n",buf);
return 0;
}