04.进程间通信

进程间通信基本概念

IPC(Inter Process Communication)

进程间通信

进程通信就是不同进程之间进行信息的交换或传播

为什么进程之间实现通信和困难

因为进程之间具有独立性,数据独立,程序可能独立也可能不独立(父子进程的程序时一样的)
所以要想进行进程间的数据交换,必须借助一个第三方的资源,进程一可以对这个资源进行读写,进程二也可以对这个资源进行读写,如图所示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个第三方的资源实际上就时OS提供的内存区域

进程通信的本质

进程通信的本质就时让不同进程访问同一份第三方资源,这个第三方资源可以由OS的不同模块提供,因此出现在不同的进程间的通信方式

进程通信的目的

1.数据传输:一个进程传输它的数据给另一个进程
2.资源共享:多个进程之间共享同样的资源
3.通知事件:一个进程需要向另一个或另一组进程发送消息,通知它们发生了某种事件,比如进程终止时需要通知其父进程(信号)
4.进程控制:有些进程希望完全控制另一个进程的执行(Bebug进程),此时控制的进程希望能够阻拦另一个进程所有陷入和异常,并且能够及时知道它的状态改变

管道

一个进程连接到另一个进程的数据流称为管道

管道符(|)

管道符就时利用这种管道的通信方式
进程1 | 进程2
ls / | wc -l

匿名管道

用于有血缘关系的进程间的通信

本质

进程通信的本质就是让不同的进程访问同一份第三方资源,而管道中第三方资源就是同一份打开的文件,父子进程都可以对这份文件进行读写,从而实现进程间的通信。
然后就是这个打开文件的位置,这个文件不可能放在磁盘中,因为磁盘的IO效率太慢了,所以它应该是在内存之中

pipe函数

创建管道的函数
int pipe(int pipefd[2])
pipefd[0] pipefd[1] 管道的读写端,传出参数
返回值:0 或 -1

因为管道是实现父子进程间的通信的,所以一般要配合fork函数使用

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

实现子进程向父进程中写入hello

#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
int main(int argc, char* argv[])
{int pipefd[2];pipe(pipefd);//创建匿名管道,pipefd[0]读端,pipefd[1]写端int pid = fork();if(pid == 0){//子进程写close(pipefd[0]);//关闭读端// close(pipefd[1]);int write_count = write(pipefd[1],"hello",6);printf("write_count = %d\n",write_count);}else{//父进程读close(pipefd[1]);//关闭写端char buf[1024]={'\0'};int read_count = read(pipefd[0],buf,sizeof(buf));//当写段开着但管道中没有数据的时候,不会直接返回-1,而是阻塞等待管道中数据的出现//如果写段没开,不会阻塞等待,直接然后0 printf("read_count = %d,buf = %s\n",read_count,buf);}while(1);return 0;
}

管道中状态的描述

初始时管道中没有数据:写端write返回成功读入的字节数,读端read阻塞等待管道中数据的产生
管道中有数据但没满:写端write返回成功读入的字节数,读端read返回成功读入的字节数
管道中数据满了:写端write阻塞等待,读端read返回成功读入的字节数
写端关闭:读端read返回成功读入的字节数,如果没有写入,总会有读完的时候,这个时候不会阻塞等待直接返回0
读端关闭:写端write会异常终止进程(被SIGPIPE信号杀死)

