进程间通信(一)

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.hqwc.cn/news/689926.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SMART700西门子触摸屏维修6AV6 648-0CC11-3AX0

西门子工控机触摸屏维修系列型号&#xff1a;PС477,PC677,TD200,TD400,KTP178,TP170A,TP170B,TP177A,TP177B,TP270,TP277,TP27,MP370,MP277,OP27,OP177B等。 触摸屏故障有&#xff1a;上电黑屏, 花屏,暗屏,触摸失灵,按键损坏,电源板,高压板故障,液晶,主板坏等,内容错乱、进不了…

基础I/O:文件系统调用接口

文章目录 文件系统调用接口open系统调用接口和C语言封装文件描述符fd重定向 文件系统调用接口 open NAME//打开、创建 - 打开并可能创建文件或设备open, creat - open and possibly create a file or deviceSYNOPSIS#include <sys/types.h>#include <sys/stat.h>…

【负载均衡式在线OJ项目day6】源文件路由功能及文件版题库构建

一.前言 前文讲到了OJ模块的设计思路&#xff0c;毫无疑问这是一个网络服务&#xff0c;我们先使用httplib&#xff0c;将源文件的路由功能实现&#xff0c;先把框架写好&#xff0c;后续再更改回调方法。 随后计划编写Modify模块&#xff0c;提供增删查改题库的功能(主要是查…

zblog中用户中心-邀请码注册插件的导出功能补充

自己加了一个导出未使用的邀请码功能&#xff0c;可惜我不是入驻作者&#xff0c;没有权限发布&#xff0c;之前被一条大河拒了&#xff0c;他说我抄他代码&#xff0c;不给我过审还冷嘲热讽&#xff0c;我一气之下&#xff0c;就没继续申请了&#xff0c;话说我是专业搞java开…

MVC:一种设计模式而非软件架构

在软件开发领域&#xff0c;MVC&#xff08;Model-View-Controller&#xff09;经常被提及&#xff0c;但很多人对其定位存在误解。本文将澄清一个常见的误区&#xff1a;MVC是一种设计模式&#xff0c;而非软件架构。 一、MVC简介 MVC&#xff0c;即模型&#xff08;Model&a…

数组二叉树-华为OD

系列文章目录 文章目录 系列文章目录前言一、题目描述二、输入描述三、输出描述四、java代码五、测试用例 前言 本人最近再练习算法&#xff0c;所以会发布一些解题思路&#xff0c;希望大家多指教 一、题目描述 二叉树也可以用数组来存储&#xff0c;给定一个数组&#xff…

医院如何做好漏费管理?什么是控费系统?控费系统现在成熟吗?

在中国深厚的人情土壤之中&#xff0c;某些医院里的医技科室&#xff0c;宛如隐秘的灰色地带&#xff0c;悄然滋生着利用职务之便谋取私利的暗流。这些科室的医务人员&#xff0c;以低于医院明文规定的收费标准&#xff0c;私下里为熟识的患者提供检查服务&#xff0c;仿佛形成…

安装SQL Server详细教程_sql server安装教程

一&#xff0c;SQL Server数据库安装 1.首先&#xff0c;下载安装程序 &#xff08;1&#xff09;从网盘下载安装exe 点击此处直接下载 &#xff08;2&#xff09;从官网下载安装exe文件 在官网选择Developer进行下载 2.开始安装 双击安装程序&#xff0c;开始安装 这里直…

每日10亿数据的日志分析系统OOM

背景 一个每日10亿数据的日志清洗系统&#xff0c;主要工作就是从消息队列中消费各种各样的日志&#xff0c;然后对日志进行清洗&#xff0c;例如&#xff1a;用户敏感信息(姓名、手机号、身份证)进行脱敏处理,然后把清理完的数据交付给其他系统使用。 我们项目中&#xff0c;…

Unity基础

概述 基础知识 3D教学 数学计算公共类Mathf 练习: 三角函数 练习&#xff1a; Unity中的坐标系 Vector3向量 向量模长和单位向量 向量加减乘除 练习&#xff1a; 向量点乘 向量叉乘 向量插值运算 Quaternion四元数 为何要使用四元数 四元数是什么 四元数常用方法 四元数计算 练…

【Java】/*方法的使用-快速总结*/

目录 一、什么是方法 二、方法的定义 三、实参和形参的关系 四、方法重载 五、方法签名 一、什么是方法 Java中的方法可以理解为C语言中的函数&#xff0c;只是换了个名称而已。 二、方法的定义 1. 语法格式&#xff1a; public static 返回类型 方法名 (形参列表) { //方…

2024.5.8 关于 SpringCloud —— Ribbon 的基本认知

目录 Ribbon 负载均衡原理 工作流程 Ribbon 负载均衡规则 Ribbon 负载均衡自定义化 代码方式修改规则 配置文件方式修改规则 小总结 Ribbon 设定饥饿加载 Ribbon 负载均衡原理 工作流程 order-service 使用 RestTemplate 发送请求&#xff0c;随后该请求将会被 Ribbon 所…