进程间通信(一)

IPC

在之前我们也有涉及到进程间通信的知识点,比如fork或exec或父进程读取子进程的退出码等,但是这种通信方式很有限,今天来学习进程间通信的其他技术——IPC(InterProcess Communication)。
IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中Socket和Streams支持不同主机上的两个进程IPC。

管道通信原理

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

特点

  1. 半双工(类似于对讲机,即数据只能在一个方向上流动),具有固定的读端和写端,数据单向传递。管道中的数据读走就没有了
  2. 它只能用于具有亲缘关系的进程之间的通信(父子进程或者兄弟进程之间)。
  3. 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

原型

#include <unistd.h>
int pipe(int fd[2]); //返回值:若成功返回0,失败返回-1
当一个管道建立时,它会创建两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。当我们需要关闭管道时,只需要将两个文件描述符关闭close即可。

利用管道实现进程间通信

实现父进程写入,子进程读取:

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>int main(){int fd[2];pid_t pid;char buf[128] = {'\0'};//int pipe(int pipefd[2]);if(pipe(fd) == -1){printf("create pipe fail!\n");}pid = fork();if(pid < 0){printf("create child fail!\n");}else if(pid > 0){      //父进程printf("this is father\n");close(fd[0]);write(fd[1],"hello world",strlen("hello world"));wait(NULL);}else{  //子进程printf("this is child\n");close(fd[1]);read(fd[0],buf,128);printf("read from father:%s\n",buf);exit(0);}return 0;
}

父进程在写入之前要将负责读的fd[0]关闭,子进程在读之前要将负责写的fd[1]关闭。无名管道使用比较简单,只需要分清楚fd[0]是负责读,fd[1]是负责写即可。相对来说无名管道的功能也比较单一,因此我们引入有名管道。

命名管道FIFO

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

特点

  1. FIFO可以在无关的进程之间交换数据,与无名管道不同
  2. FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

原型

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

其中的mode参数于open参数中的mode相同。一旦创建了一个FIFO,就可以用一般的文件IO函数操作它。
当open一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:

  1. 若没有指定O_NONBLOCK(默认),只读open要阻塞到某个其他进程为写而打开此FIFO。类似的,只写open要阻塞到某个其他进程为读而打开它。
  2. 若指定了O_NONBLOCK,则只读open立即返回。而只写open将出错返回-1.如果没有进程已经为读而打开该FIFO,其errno置ENXIO。
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>//int mkfifo(const char *pathname, mode_t mode);int main(){if((mkfifo("./file",0600) == -1) && errno == EEXIST){	//创建失败,且原因是由于文件已经存在printf("create fifo fail!\n");perror("why");}return 0;
}

运行结果:
在这里插入图片描述
我们可以利用open打开我们创建的FIFO:

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>//int mkfifo(const char *pathname, mode_t mode);int main(){if((mkfifo("./file",0600) == -1) && errno != EEXIST){printf("create fifo fail!\n");perror("why");}int fd = open("./file",O_RDONLY);printf("open success!\n");return 0;
}

运行结果:
在这里插入图片描述
无法运行到printf语句,原因是当我们使用open打开FIFO时,默认使用非阻塞标志,当我们以只读的方式打开该FIFO时,它会一直阻塞,直到其他进程以写的方式打开该FIFO,因此我们需要编写一个以写的方式打开该FIFO的程序:

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>//int mkfifo(const char *pathname, mode_t mode);int main(){int fd = open("./file",O_WRONLY);printf("open success!\n");

首先运行read
在这里插入图片描述
再打开一个终端,同时运行write
在这里插入图片描述

在这里插入图片描述
此时程序成功的跑了起来。
接下去,可以通过read和write函数进行数据的交互:
读进程:

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>//int mkfifo(const char *pathname, mode_t mode);int main(){int nread;char readBuf[30] = {'\0'};if((mkfifo("./file",0600) == -1) && errno != EEXIST){printf("create fifo fail!\n");perror("why");}int fd = open("./file",O_RDONLY);printf("open success!\n");nread = read(fd,readBuf,30);printf("read %d byte data from fifo,context:%s\n",nread,readBuf);close(fd);return 0;
}

