进程的通信方式

进程间常见的通信方式有以下几种:

  • 管道
  • 信号量
  • 消息队列
  • 信号
  • 共享内存
  • 套接字

管道分为以下两种:

  • 普通管道(PIPE):单向传输,只能在有亲缘关系的进程之间
  • 命名管道(FIFO):单向传输,可以在没有亲缘关系的进程之间

管道是一块受内核管理的缓冲区,一端连接进程的输入,进程调用 pipe_wrtie() 方法向管道中写入数据,另一端连接另一个进程的输出,进程调用 pipe_read() 方法从管道中读取数据。为了保证两个进程对管道的操作同步,内核使用了锁、等待队列和信号等同步机制:

  • 每次写操作前,首先检查内存中是否有足够的内存容纳所有要写入的数据、还要检查该内存有没有被读操作锁定。同时满足时,锁定内存,然后从写进程的地址空间复制数据到管道内存。不满足时阻塞进程到等待队列中,此时进程处于可中断的等待状态,等待读取进程基于信号唤醒,等写操作完毕后发送信号唤醒读进程,读操作和写操作类似

管道基于 fork() 机制建立,开始时元进程包含输入、输出指针指向管道,fork() 完毕后,两个进程都包含输入、输出指针指向管道。后续删除多余的两条指针,让管道的输入和输出端分别指向这两个进程,建立连接。这也是普通管道为什么只能在有亲缘关系的进程间的主要原因。其中它采用环形的数据结构,方便循环利用。当两个进程都终止时,管道释放内存。

普通管道的实现并没有使用专门的数据结构,而是借助了文件系统的 file 结构和 VFS 的索引节点 inode。通过将两个 file 结构指向同一个临时的 VFS 索引节点,这个 VFS 索引节点指向一个物理页面实现。此时两个文件的操作进程地址不同,一个指向文件写入地址,一个指向文件读取地址,此时用户的系统调用仍是文件操作,内核通过它抽象实现管道。

命名管道是一种特殊的文件类型,它在文件系统中有对应的路径。当某个进程以读的方式打开文件,另一个进程以写的方式打开文件,此时内核就会在这两个进程间建立管道,也就是说 FIFO 本质也是基于内核管理,借助文件系统只是用来命名。这样做的好处在于我们可以根据文件路径识别管道,在没有亲缘关系的进程间建立连接。


信号量是一种计数器,主要用来控制多个进程对共享资源的并发访问,可以把它理解为一种锁机制。通过它保证同时最多只能有一个进程访问临界区域。

信号量是一个特殊的变量,任何程序对它的操作都是原子的,且只能进行两种操作:等待 P(sv)、发送 V(sv):

sv:信号变量

  • P(sv):如果 sv 的值大于0,减一;如果值为0,挂起当前进程
  • V(sv):如果存在进程因等待 sv 而挂起,就让他恢复运行,如果没有进程因等待 sv 挂起,就给它加一

举个例子:进程 a、b 共享信号量 sv,进程 a 执行 P(sv) 操作后,它得到信号量,进入临界区,sv 值减一。当进程 b 执行 P(sv) 操作后,sv 值为 0,进程被挂起,等待进程 a 离开临界区执行 P(sv) 释放信号量,恢复执行

可以把 P 理解为抢锁操作、V 理解为释放锁操作,信号量理解为共享锁,sv 的值表示最大共享占有的进程数

linux 提供了一组接口方便操作信号量,这里我列出常用的几个:

// 返回信号标识符
int semget(key_t key, int num_sems, int sem_flags); 
// 改变信号量的值
int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);  
// 控制信号量的相关信息
int semctl(int semid, int sem_num, int cmd, ...);

消息队列是由消息组成的链表,存放在内核中通过消息队列标识符标记。消息队列克服了管道只能传送无格式字节流以及缓冲池大小受限、信号传递信息少的缺点。

用户进程可以向消息队列添加或读取消息,通过这种方式实现进程间的交互。与管道相比,消息队列的优势在于可以对每个消息指定类型,接收时不按照队列次序,而自定义条件接收处理。

消息队列常用函数如下:

  • ftok():用于生成标准 key,每个 key 对应一个消息队列
  • msgget():创建或打开消息队列
  • msgsnd():添加消息
  • msgrcv():读取消息
  • msgctl():控制消息队列

