进程间通信(一)

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);即可完成消息队列的清除。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【比邻智选】MF871U模组

&#x1f680;搭载国产芯&#xff0c;严苛测试&#xff0c;稳定可靠 &#x1f6e0;️R16特性加持&#xff0c;5G LAN&#xff0c;纳秒级精度 &#x1f310;超低成本&#xff0c;丰富协议&#xff0c;连接无界限

linux安装配置Docker保姆级教程

Docker到底是什么? Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中&#xff0c;然后发布到任何流行的 Linux或Windows操作系统的机器上&#xff0c;也可以实现虚拟化。 容器是完全使用沙箱机制&#xff0c;相互之间…

[AutoSar]lauterbach_001_ORTI_CPUload_Trace

目录 关键词平台说明一、ORTI概述二、ORTI文件的生成三、ORTI文件的导入四、Trace 功能4.1 Trace 功能菜单介绍4.2 Trace功能的配置4.3 Trace MCDS 设置4.4 Task Switches断点的设置4.5 Trace 数据的录取4.6 CPU 负载和Task调度的查看 关键词 嵌入式、C语言、autosar、OS、BSW…

智慧公厕建设,打造智慧城市基础设施新亮点

公共厕所是城市基础设施的重要组成部分&#xff0c;而智慧公厕的建设则是现代城市管理的创新之举。为了实现公厕的精细化管理和提供更便捷的服务&#xff0c;推进智慧公厕建设必须要实现技术融合、业务融合、数据融合的目标&#xff0c;跨越层级、地域、系统、部门和业务的限制…

LabVIEW的MEMS电容式压力传感器测试系统

LabVIEW的MEMS电容式压力传感器测试系统 针对传统微惯性测量单元(MIMU)标定方法存在的过程繁琐、标定周期长及设备复杂等问题&#xff0c;提出了一种基于LabVIEW软件的MIMU误差参数快速标定方法。通过软件上位机控制小型三轴转台&#xff0c;配合卡尔曼滤波器技术&#xff0c;…

Linux进程间通信 pipe 实现线程池 命名管道 实现打印日志 共享内存代码验证 消息队列 信号量

文章目录 前言管道匿名管道 pipe测试管道接口 --> 代码验证管道的4种情况管道的5种特征 线程池案例代码实现&#xff1a;ProcessPool.ccTask.hpp检测脚本makefile 命名管道代码演示&#xff1a;makefilenamedPipe.hppserver.ccclient.cc 实现日志Log.hpp 共享内存共享内存原…

机器人系统仿真

0、何为仿真 通过计算机对实体机器人系统进行模拟的技术。 1、为何仿真 低成本&#xff1a; 机器人实体一般价格昂贵&#xff0c;为降低机器人学习、调试的成本&#xff1b;高效&#xff1a; 搭建的环境更为多样且灵活&#xff0c;可以提高测试效率以及测试覆盖率&#xff1b…

1.基于python的单细胞数据预处理-降维可视化

目录 降维的背景PCAt-sneUMAP检查质量控制中的指标 参考&#xff1a; [1] https://github.com/Starlitnightly/single_cell_tutorial [2] https://github.com/theislab/single-cell-best-practices 降维的背景 虽然特征选择已经减少了维数&#xff0c;但为了可视化&#xff0…

pikachu靶场-全套学习

文章目录 配置pikachu靶场浏览器访问过程burpsuite配置代理hackbar安装使用kali安装中国蚁剑暴力破解cookie简化场景解释各部分含义如何工作 基于表单的暴力破解验证码绕过(On server)验证码绕过(on client)token防爆破? XSS&#xff08;Cross-Site Scripting跨站脚本攻击 &am…

牛客NC142 最长重复子串【中等 字符串 Java/Go/C++】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/4fe306a84f084c249e4afad5edf889cc 思路 注意&#xff1a;题目给的时间复杂度是O(N^2)那么直接套用双重循环&#xff1a;外层循环i为假定起始重复子串的初始位置&#xff0c;内层循环的j为假定重复子串的结束位置…

