1.进程间通信
进程通信可以实现:数据传输、资源共享、通知事件、进程控制
进程具有独立性,如何通信?
前提:先得让不同进程看到同一份资源
- 1.同一份资源:某种形式内存空间
- 2.提供资源的:只能是操作系统
本地通信------同一个主机,同一个OS,不同进程之间的通信------1.syetem V 2.管道 3.POSIX标准
网络通信------不同主机之间通信
1.1进程间通信的分类
管道(古老、经典的通信方式)
- 匿名
- 命名
system V
- 消息队列
- 共享内存
- 信号量
2.管道
管道式Unix中最古老的进程间通信的方式
我们把从一个进程连接到另一个进程的一个数据流称为一个管道
2.1匿名管道
IPC(Inter - Process Communication)本质:先让不同进程,看到同一份资源
那么要由OS来创建这个资源,可不可以实现一个纯内存级别的文件------>管道
匿名管道原理:先以读写方式打开,在由子进程继承管道,父子进程各关闭其中一个文件描述符
2.1.1一个管道只能进行单向通信
a.管道打开没有关闭? fd泄露 && 误操作
b.为什么要rw同时打开?先创建管道,再创建子进程(利用子进程继承的属性)
c.为什么叫做管道?只能单向通信?因为最开始只需求单向通信,不用设计额外数据结构,因为特性故命名为管道
b.匿名管道不需要路径,不需要名字
2.1.2系统调用接口

cpp
//pipefd为输出型参数,pipefd[0]为读,pipefd[1]为写
//成功:返回0;失败:返回-1,失败码设置
重定向完整写法
bash./mypipe 2>log.txt ./mypipe 1>log.txt 2>log.txt #这样可以分离出stdout 和 stderror
2.1.3管道保护共享资源
IPC本质:先让不同进程,看到同一份资源!------>共享资源------>数据不一致问题------>保护共享资源(临界资源)------>管道内部自己做了
所以,father在读 管道,管道没有数据的时候会阻塞
匿名管道有大小上限 Ubuntu------>64KB
现象:
1.管道正常 && 管道阻塞 ,read会阻塞[read是一个系统调用]
2.管道为满 && 管道正常,write会阻塞[write是一个系统调用]
(管道满时不会覆盖,覆盖存在信息不一致问题)
3.管道读写端关闭 && 读端读到0(返回值为0),标识读到文件结尾。
4.管道写端正常 && 读端关闭,OS会直接杀掉写入进程
(OS不会做浪费资源的事;OS会给目标进程发送信号:13)SIGPIPE;怎么证明:父进程处理僵尸进程)
2.1.4匿名管道特性
1.面向字节流
2.用来进行有血缘关系的进程,进行IPC,常用于父子
3.文件生命周期,随进程!管道也是!
4.单向数据通信(双向通信需开两个管道)
5.管道自带同步、互斥等保护机制!(对共享资源)
2.1.5管道通信的场景------进程池

1.什么是任务------任务码int
2.怎么派发任务------任务量差不多,负载均衡
- a.轮询
- b.随机
- c.根据历史任务数
3.怎么停止子进程,关闭对应信道写的管道,这样读端会自动关闭。
2.2命名管道
1.命名管道是真实存在的文件
文件属性为p
p:只使用内核文件缓冲区,不做刷新
为什么真实存在文件?为了命名
2.命名管道的原理的理解(匿名打通)
为什么叫命名管道?真正存在的文件------>路径+文件名(具有唯一性)------>先让不同进程,看到同一份资源------>让不同进程用同一个文件系统路径标志同一个资源
2.2.1系统调用
cpp//创建管道本质也是新建文件,mode是文件的权限 //1.文件会记录用户UID //2.进程的task_struct->UID
使用mkfifo可以创建一个管道,unlink可以删除一个管道,unlink也是一条bash命令
若读端打开文件时,写端还没打开,读端对用的open就会阻塞
若写段打开文件时,读端还没打开,写端在open也会阻塞
3.System V
3.1System V共享内存
1.开辟空间
2、3:将共享内存挂接到进程地址空间中
3.1.1系统调用
共享内存在任何时刻,可以在OS内部同时存在很多个。
那OS如何管理共享内存?共享内存要有唯一的标识符key

参数解释
shmflg:是共享内存打开的方式,有IPC_CREAT、IPC_EXCL
IPC_CREAT:单独使用IPC_CREAT,若shm不存在,则创建。若存在,获取它并返回。(保证调用进程能拿到共享内存,无论新老)
IPC_CREAT | IPC_EXCL:若shm不存在,就创建它。若存在,出错并返回。(只要成功,一定是新的共享内存,不使用老的共享内存)
key:
1.必须由用户输入
2.你怎么保证shm的唯一性?怎么保证不同进程看到的是同一个共享内存?
使用shmid标识。
3.这个key为什么要用户传入?内核自己生成不就行了?
内核提供就不能看到同一份资源了。(因为用户看不到无法给进程指定资源)
key_t key随便设置?冲突了怎么办,使用如下库函数,通过公共的路径+公共的项目ID(用户指定)生成一个key
那么Linux是如何在应用层面,保证不同进程看到同一份资源的?
路径+项目ID
3.1.2共享内存的管理
共享内存的生命周期:随内核!故:
1.用户必须让OS释放------a.指令释放 b.代码
2.OS重启
共享内存的管理指令及系统调用
bash
# 查看
ipcs -m
# 删除
ipcrm -m [shmid]
cpp
// 指令 输出型参数
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
//将共享内存挂接到地址空间中
// 用户指定挂接到什么位置(一般不用)
void *shmat(int shmid, const void *shmaddr, int shmflg);
//成功时返回对应地址,失败返回-1
//datach去关联 将共享内存从地址空间上去关联
int shmdt(const void *shmaddr);
共享内存也有权限,与文件一样
挂接失败:shmget时权限设置的问题,在shmflg处 | mode
3.1.3共享内存的特点
1.通信速度是最快的
因为共享内存没有缓冲区和拷贝的使用,所以速度快。
管道从管道中读写数据,需要在缓冲区进行拷贝。
共享内存因为挂接到进程地址空间上,可以直接由地址找到其中的数据。
2.让两个进程在各种自的用户空间共享内存块,但是没有加任何保护机制
3.共享内存,保护机制,需要用户自己完成保护------信号量、命名管道
·为什么需要保护机制进程具有独立性------>通信------>不同进程看到同一份资源------>数据不一致问题------>临界&&临界资源&&加锁&&同步
cpp
//共享内存也可以获取结构化的内存(结构体)
//直接指向共享内存,向共享内存中写入
struct Data *image = (struct Data *)addr;
while (true)
{
strcpy(image->status, "最新");
strcpy(image->lasttime, GetCurrentTime().c_str());
strcpy(image->image, "***********");
// 使用管道向读端发命令表示可读
fifo.Sginal();
sleep(1);
}
3.2System V消息队列
消息队列本质:一个进程向另一个进程发送有数据类型数据块的方法。
系统调用接口
cpp
//创建消息队列
int msgget(key_t key, int msgflg);
//控制接口
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
//发送消息
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//接收消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
bash指令
bash
ipcs -q
因为都是System V标准,共享内存、消息队列、信号量接口非常相似
3.3System V信号量
系统调用接口:
cpp
// 信号量个数
int semget(key_t key, int nsems, int semflg);
//
int semctl(int semid, int semnum, int cmd, ...);
bash指令
bash
ipcs -s