模拟ls / | wc -l

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
int main(int argc, char* argv[])
{int pipefd[2];pipe(pipefd);int pid = fork();if(pid > 0){//父进程//ls /内容由stdout转到管道的写端close(pipefd[0]);dup2(pipefd[1],1);execlp("ls","ls","/",NULL);}else{//子进程//wc -l读的由stdin到读端close(pipefd[1]);dup2(pipefd[0],0);execlp("wc","wc","-l",NULL);}while(1);return 0;
}

命名管道

创建管道文件有两种方式

mkfifo fifo文件
调用函数mkfifo(path,0644)

mkfifo函数

创建命名管道文件
int mkfifo(const char* pathname,mode_t mode)

wififo.c

#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>int main(int argc, char* argv[])
{// mkfifo("fifo",0644);int fd=open("./fifo",O_WRONLY);int write_count = write(fd,"hello",6);printf("write_count = %d\n",write_count);return 0;
}

riffo.c

#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>int main(int argc, char* argv[])
{int fd=open("./fifo",O_RDONLY);char buf[1024]={'\0'};int read_count = read(fd,buf,sizeof buf);printf("read_count = %d,buf = %s\n",read_count,buf);return 0;
}
如果写端没开,读端read会阻塞等待写端的打开(和匿名管道不同)
如果读端没开,写端write会阻塞等待读端的打开(和匿名管道不同)

命名管道的打开规则

在没有设置非阻塞属性的条件下,fifo读端和写端只要有一个没有打开,就会阻塞等待另一个的打开
在设置非阻塞属性的条件下,fifo的读端没有打开,立即返回失败,fifo写端没有打开,立即返回成功

user1和user2聊天实现

main.c
#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
int main(int argc, char* argv[])
{mkfifo("./u1r_u2w_fifo",0644);mkfifo("./u1w_u2r_fifo",0644);   return 0;
}
u1.c
#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>int main(int argc, char* argv[])
{//首先发送和结束数据不应该有先后顺序,所以应该一个进程接收,一个进程发送int pid = fork();if(pid == 0){//子进程,发送数据//打开u1的写管道int fd = open("./u1w_u2r_fifo",O_WRONLY);while(1){char buf[1024];//从终端把输入读到buf里面int r_count = read(0,buf,sizeof(buf));//buf中的数据写到写管道里面write(fd,buf,r_count);}}else{//父进程接收数据//打开u1的读管道int fd = open("./u1r_u2w_fifo",O_RDONLY);while(1){char buf[1024];//从管道中把数据读到buf中int r_count = read(fd,buf,sizeof buf);//把buf中的数据写道终端中write(1,buf,r_count);}}return 0;
}
u2.c
#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>int main(int argc, char* argv[])
{//首先发送和结束数据不应该有先后顺序,所以应该一个进程接收,一个进程发送int pid = fork();if(pid == 0){//子进程,发送数据//打开u2的写管道int fd = open("./u1r_u2w_fifo",O_WRONLY);while(1){char buf[1024];//从终端把输入读到buf里面int r_count = read(0,buf,sizeof buf);//buf中的数据写到写管道里面write(fd,buf,r_count);}}else{//父进程接收数据//打开u2的读管道int fd = open("./u1w_u2r_fifo",O_RDONLY);while(1){char buf[1024];//从管道中把数据读到buf中int r_count = read(fd,buf,sizeof buf);//把buf中的数据写道终端中write(1,buf,r_count);}}return 0;
}

死锁问题

前提:我们都知道命名管道假设没有设置非阻塞属性的话,读写俩端必须都打开才能进行数据的传输,只要有一个没有打开,另外一个就会阻塞等待。比如写端没有打开读端就会阻塞等待写端的打开
如果想实现进程间互相通信,必须有俩个命名管道文件,假设叫fifo1和fifo2,现在假如在进程1中fifo1的写端打开了,进程2中fifo2的读端打开了,这种情况就会造成死锁问题,因为进程1在阻塞等待fifo1读端的打开,fifo2在阻塞等待fifo2写端的打开,双方互相等待,造成死锁问题

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

解决方法:让fifo1的读端打开,fifo1的写端打开,fifo2的读端打开,fifo2的写端打开,放在四个进程中,单独执行,及时阻塞,但并不影响执行

管道通信总结

进程间的通信就如它表面上一样,即不同进程之间进行信息的交流,而本质上是因为进程与进程之间是独立的,也就是说修改进程1中的数据对进程2不会产生影响,所以为了实现进程之间的交流必须引入一个第三方资源,不同进程之间都可以对第三方资源进程读写操作,这样就完成了进程之间的通信。因为第三方资源可以由操作系统的不同模块提供,也就产生了各种各样的通信方式,管道就是其中之一,管理利用的第三方资源是内存中的文件(也就虚拟内存)。管道又分为匿名管道和命名管道。匿名管道只能用于有血缘关系进程间的通信,因为匿名管道相当于一个没有文件名的虚拟文件,只有在父子进程中,它打开该虚拟文件的fd才是一样的,这样就可以对同一个打开的虚拟文件进行读写操作,从而实现进程间的通信。而命名管道则可以实现任意两个进程间的通信,因为它是有名字的,不同进程中通过open它的路径就是找到该虚拟文件。
以上就是我对管道通信的一些理解。

内存映射

共享内存

俩个进程共享同一块虚拟内存

什么是内存映射?

内存映射指的是将磁盘文件中的数据映射到内存之中,这样修改内存就相当于修改了磁盘
image-20240421152206887

mmap函数

void char* mmap(void *addr, size_t length,int prot, int flags, int fd, off_t offset)

参数描述:
addr:磁盘文件映射到内存中的地址,如果不特殊指定,为NULL即可
length:要映射多少字节,一般为1024
prot:映射区域的保护方式是什么
PROT_READ//可读
PROT_WRITE//可写
flags:映射这段区域的属性是什么
MMAP_SHARED//创建共享映射,内存的写入即磁盘文件的写入
MMAP_PRIVATE//创建私有映射
MMAP_ANONYMOUS//创建匿名映射
fd:映射磁盘文件的文件描述符
offset:文件偏移量

munmap函数

解除映射区域
int munmap(void *addr, size_t length);

匿名映射

匿名映射相当于没有磁盘文件映射到内存之中,这段映射内存会被初始化为0
#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/mman.h>
int main(int argc, char* argv[])
{char *ptr = (char*)mmap(NULL,1024,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANONYMOUS,-1,0);int pid = fork();if(pid == 0 ){//子进程strcpy(ptr,"hello world");printf("child ps ptr = %s\n",ptr);}else{//父进程sleep(1);printf("father ps ptr = %s\n",ptr);}return 0;
}

文件映射

磁盘中文件的数据映射到内存中,对内存的修改就相当于对磁盘文件的修改

wmmap.c

#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<stdlib.h>
int main(int argc, char* argv[])
{int fd = open("./mmap.txt",O_RDWR | O_CREAT,0644);ftruncate(fd,1024);char *ptr=(char*)mmap(NULL,1024,PROT_WRITE | PROT_READ,MAP_SHARED,fd,0);if(ptr == MAP_FAILED){perror("mmap error:");exit(1);}int i = 0;while(1){sprintf(ptr,"-------------%d-----------\n",i++);sleep(1);}return 0;
}

rmmap.c

#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<stdlib.h>
int main(int argc, char* argv[])
{int fd=open("./mmap.txt",O_RDWR);char *ptr=(char*)mmap(NULL,1024,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);if(ptr == MAP_FAILED){perror("mmap error:");exit(1);}while(1){printf("ptr = %s\n",ptr);sleep(1);}return 0;
}

网络问题

sudo nmcli network off

sudo nmcli network on

消息队列

message queue

消息队列的优点

如果要实现一条一条消息发送的这种通信方式,管道和内存映射没有办法满足,因为它俩本质就是文件,没办法区分这是第一条消息,那是第二条,除非你加上某种特定的方式

基本概念

消息队列是内核中维护的消息链表,是面向消息进行通信的,每次读取一条完整的消息。每条消息具有的属性为:
1.消息本身内容
2.消息数据部分长度
3.表示消息优先级的整数

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

头文件

#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>

mq_open函数

打开消息队列
mqd_t mq_open(const char* name,int flags)
mqd_t mq_open(const char* name,int flags,mode_t mode,struct mq_ *attr)
1.返回值是消息队列的fd,mqd
2.name消息队列的名字
3.flags,mq的访问模式,O_RDONLY,O_WRONLY,O_RDWR,O_NONBLOCK,O_CREAT,O_EXCL
4.mode,mq的访问权限,0644
5.attr,mq的属性

mq_close函数

关闭消息队列

int mq_close(mqd_t mqd);

mq_getattr函数

获取消息队列属性

int mq_getattr(mode_t mqd,struct mq_attr *attr);

mq_setattr函数

设置消息队列属性

int mq_setattr(mode_t mqd,struct mq_attr *newattr,struct mq_attr *oldattr);

mq_send函数

向消息队列中发送消息

int mq_send(mqd_t mqd,const char *buf,size_t msg_len,unsigned int msg_prio)

mq_receive函数

接收消息队列中传来的消息

int mq_receive(mqd_t mqd,const char *buf,size_t msg_len,unsigned int * msg_prio)

mq_unlink函数

删除消息队列队列名

int mq_unlink(const char* name);

消息队列的四个属性

struct mq_attr{long mq_flags;//是否阻塞long mq_maxmsg;//最大消息数long mq_msgsize;//最大消息的大小,1024*9long mq_curmsgs;//当前消息的个数
}
#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<mqueue.h>
#include<stdlib.h>int main(int argc, char* argv[])
{mqd_t mqd=mq_open("/msgqueue",O_RDWR | O_CREAT,0644,NULL);int w_count = mq_send(mqd,argv[1],strlen(argv[1]),atoi(argv[2]));if(w_count == -1){perror("w_count:");exit(1);}return 0;
}
#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<mqueue.h>
#include<stdlib.h>int main(int argc, char* argv[])
{mqd_t mqd=mq_open("/msgqueue",O_RDWR,0644,NULL);char buf[8192];int flags;int r_count = mq_receive(mqd,buf,sizeof buf,&flags);if(r_count == -1){perror("w_count:");exit(1);}printf("buf = %s\n",buf);return 0;
}
#include<stdio.h>
#include<string.h>                                                              
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<mqueue.h>
#include<stdlib.h>
#include<sys/stat.h>
int main(int argc, char* argv[])
{int mqd = mq_open("/msgqueue",O_RDONLY);if(mqd == -1){perror("open mq error:");exit(1);}struct mq_attr attr;int ret = mq_getattr(mqd,&attr);struct mq_attr attr_new;attr.mq_maxmsg=20;attr_new=attr;mq_setattr(mqd,&attr_new,&attr);if(ret == -1){perror("getattr error:");exit(1);}printf("mq_flags = %lu\n",attr_new.mq_flags);printf("mq_maxmsg = %lu\n",attr_new.mq_maxmsg);printf("mq_msgsize = %lu\n",attr_new.mq_msgsize);printf("mq_curmsgs = %lu\n",attr_new.mq_curmsgs);return 0;
}

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

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

相关文章

从面试官视角出发,聊聊产品经理的面试攻略

一、请进行自我介绍 这题基本是面试的开胃菜了&#xff0c;估计面试多的&#xff0c;自己答案都能倒背如流啦。 其实自我介绍还是蛮重要的&#xff0c;对我来说主要有 3 个作用&#xff1a;面试准备、能力预估、思维评估。 面试准备&#xff1a;面试官每天都要面 3 ~6 人&am…

Windows系统下通过nginx配置多项目

文章目录 前言大概思路实际操作记录&#xff1a;查看nginx 错误日志问下AI注意点&#xff1a; 当访问域名根路径时&#xff0c;重定向到/pc总结 前言 在windows电脑启动一个nginx 测试配置多前端项目&#xff0c;一个pc端&#xff08;vue3tsvite &#xff0c;history路由&…

【C++进阶】C++中的map和set

一、关联式容器 在初阶阶段&#xff0c;我们已经接触过STL 中的部分容器&#xff0c;比如&#xff1a; vector 、 list 、 deque&#xff0c; forward_list 等&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面存储的是元素本…

ChatGLM3-6B部署与微调及微调后使用

记录ChatGLM3-6B部署及官方Lora微调示例详细步骤及如何使用微调后的模型进行推理 一、下载代码 使用git clone 命令下载源码 git clone https://github.com/THUDM/ChatGLM3.git 如图所示 二、下载模型 模型权重文件从魔塔进行下载&#xff0c;不需要翻墙。权重文件比较大&…

【docker 】push 镜像到私服

查看镜像 docker images把这个hello-world 推送到私服 docker push hello-world:latest 报错了。不能推送。需要标记镜像 标记Docker镜像 docker tag hello-world:latest 192.168.2.1:5000/hello-world:latest 将Docker镜像推送到私服 docker push 192.168.2.1:5000/hello…

设计合理的IT运维服务目录:打造高效运维的蓝图

在数字化转型的浪潮中&#xff0c;一个设计合理、内容详尽的IT运维服务目录是连接服务提供者与消费者之间的桥梁&#xff0c;它不仅体现了服务设计的专业性&#xff0c;还直接影响着运维效率和服务质量。如何设计出既合理又高效的IT运维服务目录&#xff1f;让我们结合ITIL 4框…

【全开源】JAVA台球助教台球教练多端系统源码支持微信小程序+微信公众号+H5+APP

功能介绍 球厅端&#xff1a;球厅认证、教练人数、教练的位置记录、助教申请、我的项目、签到记录、我的钱包、数据统计 教练端&#xff1a;我的页面&#xff0c;数据统计、订单详情、保证金、实名认证、服务管理、紧急求助、签到功能 用户端&#xff1a;精准分类、我的助教…

Maven的使用

1.第一个Maven工程 1.1 创建约定目录结构 ​ Hello ​ src ​ ——main(存放主程序) ​ ————java(存放源代码文件) ​ ————resources(存放配置文件和资源文件) ​ ——test(存放测试程序) ​ ————java ​ ————resources ​ pom.xml 1.2 创建核心文件 pom.xml …

mysql等保测评2.0命令-三级

版本 Win默认安装位置 C:\Program Files\MySQL\MySQL Server 8.0\bin 版本&#xff1a;select version() from dual; 身份鉴别 a应对登录的用户进行身份标识和鉴别&#xff0c;身份标识具有唯一性&#xff0c;身份鉴别信息具有复杂度要求并定期更换&#xff1b; 1、SELEC…

新能源汽车中HEV与PHEV分别代表什么车型,它们与传统燃油车都有什么区别?

前言 新能源汽车正逐渐成为全球汽车工业的主流方向&#xff0c;而HEV&#xff08;Hybrid Electric Vehicle&#xff09;和PHEV&#xff08;Plug-in Hybrid Electric Vehicle&#xff09;这两种混合动力车型在这一转型过程中扮演着重要角色。下面我们详细探讨HEV与PHEV的定义&a…

RAG查询改写方法概述

在RAG系统中&#xff0c;用户的查询是丰富多样的&#xff0c;可能存在措辞不准确和缺乏语义信息的问题。这导致使用原始的查询可能无法有效检索到目标文档。 因此&#xff0c;将用户查询的语义空间与文档的语义空间对齐至关重要&#xff0c;目前主要有查询改写和嵌入转换两种方…

扫码查看文件是如何实现的?文件活码在线生成的方法

现在很多场景下会通过扫码的方式来查看文件&#xff0c;这种方式可以让更多的人同时通过扫码的方式来查看二维码&#xff0c;有利于文件的快速分享以及用户获取内容的个人体验&#xff0c;而且可以保护文件的安全性&#xff0c;那么如何制作文件二维码呢&#xff1f; 文件二维…

中国211大学全部排名一览表

211大学是指中国教育部实施的名为“211工程”的高等教育发展战略中被选为重点支持的高等院校。这个名称来源于项目的启动背景和目标&#xff1a;“211”中的“21”代表21世纪&#xff0c;意味着该项目面向21世纪的中国高等教育发展&#xff1b;“1”则意指要重点建设大约100所左…

商机无限:实景无人自动直播软件带动实体店和电商行业新一波繁荣!

直播带货风潮的兴起确实是近年来电商行业的一个显著趋势。短视频平台的崛起为直播电商开辟了新的商业蓝海&#xff0c;商家们也纷纷加入到直播带货的行列中。然而&#xff0c;对于许多商家来说&#xff0c;找到合适的主播并不容易&#xff0c;这给他们带来了一定的困扰。hhgg加…

API接口开发实现一键智能化自动抓取电商平台数据商品详情支持高并发免费接入示例

要开发一个API接口&#xff0c;用于自动抓取电商平台的商品详情数据&#xff0c;并支持高并发和免费接入&#xff0c;你需要考虑以下几个步骤&#xff1a; 确定目标电商平台和商品详情的数据结构。设计API接口规范&#xff0c;包括请求和响应格式。实现数据抓取逻辑&#xff0…

Ryght 在 Hugging Face 专家助力下赋能医疗保健和生命科学之旅

本文是 Ryght 团队的客座博文。 Ryght 是何方神圣&#xff1f; Ryght 的使命是构建一个专为医疗保健和生命科学领域量身定制的企业级生成式人工智能平台。最近&#xff0c;公司正式公开了 Ryght 预览版 平台。 Ryght 预览版https://www.ryght.ai/signup?utm_campaignPreview%2…

各种数据获取stream流的方式

1.单列集合&#xff08;直接调用&#xff09; ArrayList<Integer> list new ArrayList<>();list.stream(); 2.双列集合 HashMap<String, Integer> map new HashMap<>();map.put("aaa",111);map.put("bbb",222);map.put("c…

传感器—超声波雷达

声波技术 在讲述超声波雷达之前&#xff0c;先了解一下声波的概念以及超声波和声波之间的关系 什么是声波&#xff1f; 声波是物体机械振动状态&#xff08;或能量&#xff09;的传播形式。所谓振动是指物质的质点在其平衡位置附近进行的往返运动形式&#xff0c;这种振动状…

工厂模式应用实例

引言 设计模式概念 设计模式&#xff08;Design Pattern&#xff09;的官方概念可以表述为&#xff1a;在软件设计中&#xff0c;设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它是针对特定问题或特定场景的解决方案&#xff0c;是一种经过…

你写的每条SQL都是全表扫描吗

你写的每条SQL都是全表扫描吗&#xff1f;如果是&#xff0c;那MySQL可太感谢你了&#xff0c;每一次SQL执行都是在给MySQL上压力、上对抗。MySQL有苦难言&#xff1a;你不知道索引吗&#xff1f;你写的SQL索引都失效了不知道吗&#xff1f;慢查询不懂啊&#xff1f;建那么多索…