写进程:

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>//int mkfifo(const char *pathname, mode_t mode);int main(){char *str = "message from fifo";int fd = open("./file",O_WRONLY);printf("open success!\n");write(fd,str,strlen(str));close(fd);return 0;
}

同时运行./read和./write运行结果:
./read
open success!
read 17 byte data from fifo,context:message from fifo

消息队列

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

特点

  1. 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级
  2. 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
  3. 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

原型

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

具体参数意义接键:消息队列函数(msgget、msgctl、msgsnd、msgrcv)及其范例

msgGet:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>//int msgget(key_t key, int msgflg);
//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
//int msgctl(int msqid, int cmd, struct msqid_ds *buf);struct msgbuf {long mtype;       /* message type, must be > 0 */char mtext[128];    /* message data */
};int main(){struct msgbuf readBuf;int msgId = msgget(0x1234,IPC_CREAT|0777);	///0777可读可写可执行if(msgId == -1){printf("get que fail!\n");}msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),888,0);//msgflag=0阻塞式接收消息,没有该消息就阻塞等printf("read data from que:%s\n",readBuf.mtext);return 0;
}

msgSnd:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>//int msgget(key_t key, int msgflg);
//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
//int msgctl(int msqid, int cmd, struct msqid_ds *buf);struct msgbuf {long mtype;       /* message type, must be > 0 */char mtext[128];    /* message data */
};int main(){struct msgbuf sendBuf = {888,"data frmo que"};int msgId = msgget(0x1234,IPC_CREAT|0777);if(msgId == -1){printf("get que fail!\n");}msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);return 0;
}

运行结果:
在这里插入图片描述
同时我们也可以让send和get同时收发数据:
msgGet.c:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>//int msgget(key_t key, int msgflg);
//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
//int msgctl(int msqid, int cmd, struct msqid_ds *buf);struct msgbuf {long mtype;       /* message type, must be > 0 */char mtext[128];    /* message data */
};int main(){struct msgbuf readBuf;struct msgbuf sendBuf = {988,"thank for your message"};int msgId = msgget(0x1234,IPC_CREAT|0777);if(msgId == -1){printf("get que fail!\n");}msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),888,0);//msgflag=0阻塞式接收消息,没有该消息就阻塞等printf("read data from que:%s\n",readBuf.mtext);msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);return 0; 
}  

msgSnd.c:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>//int msgget(key_t key, int msgflg);
//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
//int msgctl(int msqid, int cmd, struct msqid_ds *buf);struct msgbuf {long mtype;       /* message type, must be > 0 */char mtext[128];    /* message data */
};int main(){struct msgbuf sendBuf = {888,"data frmo que"};struct msgbuf readBuf;int msgId = msgget(0x1234,IPC_CREAT|0777);if(msgId == -1){printf("get que fail!\n");}msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),988,0);printf("receive data frmo get:%s\n",readBuf.mtext);return 0;
}

运行结果:
./get
read data from que:data frmo que
./send
receive data frmo get:thank for your message

键值key生成和消息队列移除

系统建立IPC通讯(消息队列、信号量和共享内存)时必须指定一个ID值。通常情况下,该id值通过ftok函数得到

#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);

因此我们可以利用ftok来修改之前代码,使得代码看起来更加规范,更加高大上:

key_t key;
key = ftok(".",12);
printf("key = %x\n",key);
//在send和get代码中同时加入这段初始化定义即可,两段代码的key值要保持一致才能进行数据交互

运行结果:
./get
key = c056491
read data from que:data frmo que

./send
key = c056491
receive data frmo get:thank for your message

我们一次交互信息会用到一个key也就是创建了一个新的消息队列,很多队列我们可能使用一次之后就不会再用,留在系统中,因此我们可以考虑将其移除,此时可以用到msgctl:

msgctl

*int msgctl(int msqid, int cmd, struct msqid_ds buf);
cmd:我们此时要用的时IPC_RMID(将消息队列中的链表清除)
只需在上述两段代码的最后加入msgctl(msgId,IPC_RMID,NULL);即可完成消息队列的清除。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1419827.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

JS代码随想录(一):数组

