目录
1.进程通信的目的
1.数据传输:一个进程需要将它的数据发送给另一个进程
2.资源共享:多个进程之间共享同样的资源
3.通知事件:一个进程需要向另一个或一组进程发送消息,通知它们发生了某件事情(例如子进程退出时要通知父进程)
4.进程控制:有些进程希望完全控制另一个进程的执行,此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时指导它的状态改变
2.进程间通信的方法
通信的本质就是操作系统直接或间接给通信双方的进程提供"内存空间",要通信的进程必须看到一份公共的资源。这份公共资源是有操作系统的不同模块提供的(所以有多种通信的方法)。
2.1管道
管道分为匿名管道和命名管道,这些管道是基于文件系统的文件。管道文件是一个内存级文件,它有属于自己的内核缓冲区,它并不需要从磁盘中读取数据。
管道的特征有如下几点:
1.管道的生命周期跟随创建此管道的进程
2.管道可以让具有亲缘关系的进程相互通信(常用于父子通信)
3.管道是面向字节流的
4.半双工 -- 管道一般只用来做单向通信
5.互斥与同步机制 -- 对共享资源进行保护的方案
2.2System V IPC
这种方案聚焦在本地通信,在现代这个万物互联的时代,这种方案是明显落后的。
1.System V 消息队列
2.System V 共享内存
3.System V 信号量
2.2POSIX IPC
这种方案使得通信可以跨主机,是现在主流的通信方法。
1.消息队列
2.共享内存
3.信号量
3.互斥量
4.条件变量
5.读写锁
3.进程间通信实例
3.1匿名管道
管道是一个文件,它是由进程打开的,也就说明当某个进程打开管道文件时,就与管道建立了读写关系。

如果在这个进程基础之上创建一个子进程,这个子进程就会拷贝父进程的东西(除了管道文件)。

此时建立的管道文件是双向通信的,不符合管道通信的特点,我们手动关闭一些文件,即可得到父子进程间的单向通信。
下面用一份很简单的代码来实现上述的通信过程:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cassert>
// 实现父进程向管道写入 "i am father"
// 子进程从管道中读取父进程发送的数据
int main()
{
int fds[2] = {0}; // 用来保存管道读端、写端的文件描述符
int n = pipe(fds); // 创建管道文件的系统调用
assert(n == 0); // pipe返回0证明创建管道成功
// 管道文件创建创建成功后,会将读、写端的文件描述符写入fds
// 其中,fds[0]表写端,fds[1]表读端
pid_t id = fork(); // 创建子进程
assert(id >= 0);
if(id == 0) // 子进程
{
close(fds[1]); // 子进程关闭写端
while(true)
{
char buffer[1024] = {0};
int s = read(fds[0],buffer,sizeof(buffer)-1); // 从管道读数据
if(s > 0)
{
buffer[s] = 0;
std::cout << buffer << std::endl;
}
else if(s == 0)
{
std::cout << "未从管道读到任何数据!" << std::endl;
}
}
close(fds[0]);
exit(0);
}
else // 父进程
{
close(fds[0]); // 父进程关闭读端
while(true)
{
char buffer[1024] = {0};
snprintf(buffer,sizeof(buffer),"i am father");
write(fds[1],buffer,strlen(buffer)); //向管道写入信息
sleep(1);
}
}
n = waitpid(id,nullptr,0); // 阻塞等待子进程退出
assert(n == id);
close(fds[1]);
return 0;
}
以上代码能够实现一个最基本的通信,但我们需要注意一些细节:
1.程序中的pipe是一个系统调用,它接收一个指针类型的参数,其内部将会在内存开辟一个管道文件,并将读端、写端的文件描述符放入参数所指的空间。返回值为0则表示正常退出
2.用来存放管道读写端的fds数组,fds[0]表管道的读端文件描述符,fds[1]表管道的写端文件描述符
3.为了更加严格的使用管道做单向通信,父进程需要关闭读端、子进程需要关闭写端
4.如果管道的写端没有被关闭,且管道内没有任何数据,则读端将会阻塞,一直等待写端向管道发送数据(即程序会停留在read,不往下执行)
5.如果管道的写端关闭,read函数将会返回0(读到0个数据)
6.管道是一个固定大小的文件(其容量是有限的)
7.如果管道的读端被关闭,那么写端存在就没有任何意义。此时write便会收到来自操作系统的终止信号
8.如果读写端都没有被关闭,写端写数据的速度较快,而读端读数据的速度较慢,就会造成管道被写满,此时就会发生写端阻塞(即程序会停留在write,不往下执行)
9.如果读写端都没有被关闭,写端写数据较慢,而读端读数据较快,就会造成管道没有任何数据,此时就会发生读端阻塞(见4)
3.2匿名管道小项目
上面的程序只实现了数据的传送。现在我们应该实现事件通知和进程控制。我们的想法如下:
1.父进程创建N个子进程和N个管道
2.父进程向某个管道发送"控制命令"
3.子进程从对应的管道读取"控制命令"
4.子进程根据"控制命令"做出相应的动作

#include <unistd.h>
#include <iostream>
#include <cassert>
#include <string>
#include <cstdio>
#include <vector>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAKE_RAND() srand((size_t)time(nullptr) ^ 15423 ^ 12)
///子进程执行的任务
void IO_mission(
最低0.47元/天 解锁文章
530

被折叠的 条评论
为什么被折叠?



