IPC-进程间通信

一、进程间通信

(IPC:Inter Process Communication)
1、概述
操作系统中的各个进程,通常存在于独立的内存空间,并且有着严格的机制来防止进程间的非法访问。但是,这并不代表进程与进程间不允许互相通信,相反,进程间通信是操作系统中一个重要的概念,应用非常广泛。
广义上,进程间通信是指运行在不同进程之间(不论是否在同一台机器)的若干线程间的数据交换。如下图所示:
在这里插入图片描述
原则上,任何跨进程的数据交换都可以称为进程间通信。IPC 中参与通信的进程,既可以运行在同一台机器上,也允许他们存在于各自的设备环境中(注:若进程是跨机器运行的,则通常由网络连接在一起)。
在这里插入图片描述
2、几种稳定高效的 IPC 机制
(1)共享内存(Shared Memory)
由于两个进程可以直接访问同一块内存区域,减少了数据的复制操作,因而速度上的优势比较明显。
在这里插入图片描述
实现内存共享的步骤:
<1> 创建内存共享区
通过 API 从内存中,申请一块共享区域(例如:在Linux环境中,可通过 shmget() 函数来实现),生成的共享内存将与某个特定的 key(即:shmget() 函数中的第一个参数)进行绑定。
<2> 映射内存共享区
创建了共享内存区后,需要将其映射到进程1的空间中,才能进一步操作(例如:在Linux环境中,可通过 shmat() 函数来实现)。
<3> 访问内存共享区
内存共享区映射到进程1的空间后,进程2可以通过 shmget() 函数,并传入上面的key值,即可访问到。然后进程2执行 shmat() 函数,再将这块内存映射到自己的空间中。
<4> 进程间通信
共享内存在各个进程实现了内存映射后,便可以利用该区域进行信息的交换。由于内存共享本身并没有同步机制,所以参与通信的各个进程需要自己协商处理。
<5> 撤销内存映射区
完成了进程间通信后,各个进程都需要撤销之前的映射操作(例如:在Linux环境中,可通过 shmdt() 来实现)。
<6> 删除内存共享区
最后必须删除共享区域,以便回收内存(例如:在Linux环境中,可通过 shmctl() 函数来实现)。

内存共享机制中相关函数说明:
在这里插入图片描述
在这里插入图片描述
(2)管道(Pipe)
它适用于所有 POSIX 系统以及 Windows 系列产品,该种进程间通信方式的特点:
<1> 进程A与进程B分立管道的两边,进行数据的传输通信。
<2> 管道中的流是单向的,意味着一个进程中若既需要”读“,也需要”写“,那么就要建立两根管道。
<3> 一根管道同时具有 “读取” 端(read end)、“写入” 端(write end)。
<4> 管道有容量限制,即:当Pipe满时,”读“、”写“操作将会被阻塞。

在Linux环境中,使用Pipe的操作流程示例:
在这里插入图片描述
说明:
<1> memset() 函数
该函数是一个初始化内存的 ”万能函数“,通常为新申请的内存进行初始化工作。它是直接操作内存空间的。该函数的原型为:

# include <string.h>
void *memset(void *s, int c, unsigned long n);    // 其中,c参数一般使用0来进行初始化

该函数的功能是:将指针变量 s 所指向的前n个字节的内存单元,用一个整型 c 替换,s 是 void* 型的指针变量,所以它可以为任何类型的数据进行初始化(注意:memset() 函数一般使用 ”0“ 来初始化内存空间,而且通常是用来给数组或结构体进行初始化)。
因此,memset() 函数就是在一段内存块中填充某个给定的值。因为它只能填充一个值,所以它无法将变量初始化为程序中需要的数据,初始化后,后面的程序再向该段内存空间中存放需要的数据。

<2> fork() 函数
当一个进程调用 fork() 函数之后,就有两个二进制代码相同的进程,它们都运行到相同的地方,但每个进程都将开始自己的活动。
在这里插入图片描述
当控制转移到内核中的 fork() 函数代码后,内核开始做:
1.分配新的内存块和内核数据结构给子进程。
2.将父进程部分数据结构内容拷贝至子进程。
3.将子进程添加到系统进程列表。
4.fork() 函数返回开始调度器,进行调度。
子进程和父进程是并发运行的独立进程。内核能够以任意的方式交替执行他们在逻辑控制流中的指令。因为父进程和子进程是独立的进程,他们都有自己私有的地址空间,当父进程或者子进程单独改变时,不会影响到彼此。
fork() 函数的常规用法:
1.一个父进程希望复制自己,使得子进程同时执行不同的代码段,例如:父进程等待客户端请求时,可以生成一个子进程来等待请求处理。
2.一个进程要执行一个不同的程序时,可以生成子进程分别进行处理。

<3> pipe() 函数
Linux 提供了Pipe接口,用于打开一个管道,该函数的原型为:

int pipe(int pipefd[2], int flags);   // 第一个参数代表成功打开后管道的两端

(3)UDS(UNIX Domain Socket)
该机制是专门针对单机内的进程间通信提出来的,有时也被称为:IPC Socket。它与 Network Socket 的内部实现原理有很大区别,由于 UDS 是本机内安全可靠的操作,因此实现机制上不依赖 TCP/IP 等协议。
(注:Android 中使用最多的一种 IPC 机制是 Binder,其次是 UDS。Android 2.2版本之前使用 Binder 作为整个GUI架构中的 IPC基础,后来改用了 UDS。)

使用 UDS 进行进程间通信的典型流程如下:
<1> 服务器端监听 IPC 请求;
<2> 客户端发起 IPC 申请;
<3> 双方成功建立起 IPC 连接;
<4> 客户端向服务器端发送数据,证明 IPC 通信是有效的。
在这里插入图片描述
UDS 的基本流程与 Network Socket 基本一致,只是在参数上有所区分,示例如下:

// Server端源码
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>   // 引用的是socket头文件
#include <sys/un.h>
#include <sys/types.h>
#define UDS_PATH "uds_test"

int main(void){
   
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值