进程间的五种通信方式介绍(代码片段)

上清风 上清风     2022-11-09     153

关键词:

进程间通信(IPC)介绍

 

进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。

IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。

以Linux中的C语言编程为例。

一、管道

管道,通常指无名管道,是 UNIX 系统IPC最古老的形式。

1、特点:

  1. 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。

  2. 它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。

  3. 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

一、管道

管道,通常指无名管道,是 UNIX 系统IPC最古老的形式。

1、特点:

  1. 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。

  2. 它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。

  3. 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

2、原型:

1 #include <unistd.h>
2 int pipe(int fd[2]);    // 返回值:若成功返回0,失败返回-1

当一个管道建立时,它会创建两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。如下图:

要关闭管道只需将这两个文件描述符关闭即可。

3、例子

单个进程中的管道几乎没有任何用处。所以,通常调用 pipe 的进程接着调用 fork,这样就创建了父进程与子进程之间的 IPC 通道。如下图所示:

若要数据流从父进程流向子进程,则关闭父进程的读端(fd[0])与子进程的写端(fd[1]);反之,则可以使数据流从子进程流向父进程。

复制代码
 1 #include<stdio.h>
 2 #include<unistd.h>
 3 
 4 int main()
 5 
 6     int fd[2];  // 两个文件描述符
 7     pid_t pid;
 8     char buff[20];
 9 
10     if(pipe(fd) < 0)  // 创建管道
11         printf("Create Pipe Error!\\n");
12 
13     if((pid = fork()) < 0)  // 创建子进程
14         printf("Fork Error!\\n");
15     else if(pid > 0)  // 父进程
16     
17         close(fd[0]); // 关闭读端
18         write(fd[1], "hello world\\n", 12);
19     
20     else
21     
22         close(fd[1]); // 关闭写端
23         read(fd[0], buff, 20);
24         printf("%s", buff);
25     
26 
27     return 0;
28 
复制代码

二、FIFO

FIFO,也称为命名管道,它是一种文件类型。

1、特点

  1. FIFO可以在无关的进程之间交换数据,与无名管道不同。

  2. FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

2、原型

1 #include <sys/stat.h>
2 // 返回值:成功返回0,出错返回-1
3 int mkfifo(const char *pathname, mode_t mode);

其中的 mode 参数与open函数中的 mode 相同。一旦创建了一个 FIFO,就可以用一般的文件I/O函数操作它。

当 open 一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:

  • 若没有指定O_NONBLOCK(默认),只读 open 要阻塞到某个其他进程为写而打开此 FIFO。类似的,只写 open 要阻塞到某个其他进程为读而打开它。

  • 若指定了O_NONBLOCK,则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO,其errno置ENXIO。

3、例子

FIFO的通信方式类似于在进程中使用文件来传输数据,只不过FIFO类型文件同时具有管道的特性。在数据读出时,FIFO管道中同时清除数据,并且“先进先出”。下面的例子演示了使用 FIFO 进行 IPC 的过程:

write_fifo.c

复制代码
 1 #include<stdio.h>
 2 #include<stdlib.h>   // exit
 3 #include<fcntl.h>    // O_WRONLY
 4 #include<sys/stat.h>
 5 #include<time.h>     // time
 6 
 7 int main()
 8 
 9     int fd;
10     int n, i;
11     char buf[1024];
12     time_t tp;
13 
14     printf("I am %d process.\\n", getpid()); // 说明进程ID
15     
16     if((fd = open("fifo1", O_WRONLY)) < 0) // 以写打开一个FIFO 
17     
18         perror("Open FIFO Failed");
19         exit(1);
20     
21 
22     for(i=0; i<10; ++i)
23     
24         time(&tp);  // 取系统当前时间
25         n=sprintf(buf,"Process %d\'s time is %s",getpid(),ctime(&tp));
26         printf("Send message: %s", buf); // 打印
27         if(write(fd, buf, n+1) < 0)  // 写入到FIFO中
28         
29             perror("Write FIFO Failed");
30             close(fd);
31             exit(1);
32         
33         sleep(1);  // 休眠1秒
34     
35 
36     close(fd);  // 关闭FIFO文件
37     return 0;
38 
复制代码

read_fifo.c

复制代码
 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<errno.h>
 4 #include<fcntl.h>
 5 #include<sys/stat.h>
 6 
 7 int main()
 8 
 9     int fd;
10     int len;
11     char buf[1024];
12 
13     if(mkfifo("fifo1", 0666) < 0 && errno!=EEXIST) // 创建FIFO管道
14         perror("Create FIFO Failed");
15 
16     if((fd = open("fifo1", O_RDONLY)) < 0)  // 以读打开FIFO
17     
18         perror("Open FIFO Failed");
19         exit(1);
20     
21     
22     while((len = read(fd, buf, 1024)) > 0) // 读取FIFO管道
23         printf("Read message: %s", buf);
24 
25     close(fd);  // 关闭FIFO文件
26     return 0;
27 
复制代码

