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

news2024/12/24 18:34:17

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.coloradmin.cn/o/1274192.html

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

相关文章

JavaWeb服务器详解和后端分层解耦

JavaWeb HTTP协议请求数据格式响应数据格式协议解析 Web服务器请求响应请求参数的接收响应 分层解耦IOC&DI入门IOC详解 HTTP协议 超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传输的规则 特点&#xff1a; 基于TCP协议&#xff1a;面向连接&#xff0c;安全 …

第三方实验室LIMS管理系统源码,asp.net LIMS源码

LIMS实验室信息管理系统源码 LIMS系统的功能根据实验室的规模和任务而有所不同&#xff0c;其系统主要功能包括:系统维护、基础数据编码管理&#xff0c;样品管理、数据管理、报告管理、报表打印、实验材料管理、设备管理等。它可以取代传统的手工管理模式而给检测实验室带来巨…

aspera传输方案怎么样,需要选择aspera替代方案吗

Aspera传输方案是一种高速、可靠的文件传输解决方案&#xff0c;适用于需要大规模传输大文件或数据集的企业和组织。Aspera采用UDP协议及自己开发的FASP协议进行加速传输&#xff0c;能够在高延迟、高丢包网络环境下实现稳定快速的传输。 Aspera传输方案具有以下优点&#xff1…

【【带Micro Blaze的 AXI GPIO 控制LED实验】】

带Micro Blaze的 AXI GPIO 控制LED实验 AXI GPIO IP 核为 AXI 接口提供了一个通用的输入/输出接口。AXI GPIO 是一个软核&#xff08;Soft IP&#xff09;&#xff0c;是由用户通过配置芯片的逻辑资源来实现的一个功能模块。 实验任务 &#xff1a; 本章的实验任务是通过调用…

颠覆性语音识别:单词级时间戳和说话人分离

vbenjs/vue-vben-admin[1] Stars: 19.7k License: MIT Vue Vben Admin 是一个免费开源的中后台模板&#xff0c;使用最新的 vue3、vite4 和 TypeScript 等主流技术进行开发。该项目提供了现成的中后台前端解决方案&#xff0c;并可用于学习参考。 使用先进的前端技术如 Vue3/…

11 款顶级的免费 iPhone 数据恢复软件

iPhone 拥有巨大的存储容量。您可以在 iPhone 设备上存储图像、文档和视频等数据。有时&#xff0c;您的 iPhone 会发生许多意外事件&#xff0c;例如意外删除&#xff0c;从而导致数据丢失。这里有 11 个最好的免费 iPhone 数据恢复软件&#xff0c;您可以免费下载&#xff0c…

基于社区电商的Redis缓存架构-用户分享内容的分页列表缓存延迟构建以及异步通知缓存重建

分页列表缓存的延迟构建 首先&#xff0c;先来讲一下业务场景&#xff0c;用户会在 APP 中去分享内容&#xff0c;那么假如用户分享的是美食菜谱内容&#xff0c;在用户分享之后&#xff0c;先将这个美食菜谱的内容作为 k-v 进行缓存&#xff0c;但是呢&#xff0c;其实对于用…

Microsoft Remote Desktop高效、安全、稳定的远程办公解决方案

在今天的数字化时代&#xff0c;Remote Desktop远程办公已成为许多人的日常生活。无论你是因为工作需要&#xff0c;还是因为在家中需要访问公司服务器&#xff0c;微软远程连接软件都是一个理想的选择。 微软远程连接软件Remote Desktop是一款高效、安全、稳定的远程办公解决…

Clickhouse Join

ClickHouse中的Hash Join, Parallel Hash Join, Grace Hash Join https://www.cnblogs.com/abclife/p/17579883.html https://clickhouse.com/blog/clickhouse-fully-supports-joins-full-sort-partial-merge-part3 总结 本文描述并比较了ClickHouse中基于内存哈希表的3种连接…

Nginx基线检查

扩展知识: Nginx主配置文件:/etc/nginx/nginx.conf 这是Nginx的主要配置文件,用于配置全局的设置、HTTP块、事件处理、邮件等内容。 打开并编辑配置文件 vim /etc/nginx/nginx.conf 一、关于禁止显示服务器版本号和操作系统版本信息: 简介: 在错误页面和响应头中显示…

