uc_12_进程间通信IPC_有名管道_无名管道

1  内存壁垒

       进程间天然存在内存壁垒,无法通过交换虚拟地址直接进行数据交换:

        每个进程的用户空间都是0~3G-1(32位系统),但它们所对应的物理内存却是各自独立的。系统为每个进程的用户空间维护一张专属于该进程的内存映射表,记录虚拟内存到物理内存的对应关系,因此在不同进程之间交换虚拟内存地址是毫无意义的。

        所有进程的内核空间都是3G~4G-1,它们所对应的物理内存只有一份,系统为所有进程的内核空间维护一张内存映射表init_mm.pgd,记录虚拟内存到物理内存的对应关系,因此不同进程通过系统调用所访问的内核代码和数据是同一份。

        用户空间的内存映射表会随着进程的切换而切换,内核空间的内存映射表不变:

        

        Unix/Linux系统(32位)中的每个进程都拥有4G字节大小的专属于自己的虚拟内存空间,出去内核空间的1G,每个进程都有一张独立的内存映射表(内存分页表)记录着虚拟内存页和物理内存页之间的映射关系。

        同一个虚拟内存地址,在不同的进程中,会被映射到完全不同的物理内存区域,因此在多个进程之间以交换虚拟内存地址的方式交换数据是不可能的。

        鉴于进程之间天然存在的内存壁垒,要想实现多个进程间的数据交换,就必须提供一种专门的机制,这就是所谓的进程间通信(InterProcessCommunication,IPC

2  进程间通信(IPC)的种类

2.1  命令行参数

        在通过exec ()函数创建新进程时,可以为其指定命令行参数——借助命令行参数,可将创建者进程的某些数据传入新进程

        execl ("./login", "login", "username", "password", NULL);

2.2  环境变量

        类似地,也可在调用exec ()函数时为新进程提供环境变量:

        sprintf (envp[0], "USERNAME=%s", username);

        sprintf (envp[1], "PASSWORD=%s", password);

        execl ("./login", "login", NULL, envp);

2.3  内存映射文件

        通信双方分别将自己的一段虚拟内存映射到同一个文件中:mmap()

2.4  管道

        管道是Unix系统中最古老的进程间通信方式,并且所有的Unix系统和包括Linux系统在内的各种类Unix系统也都提供这种进程间通信机制。管道有2种限制:

        1  管道都是半双工的,数据只能沿着一个方向流动,类似对讲机,而非手机。

        2  管道只能在具有公共祖先的进程之间使用。通常一个管道由一个进程创建,然后该进程通

            过fork()函数创建子进程,父子进程之间通过管道交换数据。

        大多数Unix/Linux系统出了提供传统意义上的无名管道以外,还提供有名管道,对后者而言,第2中限制已不复存在。

2.5  共享内存

        共享内存允许两个或两个以上的进程共享同一块给定的内存区域。因为数据不需要在通信诸方之间来回复制,所以这是速度最块的一种进程间通信方式。

2.6  消息队列

        消息队列是由系统内核负责维护并可在多个进程之间共享存取的消息链表。优点是:

        传输可靠、流量受控、面向有结构的记录、支持按类型过滤。

2.7  信号量

        与共享内存和消息队列不同,信号量并不是为了解决进程间的数据交换问题。

        信号量关注的是有限的资源如何在无限的用户间合理分配,即资源竞争问题。

2.8  本地套接字

        BSD版本的有名管道。编程模型和网络通信统一。

3  有名管道(FIFO)

        有名管道是一种特殊的文件,它的路径名存在于文件系统中。

        有名管道文件在磁盘上只有i节点,没有数据块,也不保存数据。数据由内核操作。

3.1  mkfifo 命令

         通过shell命令mkfifo,基于有名管道实现进程间通信的逻辑模型:

        

        通过mkfifo命令可以创建有名管道文件:

                $ mkfifo myfifo

        即使是毫无亲缘关系的进程,也可以通过有名管道文件通信:

                $ echo 'Hello, FIFO!' > myfifo

                $ cat myfifo

                Hello, FIFO!

3.2  mkfifo()函数

        通过mkfifo()函数,基于有名管道实现进程间通信的编程模型:

        

        #include <sys/stat.h>

        int mkfifo (char const* pathname,  mode_t mode);

                功能:创建有名管道(文件)

                pathname:有名管道文件的路径

                mode:权限模式

                返回值:成0-1 

//wfifo.c  写入有名管道文件
#include<stdio.h>
#include<string.h>
#include<unistd.h>// write() close()
#include<fcntl.h>// open()
#include<sys/stat.h>// mkfifo()int main(void){//创建有名管道printf("%d进程:创建有名管道\n",getpid());if(mkfifo("./fifo",0664) == -1){perror("mkfifo");return -1;}//打开有名管道printf("%d进程:打开有名管道\n",getpid());int fd = open("./fifo",O_WRONLY);if(fd == -1){perror("open");return -1;}//写入有名管道printf("%d进程:发送数据\n",getpid());for(;;){//通过键盘获取数据 scanf fgets read fread fscanf char buf[64] = {};fgets(buf,sizeof(buf),stdin); //这里不用减1,fgets()会自动减!!//当输入!时退出循环if(strcmp(buf,"!\n") == 0){break;}//写入管道文件if(write(fd,buf,strlen(buf)) == -1){perror("write");return -1;}}//关闭有名管道printf("%d进程:关闭有名管道\n",getpid());close(fd);//删除有名管道printf("%d进程:删除有名管道\n",getpid());unlink("./fifo");printf("%d进程:大功告成\n",getpid());return 0;
}//编译后,开两终端,一个执行wfifo,一个执行rfifo
//rfifo.c  读取有名管道文件
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>int main(void){//打开有名管道printf("%d进程:打开有名管道\n",getpid());int fd = open("./fifo",O_RDONLY);if(fd == -1){perror("open");return -1;}//读取有名管道printf("%d进程:接收数据\n",getpid());for(;;){//读取有名管道char buf[64] = {};ssize_t size = read(fd,buf,sizeof(buf) - 1);if(size == -1){perror("read");return -1;}if(size == 0){printf("%d进程:对方关闭管道文件\n",getpid());break;}//显示printf("%s",buf);}//关闭有名管道printf("%d进程:关闭有名管道\n",getpid());close(fd);printf("%d进程:大功告成\n",getpid());return 0;
}//编译后,开两终端,一个执行wfifo,一个执行rfifo

 

