
前言:
本文是由浅入深探索Linux进程间通信系列最后一篇文章,主要讲解POSIX IPC实现原理。
由浅入深系列文章地址:
由浅入深探讨Linux进程间通信(上篇)
由浅入深探讨Linux进程间通信(中篇)
POSIX IPC是基于文件实现的,POSIX IPC符合Linux一切皆文件的思想,使用起来也很方便。
1.POSIX消息队列
POSIX消息队列是一种进程间通信机制,它允许在同一主机上的进程之间传递消息。
POSIX消息队列是基于消息的,每个消息都有一个优先级和一个非负整数类型。
消息队列由内核维护,可以通过mq_open()函数打开,mq_send()函数发送消息,mq_receive()函数接收消息,mq_close()函数关闭消息队列,mq_unlink()函数删除消息队列。
1.1 认识mqueue文件系统?
POSIX消息队列基于mqueue文件系统实现。
通过cat /proc/filesystems命令查看系统是否注册mqueue文件系统。

通过mount | grep "mqueue"查看mqueue文件系统挂载路径为/dev/mqueue目录,通过mq_open创建的消息队列文件存储在该目录。

1.2 创建POSIX消息队列

用户程序通过mq_open函数创建或打开POSIX消息队列,POSIX消息队列其实就是一个mqueue inode节点,mqueue inode节点有一个msg_tree成员(红黑树),用户存储消息,mq_open函数还需要做一些辅助工作:
创建struct file对象,并将struct file对象成员f_inode指向mqueue inode节点。
申请一个未使用的fd(文件描述符),fd映射至struct file对象,并保存在进程(task_struct)已打开文件表。
通过这些辅助工作,用户程序就能通过文件描述访问mqueue inode节点。
1.3 发送和接收消息

用户程序打开POSIX消息队列后,就能往消息队列读取和写入消息,其实就是往红黑树插入和删除消息。
但是要实现进程间通信,必须要不同的进程都能往同一POSIX消息队列读取和写入消息,mq_open函数有一个参数name指的是文件名,只要两个进程以相同的文件名打开POSIX消息队列,就能访问同一消息队列,完成进程间通信。
2.POSIX信号量
POSIX信号量是一种进程间同步和互斥的机制,它是一个计数器,用于控制多个进程对共享资源的访问。它的值只能是非负整数,当一个进程试图将其值减少到小于0时,该进程将被阻塞,直到另一个进程增加该值。
2.1 认识tmpfs文件系统?
POSIX信号量是基于tmpfs文件系统实现。
通过mount | grep "tmpfs"查看系统存在多个tmpfs文件系统,posix信号量使用的是/dev/shm目录下的tmpfs文件系统。

通过sem_open函数创建的信号量文件保存在/dev/shm目录,tmpfs文件系统主要用来做内存映射。
2.2 创建POSIX信号量

用户程序通过sem_open函数创建或打开POSIX信号量,sem_open函数主要用于创建和打开tmpfs inode节点,tmpfs inode用于内存映射,sem_open函数其他辅助工作:
创建struct file对象,并将struct file对象成员f_inode指向tmpfs inode节点。
申请一个未使用的fd(文件描述符),fd映射至struct file对象,并保存在进程(task_struct)已打开文件表。
通过mmap函数实现内存映射,并返回信号量地址(sem_t *)。sem_open函数做了这么多工作,其实就只有一个目的,就是在内核空间生成一个信号量(sem_t)。
POSIX信号量为什么要在内核空间生成?
首先我们得明白一个概念,无名信号量和命名信号量。
无名信号量用于线程同步,由于无名信号量(sem_t)存储在用户空间,不同的进程不能访问同一无名信号量,所以无名信号量只能用于线程同步。
命名信号量即POSIX信号量用于进程间同步,由于命名信号量(sem_t)存储在内核空间,所以不同的进程能访问同一命名信号量,所以命名信号量能用于进程间通信。
2.3 POSIX信号量操作
POSIX信号量常用操作函数如:sem_wait,sem_post,sem_getvalue等。
无名信号量和命名信号量使用同一套操作函数。

3.POSIX共享内存
POSIX共享内存是Linux下的一种进程间通信方式,它允许多个进程共享同一块物理内存。与其他进程间通信方式相比,共享内存的速度更快,因为数据不需要在进程之间复制。
POSIX共享内存同样是基于tmpfs文件系统实现,这里不再赘述。
3.1 创建POSIX共享内存

用户程序通过shm_open函数创建或打开POSIX共享内存,shm_open函数主要用于创建和打开tmpfs inode节点,tmpfs inode用于内存映射,shm_open函数其他辅助工作:
创建struct file对象,并将struct file对象成员f_inode指向tmpfs inode节点。
申请一个未使用的fd(文件描述符),fd映射至struct file对象,并保存在进程(task_struct)已打开文件表。
注意:shm_open函数仅仅用于创建和打开tmpfs inode节点,不会进行内存映射,内存映射需要用户程序完成。
不同的进程通过同一文件名,打开同一tmpfs inode节点,并完成内存映射,这样不同的进程就能够通过同一共享内存完成通信。
3.2 内存映射

用户程序需要主动调用mmap函数完成内存映射,mmap调用成功后,返回一个指向tmpfs inode文件映射区的地址,用户程序通过该地址和共享内存进行数据同步。
3.3 读取和写入数据

完成内存映射后,用户程序就能像操作普通内存一样操作共享内存,但是普通内存和共享内存存在一个本质区别,就是普通内存指的是用户空间内存,共享内存指的内核空间内存。
普通内存由于处于用户空间,所以不能用于进程间通信。
共享内存处于内核空间,所以可以用于进程间通信。
总结:
通过三篇文章,我们把Linux进程通信基本的方法已经讲完,但是这并不代表Linux进程间通信方法只有这么几种,Linux系统功能很强大,但Linux也非常复杂,要学好Linux,我们得不断学习和实践,持之以恒。

物联网心球(嵌入式软件开发专业内容生产者)。
C/C++,Linux Bug修复秘籍,Linux基础,Linux环境编程,Linux网络编程,高性能服务器,音视频开发,网络开发,Gui开发。