内核存在结构体 ipc_ids msg_ids,通过它可以获得每个消息队列的 kern_ipc_perm,每个 kern_ipc_perm 结构如下:

struct kern_ipc_perm { 
	key_t  key;
	uid_t  uid;
	gid_t  gid;
	uid_t  cuid;
	gid_t  cgid;
	mode_t  mode;
	unsigned long seq;
}

这里 key 就是上面 ftok() 函数生成的消息队列唯一 key,通过它可以获取到具体消息队列。


共享内存就是映射一段能被其它进程所访问的内存,速度最快,一般通过它配合信号量一起使用。

简单来说就是创建共享内存,将共享内存分别映射到不同进程的地址空间,各进程通过映射完成通信。这里可以通过信号量或者互斥锁保证同步,其中共享内存通过以下函数实现:

  • shmget():获取共享内存区域的 ID,如果不存在就创建。
  • shmat():把共享内存区域映射到进程的地址空间中
  • shmdt():解除进程对共享区域的映射
  • shmctl():实现对共享内存区域的控制操作

套接字就是 socket 通信,和其它交互方式相比,它可以跨机器。

根据是否建立连接,套接字可以分为以下两种:

  • 流式 Socket:面向连接,基于 TCP 服务实现,安全但效率低
  • 数据报式 Socket:面向无连接,基于 UDP 服务实现,效率高但安全性没保障

面向连接 Socket 一般通信流程如下:

  1. 服务端打开套接字,绑定端口,监听客户端连接
  2. 客户端打开套接字,请求端口,建立连接
  3. 客户端发送数据
  4. 服务端处理并返回数据
  5. 客户端收到服务端响应
  6. 客户端关闭连接
  7. 服务端捕获到异常关闭和客户端的连接

信号用于通知进程某个事件已发生,采用异步处理的方式。

根据可靠性,信号可以分为可靠信号和不可靠信号:

  • 不可靠信号:linux 从早期 unix 系统继承来的信号,信号值小于 SIGRTMIN,可能丢失
  • 可靠信号:后来添加的新信号,信号值位于 SIGRTMIN、SIGRTMAX 之间,支持排队,不会丢失

根据是否实时,信号又可以分为实时信号和非实时信号:

  • 实时信号:支持排队,都是可靠信号
  • 非实时信号:不支持排队,都是不可靠信号

进程处理信号有三种情况:

  • 忽略信号:不做任何处理
  • 捕获信号:定义处理函数,执行相应操作
  • 缺省处理:默认操作

信号发送的函数:kill()、raise()、sigqueue() 等

如果进程要处理某信号,那么首先需要安装该信号。按照信号主要用于确定信号值和执行动作的对应关系。linux 安装函数主要有两种:signal()、sigaction()

  • signal():不支持信号传递信息,主要是用于前32种非实时信号的安装
  • sigaction():支持信号传递信息,支持实时和非实时信号的安装
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux系统中,进程通信是指两个或多个进程之间交换信息或共享资源的过程。Linux提供了多种进程通信方式,包括管道、命名管道、信号、共享内存、消息队列、套接字等。下面简要介绍一下每种通信方式的特点: 1. 管道(Pipe):管道是一种半双工的进程通信方式,用于在两个进程之间传输数据。它可以是匿名管道或命名管道,匿名管道只能在具有亲缘关系的进程之间使用,而命名管道可以在任意两个进程之间使用。 2. 信号(Signal):信号是一种异步的进程通信方式,用于在进程之间传递信息。当一个进程向另一个进程发送信号时,接收进程会中断正在执行的程序,转而执行信号处理程序,处理完后再返回原来的程序。 3. 共享内存(Shared Memory):共享内存是一种高效的进程通信方式,用于在两个或多个进程之间共享内存区域。多个进程可以访问同一块内存区域,从而实现数据共享,但需要使用信号量等机制来控制进程之间对共享内存的访问。 4. 消息队列(Message Queue):消息队列是一种进程通信方式,用于在两个或多个进程之间传输消息。进程可以把消息发送到消息队列中,其他进程可以从队列中读取消息,消息队列提供了一种可靠的消息传递机制。 5. 套接字(Socket):套接字是一种通用的进程通信方式,可用于在本地进程之间或网络上的进程之间进行通信。Linux将套接字视为一种特殊类型的文件,进程可以通过套接字进行文件读写操作,实现进程之间的通信。 不同的进程通信方式各有特点,应该根据实际情况选择合适的通信方式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值