4  无名管道(PIPE)

        通过pipe()函数,基于无名管道实现进程间通信的编程模型(5步):

        1)父进程调用pipe()函数在系统内核中创建无名管道对象,

             并通过该函数的输出参数pipefd,

             获得分别用于写该管道的两个 文件描述符pipefd[0]和pipefd[1]。

                

        2) 父进程调用fork()函数,创建子进程。

               子进程复制父进程的文件描述符表,因此子进程同样持有pipefd[0]和pipefd[1]  。

                

        3) 负责写数据的进程关闭无名管道对象的读端描述符pipefd[0],

               复测读数据的进程关闭无名管道对象的写端描述符pipefd[1]。

                

        4)父子进程通过无名管道对象以半双工的方式传输数据。 

              如果需要在父子进程间双向通信,一般会创建两个管道,一个从父流向子,一个相反。

                

        5)父子进程分别关闭自己所持有的写端或读端文件描述符。

              在相关联的所有文件描述符都被关闭后,该无名管道对象即从内核中被销毁。

                

//pipe.c  无名管道演示
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>int main(void){//父进程创建无名管道printf("%d进程:创建无名管道\n",getpid());int fd[2];//用来输出管道读端写端描述符if(pipe(fd) == -1){perror("pipe");return -1;}printf("fd[0] = %d\n",fd[0]);printf("fd[1] = %d\n",fd[1]);//父进程创建子进程pid_t pid = fork();if(pid == -1){perror("fork");return -1;}//子进程代码,从管道中读取数据if(pid == 0){printf("%d进程:接受数据\n",getpid());printf("%d进程:关闭写端\n",getpid());close(fd[1]);for(;;){//通过读端描述符读取数据char buf[64] = {};ssize_t size = read(fd[0],buf,sizeof(buf)-1);if(size == -1){perror("read");return -1;}if(size == 0){printf("%d进程:对方关闭写端描述符\n",getpid());break;}//显示printf("--->%s",buf);}printf("%d进程:关闭读端\n",getpid());close(fd[0]);printf("%d进程:大功告成\n",getpid());return 0;//!!!!}//父进程代码,向管道中写入数据printf("%d进程:发送数据\n",getpid());printf("%d进程:关闭读端\n",getpid());close(fd[0]);for(;;){//通过键盘获取数据char buf[64] = {};fgets(buf,sizeof(buf),stdin);//!退出if(strcmp(buf,"!\n") == 0){break;}//通过管道写端写入if(write(fd[1],buf,strlen(buf)) == -1){perror("write");return -1;}}printf("%d进程:关闭写端\n",getpid());close(fd[1]);//父进程收尸if(wait(NULL) == -1){perror("wait");return -1;}printf("%d进程:大功告成\n",getpid());return 0;
}//编译执行

