Linux:进程的通信

目录

进程间的通信

管道

1.概念

2.匿名管道

3.命名管道

4.匿名管道与命名管道的区别

5.总结管道的特点

共享内存

1.原理

2.共享内存的建立

3.代码

1.相关函数

2.总结


进程间的通信

1.进程间通信目的

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

2.进程间通信发展

  • 管道
  • System V进程间通信
  • POSIX进程间通信

3.进程间通信分类

  • 管道: 匿名管道pipe , 命名管道
  • System V IPC:System V 消息队列,  System V 共享内存 , System V 信号量
  • POSIX IPC:消息队列  共享内存  信号量  互斥量  条件变量  读写锁

4.进程通信的本质理解

  • 进程间通信的前提: 首先让不同进程看到同一块"内存"
  • 看到的同一块"内存" 属于那一个进程? --> 不能隶属于任何一个进程,而应该强调共享

管道

1.概念

我们把从一个进程连接到另一个进程的一个数据流称为一个“管道"

管道都是单向通信, 传输内容的 管道中传输的都是"资源",管道通信的背后是进程通过管道通信,管道就是文件

2.匿名管道

1.概念:匿名管道是内存级文件, 没有名字 , 不会写入磁盘

如何做到让不同进程看到同一份"资源"?子进程继承

-- fork让子进程继承 --- 能够让具有血缘关系的进程进行进程间通信 ---常用于父子进程

2.代码编写

#include <unistd.h>
功能:创建一无名管道
原型
int pipe(int fd[2]); //fd[2]:输出型参数,期望通过调用它, 得到被打开的文件fd
参数
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码

匿名管道底层也是文件,但是没有名字, 默认以读写方式打开pipefd[0]: 读端 pipefd[1]: 写端

a.从键盘读取数据,写入管道,读取管道,写道屏幕

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

int main()
{
    int fds[2];
    char buff[100];
    int len;

    if(pipe(fds) == -1)
        perror("创建管道失败"),exit(-1);

    //从stdin读取数据到buff,并写入管道fd[1]
    while(fgets(buff,100,stdin))
    {
        len = strlen(buff);
        if(write(fds[1],buff,len) != len)
        {
            perror("写入失败");
            break;
        }
    
        memset(buff,0,sizeof(buff));
        //从管道里读取数据,并写入到stdout
        if(len = read(fds[0],buff,100) == -1)
        {
            perror("读取失败");
            break;
        }

        if(write(1,buff,len) != len)
        {
            perror("写入stdout失败");
            break;
        }
    }
    return 0;
}

b.父进程写数据,子进程读数据

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<assert.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;

int main()
{
    //创建管道
    int pipefd[2] = {0};
    int n = pipe(pipefd);
    assert(n != -1);
    (void)n;

    //创建子进程
    pid_t id = fork();
    assert(id != -1);
    if(id == 0)
    {
        //子进程 - 读
        close(pipefd[1]);
        //将数据读取到buff里,并打印
        char buff[1024];
        while(true)
        {
            ssize_t s = read(pipefd[0],buff,sizeof(buff)-1);
            if(s > 0)
            {
                cout<<"Father:"<<buff<<endl;
            }
        }
        exit(0);

    }
    //父进程 - 写
    close(pipefd[0]);
    string message = "我是父进程,我正在发送消息";
    int count = 0;
    char send_buff[1024];

    while(true)
    {
        //往send_buff里面写要发送的信息
        snprintf(send_buff,sizeof(send_buff),"%s[%d]:%d",message.c_str(),getpid(),count++);
        //写入管道
        write(pipefd[1],send_buff,strlen(send_buff));
        sleep(1);
    }

    pid_t ret = waitpid(id,nullptr,0);
    assert(ret > 0);
    (void)ret;

    return 0;
}

c.进程池

1.主进程派发任务,子进程接受任务(匿名管道实现)

2.使用循环创建多个子进程与管道,父进程保存其pipefd与pid (这里将其保存在vector中,构造pair)

3.Task.hpp:封装一个funtional包装器,将任务的执行方法放入vector里, 在创建一张hash表记录

4.子进程通pipefd[0],将要执行的任务编号读取,然后执行

   父进程通过pipefd[1],将要执行的任务编号写入给子进程读取

5.关闭文件描述符,以及回收子进程: 根据vector里记录的pid与pipefd,回收

代码:Linux-test: Linux下,提交代码 - Gitee.com

3.命名管道

1.概念:命名管道是磁盘文件(本质也是内存文件, 不过在磁盘上构建了文件名)

--管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。

--如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。该方式为:打开目录下同一文件

--命名管道是一种特殊类型的文件

2.代码编写

命令行创建命名管道

mkfifo filename  

