为什么要有进程间通信?
在操作系统中,进程是独立运行的程序,多个进程之间要想互相协作完成任务,就需要进程间通信。
什么是进程间通信?
- 数据传输:一个进程需要将它的数据发送给另一个进程
- 资源共享:多个进程间共享同样的资源
- 通知事件:一个进程需要向另一个或一组进程发送消息,通知他们发送了某些事情,后者发送任务让他们执行
- 进程控制:有些进程希望完全控制另一个进程的执行,此时进程希望能够拦截另一个进程的所有陷入和异常,并且能够及时通知它的状态改变。
进程间通信的目的
为了实现资源的共享,协作和同步,从而提高系统的效率和可靠性。
进程间通信的种类
1.管道
- 匿名管道
- 命名管道
2.System V
- 消息队列
- 共享内存
- 信号量
3.POSIX V
- 消息队列
- 共享内存
- 信号量
- 互斥量
- 条件变量
- 读写锁
管道的特点
- 管道是用来进行某种血缘关系的进程间通信的一种手段 ——一般用于父子进程
- 管道具有让进程间协同、提供了访问控制。(访问控制就是当一个文件里面没有数据的时候,另一方不会去读,只会阻塞等待。当文件满的时候,不能再写入了。)
- 管道提供了面向流式的通信服务——面向字节流
- 管道是基于文件的,声明周期随进程
- 管道是单向通信的。
如何看到同一份资源?
进程间通信的本质是实现资源的共享,那么最重要的一点就是如何看到同一份资源。
先聊一聊匿名管道,匿名管道是通过父子进程后,写时拷贝后,看到的同一份资源,然后让父进程关闭读端,子进程关闭写端,这样就可以让两个进程看到同一份资源了。
以前说过,每一个进程都有一个PCB,而每个PCB结构体中都有一个指针指向了struct file*struct,这个结构体里面就有文件描述符表,每一个文件描述表中的指针指向了每个文件。
所以父子进程通过fork后,就看到了同一份资源,如下图所示,又因为管道是单向的,关闭对应的读写端,就实现了通信。
匿名管道接口
- pipe
成功返回0,失败返回-1,错误码被设置。pipefd是输出型参数,期望调用它,可以得到被打开的文件的fd。
实验
#include <iostream>
#include <unistd.h>
#include <assert.h>
#include <sys/types.h>
#include <stdlib.h>
#include <cstring>
#include <wait.h>
using namespace std;
char buffer[1024*8];
int main()
{
//创建管道
int pipefd[2] = {0};
int ret = pipe(pipefd);
assert(ret != -1);
(void)ret;
pid_t id = fork();
assert(id != -1);
if(id == 0)
{
//child
//子进程去读,关闭写端
close(pipefd[1]);
// char buffer[1024*8];
while(true)
{
//s == 0 是读到了文件末尾
// s > 0 说明在有数据,还在读取
ssize_t s = read(pipefd[0],buffer,sizeof(buffer));
if(s > 0)
{
buffer[s] = 0;
cout << "child get a message[" << getpid() << "] Father# " << buffer << endl;
}
else if(s == 0)
{
cout << "write quit(father),me quit!" << endl;
exit(2);
}
}
}
//father
//关闭读端
close(pipefd[0]);
string message = "我是父进程,我正在给你发信息";
int count = 0;
// char send_buffer[1024*8];
while(true)
{
snprintf(buffer,sizeof(buffer),"%s[%d] : %d",message.c_str(),getpid(),count++);
//向send_buffer中写入
write(pipefd[1],buffer,strlen(buffer));
sleep(1);
//cout << count << endl;
if(count == 5)
{
cout << "writer quit(father)" << endl;
break;
}
}
close(pipefd[1]);
pid_t n = waitpid(id,nullptr,0);
cout << "id : " << id << " "<< "n : " << n << endl;//这里pid结果一样,说明通信成功
assert(n > 0);
(void)n;
return 0;
}
实验现象: 父进程向管道里面写入,子进程读,当5s后,关闭管道。