进程间通信(一)

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://xiahunao.cn/news/3032429.html

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

相关文章

Windows 10 Manager (Win10优化工具),中文破姐版 v3.9.3

01 软件介绍 Windows 10 Manager是一款为Win10操作系统设计的综合优化工具。包含逾40种不同的功能模块&#xff0c;旨在全方位地提升系统性能。其核心效用体现在对Win10的优化、调整、清理、加速和修复方面。能够显著提高系统的运行速度&#xff0c;并有效地排查及解决系统问题…

无管理员权限linux系统手动切换cuda版本

查看当前计算机cudatoolkit的版本 nvcc -V下载 如果想切换到指定版本&#xff0c;则去官网下载&#xff08;10.1版本为例&#xff09;&#xff1a; cuda下载 cudnn下载 将下载好的文件放到服务器的文件夹中 安装 安装cuda sh cuda_10.1.243_418.87.00_linux.run选择接受 …

Python中的数据可视化:阶梯图matplotlib.pyplot.step()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 Python中的数据可视化&#xff1a; 阶梯图 matplotlib.pyplot.step() [太阳]选择题 matplotlib.pyplot.step()的功能是&#xff1f; import matplotlib.pyplot as plt import numpy as…

【向信而行 笃行致远】2024·C3合作伙伴大会即将启幕

向信而行&#xff0c;笃行致远。2024C3合作伙伴大会将于5月17日在南京拉开帷幕。 数字机遇&#xff0c;智能变革。在数智化的澎湃浪潮中&#xff0c;唯有聚合众力&#xff0c;方能乘风破浪&#xff0c;驶向更远的未来。 2023年&#xff0c;我们直面企业数字转型的需求与挑战&…

网络安全专业岗位详解+自学学习路线图

很多网安专业同学一到毕业就开始迷茫&#xff0c;不知道自己能去做哪些行业&#xff1f;其实网络安全岗位还是蛮多的&#xff0c;下面我会介绍一些网络安全岗位&#xff0c;大家可以根据自身能力与喜好决定放哪个方向发展。 渗透测试/Web安全工程师 主要是模拟黑客攻击&#…

空间尺寸对3维结构占比的影响

在3*3*3的3维空间内取3个点有27*26*25/62925种取法&#xff0c;在4*4*4的3维空间内取3个点有64*63*62/641664种取法&#xff0c;在xy&#xff0c;xz&#xff0c;yz面可自由变换的条件下同样都只有29个不同的结构。 34结构数量占比结构数量占比190.00311640.001521080.03692576…

18.Blender 渲染工程、打光方法及HDR贴图导入

HDR环境 如何导入Blender的HDR环境图 找到材质球信息 在右上角&#xff0c;点击箭头&#xff0c;展开详细部分 点击材质球&#xff0c;会出现下面一列材质球&#xff0c;将鼠标拖到第二个材质球&#xff0c;会显示信息 courtyard.exr 右上角打开已渲染模式 左边这里选择世界…

Microsoft Edge浏览器,便携增强版 v118.0.5993.69

01 软件介绍 Microsoft Edge浏览器&#xff0c;便携增强版&#xff0c;旨在无需更新组件的情况下提供额外的功能强化。这一增强版专注于优化用户体验和系统兼容性&#xff0c;具体包含以下核心功能的提升&#xff1a; 数据保存&#xff1a;通过优化算法增强了其数据保存能力&…

数组元素翻倍C++

编写一个 C 程序&#xff0c;实现一个功能&#xff0c;即将数组中的每个元素值翻倍。程序应定义一个函数 doubleArray&#xff0c;该函数接收一个整数数组的指针和数组的大小&#xff0c;然后将数组中的每个元素都翻倍。 代码 #include <iostream>void doubleArray(int…

Spring MVC(四) 数据校验

在开发过程中有一环必不可少的部分就是数据校验&#xff0c;用户在页面中填写的数据通过表单提交时&#xff0c;前端的JS可以做一些是否合法性的验证&#xff0c;比如是否为空、两次密码是否一致、格式是否正确等等验证。当数据到了后台控制器&#xff0c;为了确保程序的健壮性…