5  管道须知

         1)从写端已被关闭的管道读取,只要管道中还有数据,依然可以被正常读取,一致到管道中没有数据了,这时read()函数会返回0(不是返回-1,也不是阻塞),指示读到文件尾。

        2)向读端已被关闭的管道写入,会直接出发SIGPIPE(13)信号。该信号的默认操作是终止执行写入动作的进程。但如果执行写入动作的进程事先13信号的处理设置为忽略或捕获,则write()函数会返回-1,并置errno为EPIPE。

        3)系统内核通常为每个管道维护一个4096字节的内存缓冲区(新系统更大)。如果写管道时发现缓冲区的空闲空间不足以容纳此次write()函数所要写入的字节,则write()函数阻塞,直到缓冲区的空闲空间变得足够大为止。

        4)读取一个写段处于开放状态的空管道,直接导致read()函数阻塞

6  管道符 | 的原理

        1)Unix/Linux系统中的多数shell环境都支持,

              通过管道符号 "|" 将前一个命令的输出作为后一个命令的输入:

                $ ls -l /etc | more           实现按空格键翻页

                $ ifconfig | grep inet      过滤得到ip地址

        2)系统管理员通常用这种方法,把多个简单的命令连接成一条工具链,解决复杂问题:

                $ 命令1  |  命令2  |  命令3

        3)假设用户输入以下命令:a | b,管道符工作原理如下:

                Shell进程调用fork()函数创建子进程A

                子进程A调用pipe()函数创建无名管道,而后执行:

                                dup2 (pipefd[1], STDOUT_FILENO);

                子进程A调用fork()函数创建孙进程B,孙进程B执行:

                                dup2 (pipefd[0], STDOUT_FILENO);

                子进程A和孙进程B分别调用exec ()函数创建a、b进程。

                a进程所有的输出都通过写段进入管道,而b进程所有的输入则来自管道的读端。

                

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

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

相关文章

如何通过linux调用企业微信发送告警消息

一、前期准备 1、企业微信具备管理企业权限。 2、服务器有公网IP或者可以将本机端口通过net映射到公网。 二、通过脚本向企业微信发送消息 1、创建sh脚本用来发送消息。 vim 2.sh 注意&#xff1a;脚本中xxxx信息需要在企业微信管理后台获取。 #!/bin/bash # 设置企业…

【漏洞复现】通达OA inc/package/down.php接口存在未授权访问漏洞 附POC