在两个终端里用 gcc 分别编译运行上面两个文件,可以看到输出结果如下:

复制代码
 1 [cheesezh@localhost]$ ./write_fifo 
 2 I am 5954 process.
 3 Send message: Process 5954\'s time is Mon Apr 20 12:37:28 2015
 4 Send message: Process 5954\'s time is Mon Apr 20 12:37:29 2015
 5 Send message: Process 5954\'s time is Mon Apr 20 12:37:30 2015
 6 Send message: Process 5954\'s time is Mon Apr 20 12:37:31 2015
 7 Send message: Process 5954\'s time is Mon Apr 20 12:37:32 2015
 8 Send message: Process 5954\'s time is Mon Apr 20 12:37:33 2015
 9 Send message: Process 5954\'s time is Mon Apr 20 12:37:34 2015
10 Send message: Process 5954\'s time is Mon Apr 20 12:37:35 2015
11 Send message: Process 5954\'s time is Mon Apr 20 12:37:36 2015
12 Send message: Process 5954\'s time is Mon Apr 20 12:37:37 2015
复制代码

 

复制代码
 1 [cheesezh@localhost]$ ./read_fifo 
 2 Read message: Process 5954\'s time is Mon Apr 20 12:37:28 2015
 3 Read message: Process 5954\'s time is Mon Apr 20 12:37:29 2015
 4 Read message: Process 5954\'s time is Mon Apr 20 12:37:30 2015
 5 Read message: Process 5954\'s time is Mon Apr 20 12:37:31 2015
 6 Read message: Process 5954\'s time is Mon Apr 20 12:37:32 2015
 7 Read message: Process 5954\'s time is Mon Apr 20 12:37:33 2015
 8 Read message: Process 5954\'s time is Mon Apr 20 12:37:34 2015
 9 Read message: Process 5954\'s time is Mon Apr 20 12:37:35 2015
10 Read message: Process 5954\'s time is Mon Apr 20 12:37:36 2015
11 Read message: Process 5954\'s time is Mon Apr 20 12:37:37 2015
复制代码

上述例子可以扩展成 客户进程—服务器进程 通信的实例,write_fifo的作用类似于客户端,可以打开多个客户端向一个服务器发送请求信息,read_fifo类似于服务器,它适时监控着FIFO的读端,当有数据时,读出并进行处理,但是有一个关键的问题是,每一个客户端必须预先知道服务器提供的FIFO接口,下图显示了这种安排:

三、消息队列

消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。

1、特点

  1. 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。

  2. 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。

  3. 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

2、原型

复制代码
1 #include <sys/msg.h>
2 // 创建或打开消息队列:成功返回队列ID,失败返回-1
3 int msgget(key_t key, int flag);
4 // 添加消息:成功返回0,失败返回-1
5 int msgsnd(int msqid, const void *ptr, size_t size, int flag);
6 // 读取消息:成功返回消息数据的长度,失败返回-1
7 int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
8 // 控制消息队列:成功返回0,失败返回-1
9 int msgctl(int msqid, int cmd, struct msqid_ds *buf);
复制代码

在以下两种情况下,msgget将创建一个新的消息队列:

  • 如果没有与键值key相对应的消息队列,并且flag中包含了IPC_CREAT标志位。
  • key参数为IPC_PRIVATE

函数msgrcv在读取消息队列时,type参数有下面几种情况:

  • type == 0,返回队列中的第一个消息;
  • type > 0,返回队列中消息类型为 type 的第一个消息;
  • type < 0,返回队列中消息类型值小于或等于 type 绝对值的消息,如果有多个,则取类型值最小的消息。

可以看出,type值非 0 时用于

关于线程池的五种实现方式,中软国际java机试(代码片段)

在这里,还有创建一个只有单个线程的可以定时执行线程池(Executors.newSingleThreadScheduledExecutor())这些都是上面的线程池扩展开来了,不详细介绍了。3介绍线程池的七大参数上面我们也说到了线程池有五种实现方... 查看详情

list去除重复数据的五种方式,舒服~(代码片段)

以下介绍五种-不同的方法去除Java中ArrayList中的重复数据1.使用LinkedHashSet删除arraylist中的重复数据LinkedHashSet是在一个ArrayList删除重复数据的最佳方法。LinkedHashSet在内部完成两件事:删除重复数据保持添加到其中的数据的顺序... 查看详情

list去除重复数据的五种方式(代码片段)

List在Java开发中用得非常多,数据处理方面,咱们经常需要对重复数据去重。以下介绍五种-不同的方法去除Java中ArrayList中的重复数据1、使用LinkedHashSet删除arraylist中的重复数据LinkedHashSet是在一个ArrayList删除重复数据的最... 查看详情