2024年软考高级信息系统项目管理师备考攻略

软考高级信息系统项目管理师是一项合格性考试&#xff0c;考试内容相对有限&#xff0c;因此真题的重复率较高。下一次考试与上一次相比&#xff0c;重复率不高&#xff0c;但与之前所有年份的真题相比&#xff0c;重复率较高。在这几次考试中&#xff0c;我认为最重要的是务必…

SQL Sever 基础知识 - 数据筛选

SQL Sever 基础知识 - 四、数据筛选 四、筛选数据第1节 DISTINCT - 去除重复值1.1 SELECT DISTINCT 子句简介1.2 SELECT DISTINCT 示例1.2.1 DISTINCT 一列示例1.2.2 DISTINCT 多列示例 1.2.3 DISTINCT 具有 null 值示例1.2.4 DISTINCT 与 GROUP BY 对比 第2节 WHERE - 过滤查询…

Linux常用命令----history命令

文章目录 在Linux中&#xff0c;history命令是一个极其有用的工具&#xff0c;它可以帮助用户查看和管理之前执行过的命令历史。这个功能对于快速查找和重用之前的命令特别有帮助。下面&#xff0c;我们将通过一些实例&#xff0c;详细介绍history命令的使用方法。 1. 基本使用…

【机器视觉技术栈】- 机器视觉基础

1.1 为什么采用机器视觉 人眼与机器视觉对比 人眼机器视觉精确性差&#xff0c;64灰度级&#xff0c;不能分辨小于100微米的目标强&#xff0c;256灰度级&#xff0c;可检测微米级目标速度慢&#xff0c;无法看清间隔小于40毫秒的运动目标快&#xff0c;快门时间可达10微秒适…

计算机网络HTTP篇

目录 一、HTTP基本概念 二、GET 与 POST 2.1、GET 与 POST 有什么区别&#xff1f; 2.2、GET 和 POST 方法都是安全和幂等的吗&#xff1f; 三、HTTP 缓存 3.1、强制缓存&#xff1a; 3.2、协商缓存 四、HTTP 特性 4.1、HTTP/1.1 4.1.1、HTTP/1.1 的优点 4.1.2、HTT…

【新品上市】启扬储能管理平板,打造储能管理新模式,助力全场景储能数智化升级!

随着可再生能源的快速发展&#xff0c;储能技术的应用日益广泛&#xff0c;储能系统成为解决可再生能源波动性和不可控制性的关键环节。储能系统通过实时监测、数据分析、远程控制等智能化功能&#xff0c;实现能量的高效利用和系统的稳定运行。 启扬智能推出 工业级储能管理平…

Docker 安装部署 Sentinel Dashboard

1、下载 jar 包 官方 jar 包下载地址&#xff1a;https://github.com/alibaba/Sentinel/releases 或者点击 链接 直接跳转到下载页 进入链接下载你需要的版本 下载完毕&#xff08;我这里统一放在一个sentinel目录内&#xff09; 2、编写 Dockerfile 文件&#xff08;这里我不…

OpenCV快速入门:彩蛋——小游戏制作

文章目录 前言一、游戏玩法1.1 核心玩法1.2 特殊事件 二、功能模块划分2.1 主游戏文件 (main.py)2.2 游戏对象 (game_objects.py)2.3 游戏逻辑 (game_logic.py)2.4 事件和奖励 (events_and_rewards.py)2.5. 游戏界面 (game_ui.py) 三、完整代码3.1 主游戏文件 (main.py)3.1.1 游…

仅仅通过提示词,GPT-4可以被引导成为多个领域的特定专家

The Power of Prompting&#xff1a;提示的力量&#xff0c;仅通过提示&#xff0c;GPT-4可以被引导成为多个领域的特定专家。微软研究院发布了一项研究&#xff0c;展示了在仅使用提策略的情况下让GPT 4在医学基准测试中表现得像一个专家。研究显示&#xff0c;GPT-4在相同的基…

浅聊代理(应用部署)

以前很少接触过项目的上线部署&#xff0c; 我对前后端交互的认知还停留在前端一个请求 对应后端一个API 比如后端提供: /api/backend/categories -GET 前端则通过使用ajax或者axios组件去构建http请求&#xff0c; 发送到: https://host:port/api/backend/categories -GET 一、…