程序中创建

接口: int mkfifo(const char* pathname , mode_t  mdoe)
头文件: <sys/types.h>   <sys/stat.h> 
返回值: 创建成功: 0  创建失败: -1

//示例
int main(int argc, char *argv[])
{
    mkfifo("p2", 0644);
    return 0;
}

a.创建命名管道,进程A写数据,进程B读取数据

mkfifo name_pipe
//A
echo "hello world" > name_pipe
//B
cat < name_pipe

删除管道文件: unlink 文件名(name_pipe)   也可以 rm  文件名

b.用命名管道实现server&client通信

server:创建管道文件, 打开管道文件, 从管道文件中读取信息到buff,并打印, 关闭文件,

client: 打开管道文件, 将buff的内容写入管道文件, 关闭文件

其中server端也可以创建多个子进程去读取管道文件里的信息

代码:Linux-test: Linux下,提交代码 - Gitee.com

4.匿名管道与命名管道的区别

1.它们让不同进程看到同一份资源的手段不一样,但它们的本质是一样的(都是文件)

命名管道可以让不同进程进行通信 ~~> 打开同一目录下相同的文件

匿名管道只能让父子进程进行通信 ~~> 子进程继承的方式

2.为什么匿名管道叫匿名?

--匿名管道 ~~> 内存级文件 ~~>在磁盘上没有对应的映像

--命名管道 ~~> 内存级文件 ~~>但在磁盘上给其构建了文件名

5.总结管道的特点

a.管道是用来进行具有血缘关系的进程进行进程间的通信 --- 常用于父子通信

b.管道具有通过让进程协同 , 提供了访问控制!

c.管道提供的是面向流式的通信服务 --- 面向字节流 --- 协议

d.管道是基于文件的,文件的生命周期是随进程的-->管道的生命周期是随进程的(如果通信双方退出,管道会释放)

e.管道是单向通信的 , 就是半双工通信的一种特殊情况

访问控制:

//写入的一方,fd没有关闭,如果有数据,就读,没有数据就等

//写入的一方,fd关闭,读取的一-方,read会返回0,表示读到了文件的结尾!

--写快,读慢,写满不能在写了

--写慢,读快,管道没有数据的时候,读必须等待

--写关,读0,标识读到了文件结尾

--读关,写继续写,OS终止写进程

共享内存

1.原理

2.共享内存的建立

共享内存的提供者: 操作系统

操作系统管理共享内存~~>先描述在组织 ~~> 共享内存 = 共享内存块 + 对应的内核数据结构

--创建共享内存 ~~>将该内存与地址空间建立映射 ~~> 进行通信 ~~> 去掉关联 ~~> 删除共享内存

3.代码

1.相关函数

ftok函数

功能:用于创建共享内存、消息队列和信号量等 IPC 对象的键值
原型
    key_t ftok(const char *pathname, int proj_id);
参数
    pathname:路径名 
    proj_id:项目标识符
返回值:成功返回一个键值,失败返回-1
    
//将 pathname 所指向文件的索引节点号(inode number)与 proj_id 结合起来,
//生成一个唯一的键值,这个键值可以用作创建或获取 IPC 对象的标识符。

shmget函数

功能:用来创建共享内存
原型
    int shmget(key_t key, size_t size, int shmflg);
参数
    key:这个共享内存段名字
    size:共享内存大小
    shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

shmat函数

功能:将共享内存段连接到进程地址空间
原型
    void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
    shmid: 共享内存标识
    shmaddr:指定连接的地址
    shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1

