Linux进程间通信
目录
进程间通信介绍
进程间通信目的
数据传输:一个进程需要将它的数据发送给另一个进程
资源共享:多个进程之间共享同样的资源。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止 时要通知父进程)。
进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另 一个进程的所有陷入和异常,并能够及时知道它的状态改变。
?进程间通信发展
管道
System V进程间通信
POSIX进程间通信
?进程间通信分类
管道(基于文件)
匿名管道pipe
命名管道
?System V IPC(基于本地通信,不能跨网络)
System V 消息队列
System V 共享内存
System V 信号量
POSIX IPC?
消息队列
共享内存
信号量
互斥量
条件变量
读写锁
管道?
什么是管道
管道是Unix中最古老的进程间通信的形式。 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”
匿名管道
匿名管道的原理
匿名管道用于进程间通信,且仅限于本地父子进程之间的通信。
任何进程通信的手段
a.想办法,先让不同的进程,看到同一份资源
b.让一方写入,一方读取,完成通信过程,至于通信目的与后续工作,要结合具体场景
进程间通信的本质就是,让不同的进程看到同一份资源,使用匿名管道实现父子进程间通信的原理就是,让两个父子进程先看到同一份被打开的文件资源,然后父子进程就可以对该文件进行写入或是读取操作,进而实现父子进程间通信。?
——创建子进程的时候fork子进程,只会复制进程相关的数据结构对象 ,不会复制父进程曾经打开的文件对象
现象:这就是为什么fork之后,父子进程都printf,cout,都会向同一个显示器终端打印数据的原因
用fork来共享管道原理?
?站在文件描述符角度-深度理解管道
站在内核角度-管道本质
?
pipe函数?
?
#include <unistd.h>
功能:创建一无名管道
原型
int pipe(int fd[2]);
参数
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码
?
pipe2
pipe2函数与pipe函数类似,也是用于创建匿名管道,其函数原型如下:
int pipe2(int pipefd[2], int flags);
?
pipe2函数的第二个参数用于设置选项。
1、当没有数据可读时:
O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来为止。
O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。
2、当管道满的时候:O_NONBLOCK disable:write调用阻塞,直到有进程读走数据。
O_NONBLOCK enable:write调用返回-1,errno值为EAGAIN。
3、如果所有管道写端对应的文件描述符被关闭,则read返回0。
4、如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程退出。
5、当要写入的数据量不大于PIPE_BUF时,Linux将保证写入的原子性。
6、当要写入的数据量大于PIPE_BUF时,Linux将不再保证写入的原子性。
?进程通信的步骤
?用管道通信,大致分为四个步骤
?
第一步,创建管道?
?
查看一下管道文件是否创建成功
第二步,创建子进程
?第三步,关闭不需要的fd
?我们一般把pipefd[0]作为读端,pipefd[1]作为写端——0对应嘴巴用来读,1对应笔用来写
?
?第四步,开始通信
?
#include <iostream>
#include <string>
#include <cerrno>
#include <cassert>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
// 让不同的进程看到同一份资源!!!!
// 任何一种任何一种进程间通信中,一定要 先 保证不同的进程之间看到同一份资源
int pipefd[2] = {0};
//1. 创建管道
int n = pipe(pipefd);
if(n < 0)
{
std::cout << "pipe error, " << errno << ": " << strerror(errno) << std::endl;
return 1;
}
std::cout << "pipefd[0]: " << pipefd[0] << std::endl; // 读端, 0->嘴巴->读书
std::cout << "pipefd[1]: " << pipefd[1] << std::endl; // 写端, 1->笔->写东西的
//2. 创建子进程
pid_t id = fork();
assert(id != -1); //正常应该用判断,我这里就断言:意料之外用if,意料之中用assert
if(id == 0)// 子进程
{
//3. 关闭不需要的fd,让父进程进行读取,让子进程进行写入
close(pipefd[0]);
//4. 开始通信 -- 结合某种场景
const std::string namestr = "hello ,我是子进程";
int cnt = 1;
char buffer[1024];
while(true)
{
snprintf(buffer, sizeof buffer, "%s, 计数器: %d, 我的PID: %d\n", namestr.c_str(), cnt++, getpid());
write(pipefd[1], buffer, strlen(buffer));
sleep(1);
}
close(pipefd[1]);
exit(0);
}
//父进程
//3. 关闭不需要的fd,让父进程进行读取,让子进程进行写入
close(pipefd[1]);
//4. 开始通信 -- 结合某种场景
char buffer[1024];
while(true)
{
int n = read(pipefd[0], buffer, sizeof(buffer) - 1);
if(n > 0)
{
buffer[n] = '\0';
std::cout << "我是父进程, child give me message: " << buffer << std::endl;
}
}
close(pipefd[0]);
return 0;
}
?
?这要一个简单的管道通信就写好了
这里有一个细节,在子进程写的时候每写一次sleep一秒
管道的特点
1.单向通信
2.管道的本质是文件,因为fd的生命周期随进程,管道的生命周期是随进程的
3.管道通信,通常用来进行具有“血缘”关系的进程,进行进程间通信。常用与父子通信——pipe打开管道,并不清楚管道的名字,匿名管道
4.在管道通信中,写入的次数,和读取的次数不是严格匹配的,读写次数的多少没有强相关--表现--字节流
5.具有一定的协同能力,让reader和writer能够按照一定的步骤进行通信--自带同步机制
?管道的四种场景
1.如果我们read读取完毕了所有的管道数据,如果对方不发,我们只能等待
2.如果我们write端将管道写满了,我们还能写吗?不能
3.如果我关闭了写端,读取完毕管道数据,再读,就会read返回0,表明读到了文件结尾
4.写端一直写,读端关闭,会发生什么呢?没有意义OS不会维护无意义,低效率,或者浪费资源的事情。OS会杀死一直在写入的进程!OS会通过信号来终止进程? ?13)SIGPIPE
————————————————————————12.15日,明天在写@_@?
命名管道?
?
?
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!