代码随想录 一、数组理论基础 二、LeetCode 704. 二分查找 三、LeetCode 27. 移除元素 四、LeetCode 977.有序数组的平方 五、LeetCode 209.长度最小的子数组 六、LeetCode 59.螺旋矩阵II 七、数组总结 一、数组理论基础 数组是存放在连续内存空间上的相同类型数据的集合。 数组…

vue3+TS或JS, 实现粒子特效 @tsparticles/vue3

在跟着B站视频BV11s4y1a71T学习时&#xff0c;使用到了粒子效果&#xff0c;但是以下这种情况只适用于项目是基于typescript的写法&#xff0c;否则无法实现。 粒子效果 VUE3TStsparticles/vue31、安装2、main.ts 引入3、App.vue4、效果 VUE3JS非最新版1、安装低版本的vue3-pa…

生信人写程序1. Perl语言模板及配置

生物信息领域常用语言 个人认为&#xff1a;是否能熟悉使用Shell(项目流程搭建)R(数据统计与可视化)Perl/Python/Java…(胶水语言&#xff0c;数据格式转换&#xff0c;软件间衔接)三门语言是一位合格生物信息工程师的标准。 生物信息常用语言非常广泛&#xff0c;我常用的有…

5/11后面部分:+顺序排序+元素交换+计算每门课程的各种成绩+存放规律的数据 注意:一味的复制肯定要出问题,第2个的最后一部分有修改,注意观察

目录 第一个已经输出过一次&#xff1a; 第二个: 编程实现&#xff1a;程序功能是用起泡法对数组中n个元素按从大到小的顺序进行排序。 ​编辑的确出现了一些问题哦&#xff1a; ​编辑目前是可以运行&#xff0c;但AI不给我们通过&#xff1a; 最后还是我的代码获胜&#x…

SAP-CentralFinance - 会计核算中的组织要素 - 学习心得1

1. 定义SAP组织架构和理解各组织架构含义 组织结构遍布SAP 系统的所有重要功能范围。FI 中最重要的组织要素是公司代码。它是“财务会计”中的最小组织单位,可以为其编制自主式完整科目集供外部报告使用。其他重要的组织要素是利润中心业务范围和段。您可以为各个利润中…

基于Vue3与ElementUI Plus的酷企秀场景可视化DIY设计器探索(更新版)

一、引言 在当今数字化快速发展的时代&#xff0c;企业对于展示自身形象、产品细节以及提升客户体验的需求日益增强。酷企秀场景可视化DIY设计器&#xff0c;以其强大的功能和灵活的定制性&#xff0c;为企业提供了从VR全景展示到地图可视化、电子画册制作等一系列数字化解决方…

vue3使用高德地图

一、获取高德地图key和秘钥 1、注册高德开放平台账号 #高德地图开放平台地址 https://lbs.amap.com/2、创建应用和key(选择web端) 二、安装vuemap/vue-amap库 库地址&#xff1a;https://vue-amap.guyixi.cn/zh-cn/introduction/install.html // 安装核心库 npm install vu…

[ue5]编译报错:使用未定义的 struct“FPointDamageEvent“

编译报错&#xff0c;错误很多&#xff0c;但很明显核心问题是第一个&#xff1a;使用未定义的 struct“FPointDamageEvent“&#xff1a; 程序没有找到FPointDamageEvent的定义。 解决办法&#xff1a; 处理这类未定义都可以先F12&#xff0c;找到它的库位置&#xff0c;之后…

py黑帽子学习笔记_网络编程工具

tcp客户端 socket.AF_INET表示使用标准IPV4地址和主机名 SOCK_STREAM表示这是一个TCP客户端 udp客户端 udp无需连接&#xff0c;因此不需要client.connect这种代码 socket.SOCK_DGRAM是udp的 tcp服务端 server.listen(5)表示设置最大连接数为5 发现kill server后端口仍占用…

【Spring Boot】玩转基础 (一篇就够了)

目录 资源 项目地址 PS 一、新建 SpringBoot 项目 1.我这里连接了码云仓库 2.新建项目 2.1不用码云的的创建方式 2.2使用码云的创建方式 3.使用 Spring InitiaIizr 创建项目 4.选择基本 Dependencies 依赖项 5.设置项目与文件编码格式 UTF-8 6.观察我们的项目架构 7.…