漏洞描述 通达OA(Office Anywhere网络智能办公系统)是由通达信科科技自主研发的协同办公自动化软件,是与中国企业管理实践相结合形成的综合管理办公平台。通达OA为各行业不同规模的众多用户提供信息化管理能力,包括流程审批、行政办公、日常事务、数据统计分析、即时通讯、…

类和对象——(3)再识对象

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 你说那里有你的梦想&#xff0c;…

【数电笔记】码制

目录 说明&#xff1a; 二进制代码 1. 二 - 十进制码 2. 常用二 - 十进制代码表 2.1 例题 可靠性代码 1. 格雷码 2. 奇偶校验码 3. 8421奇偶校验码表 说明&#xff1a; 笔记配套视频来源&#xff1a;B站 二进制代码 1. 二 - 十进制码 2. 常用二 - 十进制代码表 2.1 例题…

初识Java 18-6 泛型

目录 潜在类型机制 支持潜在类型机制的语言 Python的潜在类型机制 C的潜在类型机制 Java中的直接潜在类型机制 潜在类型机制的替代方案 反射 将方法应用于序列中的每个元素 Java 8的潜在类型机制&#xff08;间接实现&#xff09; 潜在类型机制的使用例&#xff08;S…

解决:uniapp项目打包微信小程序时,报错:failed to load config from /xx/xx-mall/vite.config.ts

复现步骤&#xff1a;在vscode终端中运行&#xff1a;pnpm build:mp-weixin-prod 命令&#xff0c;打包小程序生产包时&#xff0c;报错failed to load xxx/vite.config.ts&#xff0c;但实际项目根目录中有该vite.config.ts文件。 项目使用技术&#xff1a;uniapp vue3 node…

企业软件的分类|app小程序网站定制开发

企业软件的分类|app小程序网站定制开发 企业软件是指为满足企业管理和运营需求而设计和开发的一类软件&#xff0c;它通常用于支持企业的各项业务活动和流程。根据其功能和应用领域的不同&#xff0c;可以将企业软件分为以下几类。 1. 企业资源计划&#xff08;ERP&#xff09…

Paraformer 语音识别原理

Paraformer(Parallel Transformer)非自回归端到端语音系统需要解决两个问题&#xff1a; 准确预测输出序列长度&#xff0c;送入预测语音信号判断包含多少文字。 如何从encoder 的输出中提取隐层表征&#xff0c;作为decoder的输入。 采用一个预测器&#xff08;Predictor&…

2的幂运算

2的幂 描述 : 给你一个整数 n&#xff0c;请你判断该整数是否是 2 的幂次方。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 如果存在一个整数 x 使得 n 2x &#xff0c;则认为 n 是 2 的幂次方。 题目 : LeetCode 231.2的幂 : 231. 2 的幂 分…

element中el-form-item设置label-width=‘auto‘报错

文章目录 一、问题二、解决三、最后 一、问题 el-form中的设置了全局标题宽度是200px&#xff0c;此时想要对el-form-item取消标题宽度&#xff0c;设置了label-widthauto&#xff0c;结果&#xff0c;报错了~~~ <el-form label-width"200px" label-position&quo…

excel 计算断面水质等级

在工作中遇到根据水质监测结果要判断断面等级。写了下面的公式&#xff1a; 因子标准值 limits {COD: [15,15, 20, 15,20],氨氮: [0.15, 0.5, 1, 1.5, 2.0],总磷: [0.02, 0.1, 0.2, 0.3, 0.4] } excel公式&#xff1a; IFS(MAX(IF(M2>20,1,0), IF(N2>2,1,0), IF(O2&g…

websocket 消息包粗解

最近在搞websocket解析&#xff0c;记录一下: 原始字符串 &#xfffd;~&#xfffd;{"t":"d","d":{"b":{"p":"comds/comdssqmosm7k","d":{"comdss":{"cmdn":"success",…