目录
一、管道
1、匿名管道
2、命名管道
二、共享内存
操作流程:
三、消息队列
四、信号量
1、同步(临界资源访问的合理性)
2、互斥(同一时间只有一个进程能够访问资源,保证临界资源的安全性)
五、相关操作指令
进程间通信就是两个进程间的数据交换
因为进程间具有独立性,每个进程访问的都是自己的虚拟地址,进程之间无法直接通信,因此需要操作系统提供中间媒介进行通信
操作系统为进程间通信提供的方式:管道、共享内存、消息队列、信号量
管道的本质:管道实际上就是内核中的一块缓冲区,多个进程通过访问同一个缓冲区,实现进程间的通信
管道用于实现进程间的数据传输
管道分类:匿名管道、命名管道
管道符:| 作用:连接两个命令,将前边命令的输出结果交给后边的命令作为输入进行处理
这个内核中的缓冲区没有标识符,无法被其他进程直接找到,因此,只能用于具有亲缘关系的进程间通信
操作:父进程创建一个匿名管道,操作系统返回一个管道的操作句柄(文件描述符),在这之后,父进程创建子进程,子进程复制了父进程,也拥有这个描述符,指向了内核中同一个管道
(1)进程通过系统调用接口在内核中创建管道
int pipe(int pipefd[2]);
pipefd[2]:拥有两个整形元素的数组,用于接收管道的操作句柄(其中,pipefd[0]表示用于从管道中读取数据,pipefd[1]表示用于向管道中写入数据)
返回值:成功返回0,失败返回-1
(2)通过这个接口操作系统返回管道的操作句柄
(3)管道的操作方式类似于文件操作(通过文件描述符以及IO接口进行访问)
(4)创建子进程,子进程复制了父进程,也拥有同一个管道的操作句柄
管道的特性:管道是半双工通信;管道自带同步与互斥;提供字节流服务;管道生命周期随进程
名词解释:
- 半双工通信:可以选择方向的单向通信
- 同步:让资源的访问操作按照某种规则有序进行,避免产生饥饿问题。(同步用于保证合理操作)
- 互斥:让临界资源同一时间只能被一个进程访问,管道的写操作在不超过PIPE_BUF大小(4096字节)时保证原子性(互斥用于保证安全操作)
- 临界资源:进程都能访问到的资源
- 原子性:操作要么一次性完成中间不会被打断,要么不做
- 若管道中没有数据,则read会阻塞;如果数据满了,write会阻塞
- 提供字节流服务:有序的、可靠的、不限制大小的、基于连接的
- 基于连接的:有读必有写,有写必有读
- 若所有读端被关闭,则继续写入会触发异常导致进程退出;若所有写端被关闭,则读完数据之后不会阻塞,返回0
- 管道生命周期随进程:不人为干预的情况下,所有打开管道的进程退出后,管道被释放
这个内核中的缓冲区具有标识符,所有进程只要通过标识符就可以通过相同的标识符打开同一个管道进行通信,因此,可以用于同一主机上的任意进程间通信
本质依然是内核中的缓冲区,只是标识符是可见于文件系统的管道文件 ,多个进程通过打开一个管道文件访问到同一个缓冲区实现通信,管道的本质并没有变,因此缓冲区大小不受磁盘空间影响
创建一个命名管道:mkfifo filename
int mkfifo(const char* filename, mode_t mode);
filename:文件名称,mode:文件权限
返回值:成功返回0,失败返回-1
打开特性:如果命名管道没有被写的方式打开,则只读会打开阻塞;如果命名管道没有被读的方式打开,则只写会打开阻塞
本质:开辟的一块物理内存,多个进程通过将同一块物理内存映射到自己的虚拟地址空间,通过自己的虚拟地址空间进行访问来实现数据共享
特性:共享内存是最快的进程间通信方式;生命周期随内核
共享内存操作相较于其他操作少了两步用户态与内核态的数据拷贝
注意:共享内存的操作并非安全,可能多个进程写入则会造成数据交叉或覆盖的危险,因此,共享内存的操作是需要保护的(通过同步与互斥)
1、创建共享内存(shmget函数)
int shmget(key_t key, size_t size, int shmflg);
key:共享内存的标识符——多个进程通过相同的标识符打开同一个共享内存
size:共享内存大小
shmflg:操作方式:IPC_CREAT | IPC_EXCL ; 操作权限:0664
返回值:成功返回共享内存在代码中的操作句柄,失败返回-1
2、将这块共享内存映射到进程的虚拟地址空间(shmat函数)
void *shmat(int shmid, const void* shmaddr, int shmflg);
shmid:shmget返回的操作句柄
shmaddr:虚拟地址空间中的映射首地址(通常为NULL)
shmflg:SHM_RDONLY表示只读,否则默认0表示可读可写
返回值:成功返回实际映射在虚拟地址空间的首地址,失败返回-1
3、具体的内存操作(strcpy/memcpy......)
4、解除进程与共享内存的映射关系(shmdt函数)
int shmdt(const void* shmaddr);
shmaddr:虚拟地址空间中的映射首地址
返回值:成功返回0,失败返回-1
5、删除共享内存(shmctl函数)
每个共享内存都有一个映射连接数(表示当前有多少进程与自己建立了映射连接),因此删除共享内存,共享内存只有在映射连接数为0时才会被删除,如果当前不为0,则从当前开始拒绝后续的映射连接请求直到映射连接数为0时删除
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid:操作句柄
cmd:操作(一般为IPC_RMID,表示删除共享内存段)
buf:主要用于设置或获取共享内存的信息,直接置NULL
返回值:成功返回0,失败返回-1
本质:在内核中创建了一个优先级队列,多个进程可以通过相同的标识符找到内核中同一个消息队列,通过添加或者获取节点来实现数据传输
特性:自带同步与互斥;生命周期随内核
本质:信号量本质就是一个计数器(对资源进行计数)
主要用于实现进程间的同步与互斥
信号量对数据资源进行计数,所有的进程在访问资源之前先访问信号量,如果还有资源则流程继续,获取一个资源,计数-1,等到计数<=0时,进程想要获取资源则需要等待(将进程状态设置为阻塞状态,进入休眠),等待资源计数>0(产生一个资源计数就会+1),则唤醒进程的等待(将进程设置为运行状态,开始调度)
信号量的操作:
- P操作:在临界资源获取之前,先进行P操作,判断能否获取(能获取:计数-1;不能:等待)
- V操作:在资源产生之后,进行V操作(计数+1,唤醒一个等待中的进程)
实现互斥的原理:总数认为资源只有一个,计数最大为1
一个进程在访问前访问信号量,计数-1为0(其他进程再来会被阻塞),等到这个进程访问完毕后,再进行V操作,计数+1
ipcs [-m|-s|-q]:查看共享内存、信号量,队列等共享信息
-m列出共享内存,-s列出共享信号量,-q列出共享队列
ipcrm [-m|-s|-q] :清除命令
-m 删除共享内存,-s删除共享信号量,-q删除共享队列