Redis进阶学习

Redis进阶学习 一、Redis事务1.2 Redis监控1.3 Jedis连接1.4 SpringBoot整合1.5 自定义RedisTemple1.6 Redis.conf详解 二、 Redis持久化2.1 RDB2.2 AOF进程 三、Redis发布订阅3.1 Redis主从复制3.2 集群环境配置3.3、复制原理3.4、宕机后主动变为主机3.5、哨兵模式 四、Redis缓…

机器学习算法应用——时间序列分析(4-5)

时间序列分析&#xff08;4-5&#xff09; 时间序列分析&#xff08;Time-Series Analysis&#xff09;是一种对按时间顺序排列的数据序列进行统计分析和预测的方法。这种方法通常用于研究某个现象随时间的变化规律&#xff0c;并据此预测未来的发展趋势。以下是时间序列分析的…

Java入门基础学习笔记11——关键字和标识符

1、关键字 关键字是java中已经被赋予特定意义的&#xff0c;有特殊作用的一些单词&#xff0c;不可以把这些单词作为标识符来使用。 注意&#xff1a;关键字是java用了的&#xff0c;我们就不能用来作为&#xff1a;类名、变量名、否则会报错。 标识符&#xff1a; 标识符就是…

又一个 开箱即用的 SpringBoot 企业级开发平台

项目概述 基于 Spring 实现的通用权限管理平台(RBAC模式)。整合最新技术高效快速开发,前后端分离模式,开箱即用。 核心模块包括:用户、角色、职位、组织机构、菜单、字典、日志、多应用管理、文件管理、定时任务等功能。 代码量少、学习简单、功能强大、轻量级、易扩展,轻…

Dual Aggregation Transformer for Image Super-Resolution论文总结

题目&#xff1a;Dual Aggregation Transformer&#xff08;双聚合Transformer&#xff09; for Image Super-Resolution&#xff08;图像超分辨&#xff09; 论文&#xff08;ICCV&#xff09;&#xff1a;Chen_Dual_Aggregation_Transformer_for_Image_Super-Resolution_ICCV…

水表智能抄表系统是什么?

水表智能抄表系统是一种现代化水资源保护专用工具&#xff0c;它利用先进的物联网、云计算和大数据剖析&#xff0c;完成了智能抄表、实时监控系统、数据分析等作用&#xff0c;大大提高了水务管理的效率和精确性。 1.功能特点 1.1远程控制自动抄表 传统水表抄水表方法采用人…

【初始类和对象】(实例讲解!超级详细!)

【初始类和对象】 前言1. 面向对象的初步认知1.1什么是面向对象1.2 面向对象与面向过程 2. 类的定义和使用2.1 简单认识类2.2 类的定义格式 3. 知识的代码举例讲解3.1 创建类、实例化类3.2 构造方法初始化类、this 3. 总结 前言 由于类和对象是我们在学习过程中需要接受的概念…

使用 Valgrind 检测内存泄漏

Valgrind 是一个编程工具&#xff0c;用于内存调试、内存泄漏检测以及性能分析。Valgrind 工具集中的 Memcheck 是用于检测内存管理和线程错误的主要工具。 参考&#xff1a;https://blog.csdn.net/weixin_44046545/article/details/138417524 1、安装 Valgrind sudo apt-ge…

牛客网Java实战项目--仿牛客网社区的学习笔记

仿牛客网社区的学习笔记 1. 项目环境搭建1.1 开发社区首页 2.开发社区登录模块2.1 发送邮件2.2 开发注册功能2.3 会话管理2.4 生成验证码2.5 开发登录、退出功能2.6 显示登录信息 4 Redis实现点赞关注4.1 Spring整合Redis访问Redis的方法&#xff1a; 4.2 Redis实现点赞4.2.1 点…

【Web】CTFSHOW 月饼杯 题解(全)

目录 web1_此夜圆 web2_故人心 web3_莫负婵娟 web1_此夜圆 拿到源码&#xff0c;一眼字符串逃逸 本地测一测&#xff0c;成功弹出计算器 <?phpclass a {public $uname;public $password;public function __wakeup(){system(calc);} }function filter($string){retur…