(一)进程间通信的理解;
对进程间通信的理解,我们分为三个问题;
(1)进程间通信的目的;
数据传输:一个进程需要将它的数据发送给另一个进程.资源共享:多个进程之间共享同样的资源.通知事件:一个进程向另一个或一组进程发送消息.通知它发生了某种事件进程控制;进程知道另外一个进程的运行状态.
(2)如何做到呢?
我们从进程的概念所知,如果仅仅依靠两个进程发生通信,是很难的。因为进程运行 具有独立性!
进程A---->"拷贝"资源给OS(提供一段内存区域)---->OS“拷贝”资源---->进程B
因此,通信的本质就是; 不同的进程看到,同一份资源 ;
(3)通信的分类;
①管道:匿名管道 命名管道
② system V
③POSIX进程间通信
(二)进程间如何进行通讯(匿名管道)
(1)什么是管道?
管道是Unix中最古老的进程间通信的形式。我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”且管道是允许 单向通讯
(2)匿名管道(无文件名);
因为把文件写到磁盘效率太低,所以通信管道会在内存中就进行通信。成为内存文件
#include <unistd.h>创建无名管道int pipe(int fd[2])
(3)如何使用匿名管道?
根据文件描述符使用规则,系统在打开文件的时候,会默认打开三个流0,1,2。
这个原理也就不多赘述。
①管道搭建;
②读和写入管道数据;![](https://i-blog.csdnimg.cn/blog_migrate/7158d4e01d59fbae3d1185aa6a542ea9.png)
(4)如何理解?
①写时拷贝与管道文件
②管道自带的同步与互斥;
因此,是因为子进程写得慢,导致父进程需要等待(被阻塞) 子进程进行写入。----->同步+互斥
此时让写端停止写入,读端也就读不到数据A(挂起)
当写端的管道写满 也就不会再写入(挂起)
所以当我们把读端关闭掉,那么写端是否还能继续写入呢?
答案是不可能!操作系统会自动把该进程杀掉!-------->写入无意义
③管道大小;
ulimit -a 查看系统资源
所以管道大小就是65536
④管道特性+情况总结;
特征:
1.如果写端关闭,读端也就会读到0 |
2/如果打开文件的进程退出了,文件也会被释放掉。(文件的生命周期随进程).
3.管道提供流式服务
4.自带的同步与互斥
5.半双工通信.
6.匿名管道适合血缘关系的进程进行通信.
情况:
read --->数据区没数据
write-->数据区填满
write时 read被关闭 此时会被操作系统干掉。
write写完 关闭,read 返回值0;
(三)进程间通信(命名管道)
刚刚的管道的槽点,就在于需要相关的进程才能进行通信。但实际中大多数都是 非关联的进程巨多~
(1)命令行形式
命令行;
mkfifo + filename;
借用命名管道 重定向 通信;
(2)函数mkfifo;
#include<sys/types.h>
#include<sys/stat.h>
int mkfifo(char* pathname,size_t mode)
看到同一份comm.h
完成通信;
值得注意的是,因为两个进程通信的数据是存在管道缓冲区中,并没有刷新到磁盘里。
所以命名管道并没有任何 变化。
②用命名管道发送 命令信号
我们还可以借用命名管道做其他事情>
自动去 启动这些命令的 进程。
③拷贝文件;
所以,进程间通信的意义在于,让多个进程协同完成同一件事情~
(四)命名管道与匿名管道;
我们linux中的 | 匿名管道还是命名管道?
说明三个进程 ppid都是同一个! 所以有血缘关系。因此 这是 匿名管道!!
(五)system V共享内存;
(1)原理
想尽一切办法让不同进程,看到同一份资源。
(2)建立过程;
1.申请共享内存(目前就这样认为)
2.挂接共享内存(映射到进程的 地址空间里)---->已经达到看到同一份资源
3.去关联(取消映射)4.释放空间(还给系统)
(3)共享内存创建!
①创建共享内存;
#include<sys/ipc.h>
#included<sys/shm.h>
int shmget(key_t key,size_t size,int shmflg);
(内存句柄)--->用户层的 共享内存接口~!
参数;
key:标定共享内存 唯一的标志!
size:共享内存容量
shmflg;选项
注;IPC_CREAT 存在返回该共享内部 || 反之创建一个新的共享内存
IPC_EXCL单独使用无意义~
IPC_CREAT | IPC_EXCL(组合) ---->调用成功 获得一个新的共享内存。
②获取一个key;
#include<sys/types.h>
#include<sys/ipc.h>
key_t ftok(const char* pathname,int proj_id)参数:
pathname:路径名
id:工程id
单纯把id+pathname通过算法 生成一个序号~
获取key值;
管道是文件,不容易查它的容量。但是共享内存可以查到
ipcs -m //查看
当进程退出,这个共享内存是否还存在?
这又说明什么呢?
管道;当进程退出时,管道也就不复存在。是随管道。
共享内存:当进程退出时,不会销毁。是随内核。(再次论证,是由OS掌控)
ipcrm -m + shmid //删除
所以 key 与 shmid 的关系 类似于文件的
fd FIEL*
内核层 用户层
③函数实现移除;
#include<sys/ipc.h>
#include<sys/shm.h>
int shmctl(int shmid,int cmd,struct shmid_ds* buf)
//这里可以 写一下 监控脚本
while :; do ipcs -m ; echo "##################" ; sleep 1 ;done
(4)共享内存关联;
①关联
#include<sys/types.h>
#include<sys/shm.h>void* shmat(int shmid,const void* shmaddr,int shmflg);
参数:
shmid;链接共享内存
shmaddr;挂接哪个区域(本质上由操作系统完成)
shmflg;默认设置(读和写)
如何理解返回值?(void*)
这是对应共享内存 映射到虚拟地址的起始地址!
~= malloc(同样也是返回申请内存的起始地址)
如何理解挂接?
②去关联
int shmdt(const void* shmaddr);
(5)共享内存大小(size)
我们把size改为4097;
其实本质上不是,你要申请开 大小的空间,就给你好多大小的空间。
也就是说size 会被取整成 PAGE_SIZE(4096 byte)=一页数据!
所以,我们虽然得到4097的空间显示,但事实上得到的是 2页匡空间!
(6)实现通信;
我们首先一个打印A~z字母的打印;
管道与共享内存的反思;
反观共享内存,就不会存在拷贝的概念。因为这段代码 由进程共享。
但有一个缺点,不提供任何保护机制(没有同步和互斥!!!)------->需要使用信号量提供保护
总结;
①进程间通信的三种方法; 管道+共享内存(本质是让不同进程看到同一份资源);
②匿名管道pipe "|",针对具有血缘关系的进程。
③命名管道 mkfifo+file_name mkfifo(file_name);
④共享内存完成的 是不同进程,通过进程地址空间+页表 映射了物理内存的一块共享区域。
看到同样一份资源
四部过程;申请共享内存(shmget) 挂接空间(shmat) 去关联(shmdt) 释放空间(shmctl);
本篇就到这里啦,感谢你的阅读!
祝你好运~