【LeetCode算法】28. 找出字符串中第一个匹配项的下标

提示&#xff1a;此文章仅作为本人记录日常学习使用&#xff0c;若有存在错误或者不严谨得地方欢迎指正。 文章目录 一、题目二、思路三、解决方案四、JAVA截取字符串的常用方法4.1 通过subString()截取字符串* 一、题目 给你两个字符串 haystack 和 needle &#xff0c;请你在…

ubuntu安装oceanbase调通本地navicat链接

分为两部分 一安装oceanbase服务 准备工作 mkdir -p /data/1 /data/log1 chown -R admin.admin /data/1 /data/log1/偷偷说&#xff1a;其实这步我忘记执行&#xff0c;也没影响我安装 oceanbase程序是很占内存的在安装时我们要先下载好安装包&#xff1a; 然后放在能记住的…

react引入阿里矢量库图标

react引入阿里矢量库图标 登录阿里矢量库&#xff0c;将项目所需的图标放一起 react项目中新建文件夹MyIcon.js 3. 在页面中引入&#xff0c;其中type为图标名称

机器人系统ros2-开发实践08-了解如何使用 tf2 来访问坐标帧转换(Python)

tf2 库允许你在 ROS 节点中查询两个帧之间的转换。这个查询可以是阻塞的&#xff0c;也可以是非阻塞的&#xff0c;取决于你的需求。下面是一个基本的 Python 示例&#xff0c;展示如何在 ROS 节点中使用 tf2 查询帧转换。 本教程假设您已完成tf2 静态广播器教程 (Python)和tf…

探索循环购模式:消费返利与积分机制的创新融合

大家好&#xff0c;我是吴军&#xff0c;今天非常荣幸能与大家分享一种别具一格的商业模式——循环购模式。这种商业模式在近年来逐渐崭露头角&#xff0c;受到了广大消费者的热烈追捧。或许您之前听说过消费满额即送现金的活动&#xff0c;但循环购模式不仅仅局限于此&#xf…

单细胞分析:多模态 reference mapping (2)

引言 本文[1]介绍了如何在Seurat软件中将查询数据集与经过注释的参考数据集进行匹配。我们展示了如何将来自不同个体的人类骨髓细胞&#xff08;Human BMNC&#xff09;的人类细胞图谱&#xff08;Human Cell Atlas&#xff09;数据集&#xff0c;有序地映射到一个统一的参考框…

数据库数据恢复—Sql Server数据库文件丢失丢失怎么恢复数据?

数据库数据恢复环境&#xff1a; 5块硬盘组建一组RAID5阵列&#xff0c;划分LUN供windows系统服务器使用。windows系统服务器内运行了Sql Server数据库&#xff0c;存储空间在操作系统层面划分了三个逻辑分区。 数据库故障&#xff1a; 数据库文件丢失&#xff0c;主要涉及3个…

【微信开发】微信支付前期准备工作(申请及配置)

1、申请并配置公众号或微信小程序 1.1 账户申请 通过微信公众平台&#xff0c;根据指引申请微信小程序或公众号&#xff0c;申请时需要微信认证&#xff0c;申请流程不在赘述 1.2 信息配置 申请通过后&#xff0c;需进入小程序和公众号内进行信息配置 1.2.1 小程序信息配置…

如何批量将十六进制数据转成bin文件

最近在做新项目遇到一个问题&#xff0c;我们要通过上位机把一堆数据通过串口发送给下位机存储&#xff0c;而上位机需要Bin文件。 解决办法&#xff1a; 1)创建一个记事本文件&#xff0c;然后将其后缀修改成.bin 2)然后打开notepad,新建一个文件&#xff0c;随便写下数据 我…

怎么制作流程图?介绍制作方法

怎么制作流程图&#xff1f;在日常生活和工作中&#xff0c;流程图已经成为我们不可或缺的工具。无论是项目规划、流程优化&#xff0c;还是学习理解复杂系统&#xff0c;流程图都能帮助我们更直观地理解和表达信息。然而&#xff0c;很多人可能并不清楚&#xff0c;其实制作流…