shmaddr为NULL,核心自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。
公式:shmaddr -(shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存

shmdt函数

功能:将共享内存段与当前进程脱离
原型
    int shmdt(const void *shmaddr);
参数
    shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段

shmctl函数

功能:用于控制共享内存
原型
    int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
    shmid:由shmget返回的共享内存标识码
    cmd:将要采取的动作(有三个可取值)
    buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

当进程运行结束, 我们的共享内存还存在, IPC资源的生命周期随内核

命令行删除:

查看共享内存: ipcs -m
删除共享内存: ipcrm -m shmid (手动)

a.示例1

// 1. 创建公共的Key值
key_t k = ftok(PATH_NAME, PROJ_ID); // const char* path_name, int id
assert(k != -1);                    // key创建失败

// 2. 创建共享内存 -- 建议要创建一个全新的共享内存 -- 通信的发起者
int shmid = shmget(k, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666); // 最后可以给0
if (shmid == -1)
{
    perror("shmget"); 
    exit(1);
}
//IPC_CREAT: 如果创建共享内存,底层已经存在(直接获取并返回),如果不存在(创建后再返回)
//IPC_CREAT | IPC_EXCL :底层已经存在(出错返回),如果不存在(创建后再返回)

// 3.将指定的共享内存,挂接到自己的地址空间
char *shmaddr = (char *)shmat(shmid, nullptr, 0); // 类似于malloc

// 进行通信
//...

// 4. 将指定的共享内存,从自己的地址空间中去关联
int n = shmdt(shmaddr);
assert(n != -1);
// 5.删除共享内存,IPC_RMID即便是有进程和当下的shm挂接,依旧删除共享内存
n = shmctl(shmid, IPC_RMID, nullptr);
assert(n != -1);

b.用共享内存实现server&client通信

server:获取key,创建共享内存,挂上链接,进行通信(读取数据), 去除链接,删除共享内存

client:获取key,获取共享内存,挂上链接,进行通信(写数据),去除链接

由于共享内存没有访问控制, 可以利用管道具有访问控制的特性(实现其访问控制)

代码:Linux-test: Linux下,提交代码 - Gitee.com

结果:

2.总结

1.共享内存是所有进程通信(IPC)速度最快的,不需要过多的拷贝(不需要将数据给OS):

只要通信双方使用shm,一方直接向共享内存中写入数据,另一方,立马就可以看到对方写入的数据

(共享内存一旦映射进各进程的地址空间,双方进程如果想要通信,直接进行内存级的读写即可)

2.共享内存缺乏访问控制 ! ~~> 会带来并发问题 (但我们可以用管道来增加访问控制)

3.为什么之前的pipe,fifo都要通过read,write来进行通信?

使用管道让双方看到的公共资源属于文件,需要操作系统自己去维护(文件是属于内核中的一种数据结构)

管道

共享内存

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

服务器宕机怎么办?怎么预防宕机?

相信不少用户会听到或者在文章中提到电脑宕机或者服务器宕机&#xff0c;不少用户对宕机的意思不太理解。那么服务器宕机是什么意思&#xff1f; 宕机属于计算机的术语&#xff0c;指电脑或者服务器不能正常工作。口语中我们简单的把停掉机器叫做down机&#xff0c;转换为汉字是…

高通平台开发系列讲解(USB篇)adb function代码分析

文章目录 一、FFS相关动态打印二、代码入口三、ffs_alloc_inst四、ep0、ep1&ep2的注册五、读写过程沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本文主要介绍高通平台USB adb function代码f_fs.c。 一、FFS相关动态打印 目录:msm-4.14/drivers/usb/gadget/fun…

Ubuntu下Lighttpd服务器安装,并支持PHP

1、说明 Lighttpd 是一个德国人领导的开源Web服务器软件&#xff0c;其根本的目的是提供一个专门针对高性能网站&#xff0c;安全、快速、兼容性好并且灵活的web server环境。具有非常低的内存开销、cpu占用率低、效能好以及丰富的模块等特点。 Lighttpd是众多OpenSource轻量级…

DSP2335的时钟PLL配置

PLL模块框图 xclkin是直接进来的外部时钟&#xff1b; 而下面的是振荡器&#xff08;晶振出来&#xff09;的时钟 PLLSTS 锁相环状态寄存器 PLLCR 锁相环控制寄存器 PLLSTS【oscoff】 决定着外部时钟的输入 PLLSTS【plloff】 锁相器关闭位 0使能PLL 锁相环控制寄存器…

day6:进程间的通信

思维导图&#xff1a; 实现多个进程之间的收发信息操作 create.c&#xff1a; #include <head.h> int main(int argc, const char *argv[]) {if(mkfifo("a_send_b",0664)!0){perror("");return -1;}if(mkfifo("b_send_a",0664)!0){perro…

vue3 封装一个按钮组件(可自定义按钮样式)

效果图 鼠标悬浮有对应的文字提示&#xff0c;且图标出现背景色和颜色 实现 目前提供五个固定样式的图标及三个用户自定义的图标&#xff0c;可根据需要补充 组件代码 <script setup lang"ts"> import { onMounted, PropType, reactive, ref, watch } from v…

jupyter内核错误

1、在dos窗口输入以下命令激活环境&#xff1a;anaconda activate 【py环境名&#xff0c;比如py37】&#xff08;目的是新家你一个虚拟环境&#xff09; 2、在虚拟环境py37下安装jupyter notebook&#xff0c;命令&#xff1a;pip install jupyter notebook 3、安装ipykerne…

python24.1.8

嵌套/多条件判断 嵌套 多条件&#xff1a; 实践&#xff1a;

pybind11实现numpy和OpenCV Mat的数据交互

1、编译安装pybind11 下载源代码&#xff1a;https://github.com/pybind/pybind11&#xff0c; 文档&#xff1a;https://pybind11.readthedocs.io/en/stable/compiling.html 解压后进入到命令行&#xff0c;如果有conda环境&#xff0c;就先进入想要的conda环境&#xff0c…

【python入门】day22:机票订购界面、北京地铁1号线运行图

机票订购界面 print(✈国内\t✪国际&#xff0c;港澳台\t↘发现低价) print(----------------------------------) print(航班类型&#xff1a;⊙单程 ⊙往返 ⊙多程&#xff08;含缺口程&#xff09;) print(出发城市&#xff1a;北京) print(到达城市&#xff1a;长春) pri…

OpenHarmony - 基于ArkUI框架实现日历应用

前言 对于刚刚接触OpenHarmony应用开发的开发者&#xff0c;最快的入门方式就是开发一个简单的应用&#xff0c;下面记录了一个日历应用的开发过程&#xff0c;通过日历应用的开发&#xff0c;来熟悉基本图形的绘制&#xff0c;ArkUI的组件的使用&#xff0c;UI组件生命周期&a…

Java集合框架概念详解

目录 1. 什么是Java集合框架&#xff1f;2. 常用接口介绍3. 常用实现类介绍4. 集合框架的应用场景 前言&#xff1a; Java集合框架是Java编程中最重要的工具之一。它提供了一套强大而灵活的数据结构和算法&#xff0c;用于存储和操作数据。本文将详细介绍Java集合框架的概念、常…

前端算法总结

基础–时间复杂度&空间复杂度 什么是复杂度分析 &#xff1f;为什么要进行复杂度分析 &#xff1f;如何进行复杂度分析 &#xff1f; 双指针 最接近的三数之和通过删除字母匹配到字典里最长单词 滑动窗口 滑动窗口的最大值 二叉树 二叉树的最近公共祖先 堆 最小的k个数前 K…

Vant2组件库van-list+Toast下拉加载滚动条回顶问题

目录 List 列表 Toast 轻提示 解决方案 1、不使用 Toast 的 加载提示 2、修改调整 pointer-event 属性值 3、判断是否为第一次加载再使用 背景 &#xff1a; 移动端项目 开发时&#xff0c;有数据长列表展示的场景需求&#xff0c;此时就用到了 Vant2 组件库里面的 <v…

结构型设计模式——外观模式

外观模式 有句话说这个世界就是个草台班子&#xff0c;只不过排面做的好看而已&#xff0c;里面都是一包糠。这句话来形容外观模式非常准确&#xff0c;外观模式又叫门面模式&#xff0c;顾名思义一个系统我不管你里面有多复杂有多少屎山代码&#xff0c;我只要求你提供的接口…

使用JGit拉取代码提示未授权not authorized

原因&#xff1a;2021年8月13日后不支持密码登录&#xff0c;需要使用token验证 调用时候需要先去git仓库创建个人令牌 需要在安全中心创建个人token&#xff0c;使用token名称作为账号&#xff0c;使用token作为密码。 另&#xff1a; Github克隆仓库的三种方式对比&#xff…

Qt连接数据库(内含完整安装包)

遇到问题必须多思考 这里是最全的Qt连接数据库步骤 qt下载地址 链接&#xff1a;https://pan.baidu.com/s/1wdnTfyL9MQlNOCrSmIOxrQ?pwddgqi 提取码&#xff1a;dgqi --来自百度网盘超级会员V1的分享 数据库百度网盘地址 链接&#xff1a;https://pan.baidu.com/s/1orCczey…

Python+Flask+MySQL的图书馆管理系统【附源码,运行简单】

PythonFlaskMySQL的图书馆管理系统【附源码&#xff0c;运行简单】 总览 1、《的图书馆管理系统》1.1 方案设计说明书设计目标需求分析工具列表 2、详细设计2.1 登录2.2 注册2.3 程序主页面2.4 图书新增界面2.5 图书信息修改界面2.6 普通用户界面2.7 其他功能贴图 3、下载 总览…

c++学习:STL库(框架)+字符串模板类string+vector容器+list链表

目录 stl库 常用组件包括 字符串库 字符串模板类string 头文件 最常用的字符串模板类 字符串类型 模板原型 模板的成员数据类型 模板成员函数 有些函数会有重载&#xff0c;可以去下面网址查看std::basic_string - cppreference.comhttps://zh.cppreference.com/w/cp…

探索C语言中的水仙花数及其计算方法

在计算机科学与数学的交叉领域中&#xff0c;有一种特殊的整数被称为“水仙花数”&#xff0c;它是指一个三位数&#xff0c;其各位数字立方和等于该数本身。例如&#xff0c;153是一个典型的水仙花数&#xff0c;因为1 5 3 1 125 27 153。 下面&#xff0c;我们通过一段…