读取属性配置文件的五种方式(代码片段)

读取属性配置文件的五种方式读取属性配置文件的五种方式读取属性配置的示例属性配置文件方式一:使用注解@Value读取属性配置方式二:使用注解@ConfigurationProperties读取属性配置方式三:使用注解@PropertySou... 查看详情

进程间的通信方式(六种)(代码片段)

进程之间的通信参考文章:https://blog.csdn.net/qq_34827674/article/details/107678226前提知识:每个进程都有自己的用户空间,而内核空间是每个进程共享的。因此进程之间想要进行通信,就需要通过内核来实现。管道:... 查看详情

微前端:qiankun的五种通信方式(代码片段)

背景今天盘点一下qiankun父子应用的通信方式都有哪些,我发现了5种。1、localStorage/sessionStorage2、通过路由参数共享3、官方提供的props4、官方提供的actions5、使用vuex或redux管理状态,通过shared分享接下来我们一个一个进行... 查看详情

list的五种去重方式(代码片段)

//set集合去重,不改变原有的顺序publicstaticvoidpastLeep1(List<String>list)System.out.println("list=["+list.toString()+"]");List<String>listNew=newArrayList<>();Setset=newHashSet();for(Stringstr: 查看详情

进程间的通信(代码片段)

进程间的通信方式有管道,消息队列,共享内存,信号(不太会),套接字(用于web)管道方式:一个放入一个取出defps(c,msg):print("childstart")c.send("hello"+str(msg))print(os.getpid())if__name__=="__main__":c,pa=Pipe()li=[]foriinrange(5):p=Process(target=ps 查看详情

圣杯布局的五种方式(代码片段)

方法一center还在文本流之中,会影响到后面的元素,不影响前面的元素<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content= 查看详情

nginxupstream的五种分配方式(代码片段)

Nginx负载均衡选项upstream用法举例1、轮询(weight=1)默认选项,当weight不指定时,各服务器weight相同,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。upstreambakendserver192.168.1.10;server192.168.1.11;&... 查看详情

物体运动的五种方式(很重要)(代码片段)

1**************************************2物体运动的几种方式:31.4this.transform.position+=Vector3.left*Time.deltaTime;5/*Vector3.left是Vector3的一个属性,表示的是3为坐标系中的向左的单位向量,6实质和newVector3(-1,0,0)是一个效果。还有right,up,down,f 查看详情

linuxc与c++一线开发实践之四linux进程间的通信(代码片段)

Linux中的进程为了能在同一项任务上协调工作,它们彼此之间必须能够进行通信。下面主要介绍Linux常用的3种通信方式:信号、管道和消息队列。效果差别不大,熟练一种基本可以应对一般的一线开发场景了。(这里讲... 查看详情

linux进程间通信详解(最全)(代码片段)

进程间的五种通信方式介绍进程间通信(IPC,InterProcessCommunication)是指在不同进程之间传播或交换信息。IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享内存、Socket(套接字&#... 查看详情

linux系统编程-进程间通信(管道)(代码片段)

1.进程间通信方式介绍这篇文章介绍Linux下进程的间的通信方式,常用的方式如下:1.socket—网络通信2.管道---无名管道—命名管道---文件--FIFO3.消息队列4.共享内存5.信号量集6.信号—signal捕获信号---kill命令发送信号intkill(pid_tpid... 查看详情

unix下可用的五种i/o模型(代码片段)

...接字,当客户端fgets(在标准输入上)被阻塞并且服务器进程被终止时,我们遇到了问题。服务器TCP正确地将FIN发送到客户端TCP,但由于客户端进程被禁止从标准输入读取,所以它从没有看到EOF,直到它从套接字读取(可能更晚... 查看详情

jdbc连接mysql的五种方式(代码片段)

测试环境说明mysql数据库:jdbc:mysql://localhost:3306/testIDE:IDEA2022JDK:JDK8mysql:mysql5.7JDBC:5.1.37第一种方式使用静态加载驱动方式,连接mysql这种方式灵活性差,依赖性强publicvoidconnection01( 查看详情

windows进程间的通信(代码片段)

一、进程与进程通信   进程间通信(Interprocess Communication, IPC)是指不同的进程之间进行数据共享和数据交换。 二、进程间通信方式    1. 文件映射    注:文件映射是在多个进程间共享数据的... 查看详情

linux入门进程间的通信(代码片段)

进程间通信进程间通信介绍进程间通信目的进程间通信发展进程间通信分类管道什么是管道匿名管道用fork来共享管道的原理站在内核角度-深度理解管道站在内核角度-管道的本质管道读写规则管道特点命名管道创建一个命名管道... 查看详情