论文 学习 Transformer : Attention Is All You Need

目录 概述&#xff1a; 对摘要的理解&#xff1a; 框架解析 按比例缩放的点积注意力 多头注意力机制 前馈神经网络与位置编码 概述&#xff1a; transformer 是一个encoder ——decoder 结构的用于处理序列到序列转换任务的框架&#xff0c;是第一个完全依赖自注意力机制…

软件测试基础知识必备之浅谈单元测试

什么是单元测试&#xff1f; 单元测试是指&#xff0c;对软件中的最小可测试单元在与程序其他部分相隔离的情况下进行检查和验证的工作&#xff0c;这里的最小可测试单元通常是指函数或者类。 单元测试都是以自动化的方式执行&#xff0c;所以在大量回归测试的场景下更能带来…

算法设计与分析 例题解答 解空间与搜索

1.请画出用回溯法解n3的0-1背包问题的解空间树和当三个物品的重量为{20, 15, 10}&#xff0c;价值为{20, 30, 25}&#xff0c;背包容量为25时搜索空间树。 答&#xff1a; 解空间树&#xff1a; 搜索空间树&#xff1a; 2. 考虑用分支限界解0-1背包问题 给定n种物品和一背包…

数据的均匀化分割算法(网格划分法、四叉树法(含C++代码))

数据的均匀化分割主要是指在分割过程中尽可能均匀地将数据点分布在各个子区域中&#xff0c;以保持数据分布的平衡和优化数据结构的性能。以下是几种可以实现数据均匀化分割的方法&#xff1a; 一. 网格划分法 1. 基本概念 虽然传统的网格划分法不是动态调整的&#xff0c;但通…

共享旅游卡免费旅游真实反馈,有图有真相?

新伙伴体验&#xff0c;云南昆大丽6天5晚品质双人游&#xff0c;真实反馈&#xff01;珠海伙伴蔡总&#xff0c;加入千益畅行共享旅游卡团队&#xff0c;自己亲自体验“云南昆大丽6天5晚品质双人游”真实反馈&#xff0c;分享全程内容截图&#xff0c;无半点虚假&#xff01; …

C语言例题36、判断一个数是否是回文数

题目要求&#xff1a;输入一个5位数&#xff0c;判断它是不是回文数。即12321是回文数 #include <stdio.h>int main() {int x;int ge, shi, qian, wan;printf("请输入一个5位数&#xff1a;");scanf("%d", &x);ge x % 10; //个sh…

Android ashmem 原理分析

源码基于&#xff1a;Andoird U Kernel-5.10 0. 简介 ashmem 称为匿名共享内存(Anonymous Shared Memory)&#xff0c;它以驱动程序的形式实现在内核空间中。它有两个特点&#xff1a; 能否辅助内存管理系统来有效地管理不再使用的内存块(pin / unpin)&#xff1b; 通过Bind…

ROS2 工作空间

文章目录 ROS2 工作空间创建工作空间自动安装依赖编译工作空间设置环境变量参考链接 ROS2 工作空间 工作空间可以简单理解为工程目录。 ROS 系统中一个典型的工作空间结构如图所示&#xff1a; dev_ws&#xff1a; 根目录&#xff0c;里面会有四个子目录&#xff08;子空间&a…

贪心算法----摆动序列

今日题目&#xff1a;leetcode376 点击跳转题目 观察样例2&#xff1a; 发现最长摆动序列都是极大值和极小值 再加上两个端点&#xff0c;那么我们保证每次都能选择到每个极值点&#xff0c;就能从局部最优推广全局最优了&#xff01; 但是还有一些细节情况需要注意&#xff…

记录一次接口优化的过程。接口响应时间从500s下降到5s。

记录一次接口优化的过程。接口响应时间从500s下降到5s。 接口说明&#xff1a; 该接口通过用户导入的一年内每天的厂区用电功率数据来计算用户安装储能设备后的收益情况。 用电功率数据具体为每15分钟一条&#xff0c;一年约有 12*30*24*4 34560 条。 代码循环情况为&…