Linux_进程间通信

  • 管道
  • System V 共享内存
  • System V IPC 接口介绍

由于进程地址空间的存在,所以进程间有具有独立性,一个进程看不到另一个进程的数据。那么如果我们想让进程间通信,就必须先让它们先看到同一份资源。常见的进程间通信的方法有管道,System V IPC, POSIX IPC。管道是最古老的一种通信手段,System V ,POSIX 是用于进程间通信的标准。

一.管道

管道的本质是文件,让两个进程看到同一份文件,即指向同一个struct file 结构体,就能让两个进程看到同一份资源,之后就可以开始正常通信了。

1.1 匿名管道

匿名管道就是利用系统调用在程序内创建一个内存级文件(只有struct file结构体),通过父子进程间的继承关系来看到同一份文件。
接口:
image.png

  • 功能:用读写方式打开匿名文件,pipefd[0] 表示读方式打开,pipefd[1] 表示写方式打开

当父进程创建子进程时,子进程会复制大部分父进程的task_struct。故此父进程打开的文件,子进程也能看到。如图:
在这里插入图片描述

由于管道是单向通信,所以我们应该关闭不需要的fd。如果我们想让父进程读,子进程写,则需关闭父进程写端pipefd[1],子进程读端pipefd[0]。之后父进程就可以正常通信了。代码示例如下:

int main()
{
    int pipefd[2];
	int n = pipe(pipefd);
    assert(n != -1);

    pid_t id = fork();
    if (id == 0)
    {
        // 子进程
        close(pipefd[0]);

        // 通信代码。。。

    	//通信完毕,关闭文件描述符
        close(pipefd[1]);
        exit(0);
    }

    // 父进程
    close(pipefd[1]);

    // 通信代码。。。。

    waitpid(id, nullptr, 0);

    close(pipefd[0]);
    
    return 0;
}

特性:

  1. 匿名管道是一种单向通信方式,是半双工的一种特殊情况。
  2. 匿名管道用于具有血缘关系的进程之间通信,比如父子,兄弟
  3. 由于匿名管道是文件,所以生命周期随进程
  4. 面向字节流,当写入的字节数小于PIPE_BUFF(4096)时,保证其原子性,
  5. 由于匿名管道是通过系统调用写入和读出数据,所以自带保护机制(同步)
    1. 当写端退出,读端读完数据后,read系统调用返回0
    2. 当读端退出,写端立即退出,os向写端发送SIGPIPE信号
    3. 当缓冲区空时,读端阻塞等待写端写入数据
    4. 当缓冲区数据满时,写端阻塞等待读端读取数据

1.2 命名管道

匿名管道主要用于具有血缘关系的进程,如果想让两个没有关系的进程通信,就需要用到命名管道,命名管道具有Inode节点,但是没有Data Blocks,不会将数据刷新到磁盘,它只用于进程间通信。创建命名管道有两种方式:1.命令行:mkfifo 指令 2.函数接口mkfifo
接口:

  1. 命令行:mkfifo

image.png

  1. 函数接口mkfifo

在这里插入图片描述

  • pathname:在哪个路径下创建
  • mode:文件的起始权限

需要通信的多个进程中,只要有一个进程创建了命名管道,然后其他进程只需要知道路径,就能满足多个进程看到同一份资源。
代码:
下面构造一个客户端与服务端的代码示例。由服务器端创建命名管道,服务区读,客户端写

#define PATHNAME "./fifo"
// server.c  

int main()
{
    mkfifo(PATHNAME, 0666);

    int fd = open(PATHNAME, O_RDONLY);

	// 调用read函数进行读取数据通信

    close(fd);

    // 删除管道文件
    unlink(PATHNAME);
    
    
    return 0;
}

//
// client.c  

int main()
{
    int fd = open(PATHNAME, O_WRONLY);

	// 调用write函数向管道写入数据

    close(fd);
      
    return 0;
}

二.System V 共享内存

进程间通信除了管道,还可以调用系统接口在内存中创建一块共享空间,并且将这块共享空间映射到程序地址空间中(关联),此时进程就可以读写这块空间。共享内存在操作系统内可能有很多,为了便于管理,需要有一个结构体。类似于进程等于内核数据结构+代码,共享内存=内核数据结构+空间。
image.png
ipcs -m 可以查看系统内部的共享内存
ipcrm -m shmid 可以根据shmid删除共享内存

1.接口介绍
  1. 创建共享内存:shmget在这里插入图片描述

image.png

  • key:一个整数,多个进程只要保证调用shmget时,传递相同的key,就可以保证打开同一个共享内存。获取key可以用ftok函数,其中参数是自己设定的
  • size:共享内存的实际可用大小。操作系统分配是以4KB为单位的。
  • shmflg:位图结构体,常用的宏:IPC_CREAT IPC_EXCL,也可以设置共享内存的权限
  • 返回值:返回一个shmid值
  • shmid与key的关系。shmid和key的关系,类似于文件inode编号和fd的关系。key类似于inode编号,shmid类似于fd
    2. 进程与共享内存关联与取消关联

image.png

  • shmat:进程与共享内存关联
    • shmaddr一般设置为空,shmflg一般设置为0
    • 返回值:返回共享内存在虚拟空间中的地址
  • shmdt:
    • 根据虚拟地址将共享内存与进程取消关联
    1. 删除共享内存

在这里插入图片描述

  • shmctl有许多功能,例如查看共享内存结构体的属性,删除共享内存等等,我们重点用这个接口删除共享内存
  • 将cmd参数设置为IPC_RMID ,即可删除对应的共享内存
2.代码示例

三个文件,一个头文件,放函数定义,一个客户端文件,一个服务端文件,服务端创建共享内存和删除共享内存。

#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#include <cstring>
#include <unistd.h>


using namespace std;

//int shmget(key_t key, size_t size, int shmflg);
//key_t ftok(const char *pathname, int proj_id);

const string pathname = ".";
const int proj_id = 0432;
const size_t shmSize = 4096;

key_t getKey(const string& pathname, int proj_id)
{
    return ftok(pathname.c_str(), proj_id);
}

// 当shmflg = IPC_CREAT | IPC_EXCL ,表明创建一个共享内存,
//										如果不存在,则创建,存在则报错
int shmHelper(int shmflg)
{
    key_t key = getKey(pathname, proj_id);

    int shmid = shmget(key, shmSize, shmflg);

    cout << shmid << endl;
    if (shmid == -1)
    {
        cerr << errno << " : " << strerror(errno) << endl;
        exit(1);
    }

    return shmid;
}


int createShm()
{
    umask(0);
    return shmHelper(IPC_CREAT | IPC_EXCL|0666); 
    // 不存在则创建,存在则返回shmid
}

int getShm()
{
    return shmHelper(IPC_CREAT);
    // 不存在则创建,存在则失败返回
}

char* attchShm(int shmid)
{
    // 用法和malloc差不多
    char* begin = (char*)shmat(shmid, nullptr, 0);
    if (begin == (void*)-1)
    {
        cerr << errno << " : " << strerror(errno) << endl;
        exit(1);
    }

    return begin;
}


void detachShm(char* begin)
{
    int n = shmdt(begin);
    if (n == -1)
    {
        cerr << errno << " : " << strerror(errno) << endl;
        exit(1);
    }
}

void destroyShm(int shmid)
{
    int n = shmctl(shmid, IPC_RMID, nullptr);

    if (n == -1)
    {
        cerr << errno << " : " << strerror(errno) << endl;
        exit(1);
    }
}

#include"common.hpp"


int main()
{
    // 1. 创建共享内存
    int shmid = createShm();

    // 2.与进程关联
    char* begin = attchShm(shmid);

    //中间就是通信部分
    int i = 0;
    while (true)
    {
        sleep(3);
        cout << begin[i++] << endl;
        i %= 25;
        sleep(1);
    }

    // 3.去关联
    detachShm(begin);

    // 4. 销毁共享内存
    destroyShm(shmid);

    return 0;

#include"common.hpp"


int main()
{
    // 1. 获取共享内存
    int shmid = getShm(); 

    // 2.与进程关联
    char* begin = attchShm(shmid);

    //通信部分
    // 共享内存没有保护机制,所以

    int i = 0;
    while (true)
    {
        begin[i++] = 'a'+i;

        i %= 25;
        sleep(1);

    }

    // 3.去关联
    detachShm(begin);

    return 0;

3.共享内存的特点
  1. 大小是以4KB为单位的,比如在shmget中设置size为4087,它会向上对奇到2KB
  2. 速度在众多通信方式中是最快,因为它是直接访问内存,不用经过函数调用
  3. 没有保护机制(同步互斥)。因为不经过系统调用,所以保护机制需要由用户设置

三.System V IPC接口介绍

System V IPC 有三种通信方式:共享内存,消息队列,信号量。由于System V 定义的是一套标准,所以它们的接口都很相似。
命令行中可以用ipcs查看这三种通信方式的属性。也可以用 ipcs -q 查看消息队列,ipcs -s 查看信号量,ipcs -m 查看共享内存。删除命令:ipcrm -m/-s/-q
image.png
下面初步了解一下接口,知道它们的相似性。

1.消息队列
  1. 创建消息队列:msgget

image.png

  1. 发送/接受消息:msgsnd-发送,msgrcv-接收

image.png
其中,msgp需要用户自定义:
image.png

  1. 删除消息队列:msgctl(msqid, IPC_RMID, nullptr)

image.png

2.信号量

信号量(信号灯),可以将它看作一个计数器,表明可用资源的数量。信号量可与共享内存协同工作,每一次申请空间资源时,信号量-1,当信号量为0时,申请资源的进程挂起。当释放空间资源时,信号量+1。

  1. 创建:semget

image.png

  • nsems:一次创建多个信号量
  1. 操作:加1,减1。semop

image.png

  • nsops,指明需要对哪个信号量做操作,默认为0
  • sops,设置操作方式image.png

当sem_op 为-1时,表明信号量做-1操作;当sem_op为1时,表明信号量做+1操作

  1. 删除信号量

image.png

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

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

相关文章

“bound drug/molecule”or “unbound drug/molecule”、molecule shape、sketching是什么?

“bound drug/molecule”or “unbound drug/molecule” For clarity, the following terms will be used throughout this study: “bound drug/molecule” (or “unbound drug/molecule”) refers to the drug/molecule that is bound (or unbound) to proteins [48]. 意思就是…

力扣精选算法100道——【模板】前缀和 (二维)

目录 &#x1f388;题目解析 &#x1f388;算法原理 &#x1f388;实现代码 二维前缀和【模板】 &#x1f388;题目解析 上一题我们讲述了一维的前缀和求法。 第一行三个参数&#xff0c;n是行数3&#xff0c;m是列数4&#xff0c;q3代表查询次数 接下来就是n行m列的矩阵…

用shell脚本批量修改文件名

今天继续分享一个实用的shell脚本哈 示例&#xff1a;# touch file{1..3}.txt # ls file1.txt file2.txt file3.txt 脚本内容&#xff1a; #!/bin/bash #方法1&#xff1a;for file in $(ls *txt); domv $file bbs_${file#*_}# mv $file $(echo $file |sed -r s/.*(_.*)/b…

C++入门篇——类与对象重点解析(下篇)

1. 再谈构造函数 1.1 构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给对象中各个成员变量一个合适的初始值。 class Date { public: Date(int year, int month, int day) {_year year;_month month;_day day; } private:int _year;int _m…

AI大模型开发架构设计(9)——AI 编程架构刨析和业务应用实战案例

文章目录 AI 编程架构刨析和业务应用实战案例1 AI编程代码生成模型剖析编程方式的发展代码自动生成基于大模型的AI编程工具——Github Copilot以 CodeGeeX 为例-发展过程以 CodeGeeX 为例-训练过程以 CodeGeeX 为例-大规模代码数据处理以 CodeGeeX 为例-模型结构以 CodeGeeX 为…

IDEA 推荐插件

grep-console 输出日志换颜色 MybatisLogFormat 直接复制mybatis的日志成完整的SQL SequenceDiagram 生成时序图

OpenCV 人脸检测(易上手版)

在丰富多彩的计算机视觉世界中&#xff0c;人脸检测是最有趣和最广泛应用的领域之一。无论是在安全系统、用户界面控制&#xff0c;还是在社交媒体中应用过滤器&#xff0c;准确有效地检测人脸的能力都是至关重要的。今天&#xff0c;很高兴与大家分享如何在 Python 中使用 Ope…

Java实现软件学院思政案例库系统 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统管理员2.2 普通教师 三、系统展示四、核心代码4.1 查询思政案例4.2 审核思政案例4.3 查询思政课程4.4 思政案例点赞4.5 新增思政案例评语 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的软件学…

【制作100个unity游戏之25】3D背包、库存、制作、快捷栏、存储系统、砍伐树木获取资源、随机战利品宝箱4(附带项目源码)

效果演示 文章目录 效果演示系列目录前言快捷栏操作&#xff0c;并可切换手臂绘制快捷栏UI代码控制快捷栏切换 快捷栏显示选中效果绘制选中效果UI图代码重新定位选中效果图 源码完结 系列目录 前言 欢迎来到【制作100个Unity游戏】系列&#xff01;本系列将引导您一步步学习如…

CUDA编程 - 共享内存 - shared memory - 学习记录

CUDA编程 - 共享内存 共享内存一、为什么要使用 shared memory&#xff1f;1.1、从硬件出发理解&#xff1a;1.2、从软件出发理解&#xff1a; 二、如何使用shared memory2.1、静态共享内存2.2、动态共享内存 三、实践 - 使用共享内存执行矩阵乘法总结 共享内存 一、为什么要使…

探索机器学习:定义、算法及应用领域

目录 前言1 机器学习的定义2 机器学习算法2.1 监督学习2.2 无监督学习2.3 强化学习 3 机器学习的应用3.1 智能搜索3.2 医疗诊断3.3 无人驾驶 结语 前言 机器学习&#xff0c;源自Arthur Samuel的定义&#xff0c;赋予计算机通过领域学习的能力&#xff0c;使其在不需要明确程序…

编写Makefile

现在我们将创建一个程序&#xff0c;该程序能够读取次位码文件并打印其中定义的函数名称&#xff0c;以及它们的基本块数&#xff0c;从而显示LLVM库的易用性 什么是Makefile&#xff1f; C语言中&#xff0c;我们使用visual studio开发软件时候&#xff0c;写程序开始时候都…

专业140+总分420+浙江大学842信号系统与数字电路考研经验电子信息与通信,真题,大纲,参考书。

今年考研已经结束&#xff0c;初试专业课842信号系统与数字电路140&#xff0c;总分420&#xff0c;很幸运实现了自己的目标&#xff0c;被浙大录取&#xff0c;这在高考是想都不敢想的学校&#xff0c;在考研时实现了&#xff0c;所以大家也要有信心&#xff0c;通过自己努力实…

Kubernetes(K8S)集群部署实战

目录 一、准备工作1.1、创建3台虚拟机1.1.1、下载虚拟机管理工具1.1.2、安装虚拟机管理工具1.1.3、下载虚Centos镜像1.1.4、创建台个虚拟机1.1.5、设置虚拟机网络环境 1.2、虚拟机基础配置&#xff08;3台虚拟机进行相同处理&#xff09;1.2.1、配置host1.2.2、关闭防火墙1.2.3…

【北邮鲁鹏老师计算机视觉课程笔记】08 texture 纹理表示

【北邮鲁鹏老师计算机视觉课程笔记】08 texture 纹理表示 1 纹理 规则和不规则的 2 纹理的用处 从纹理中恢复形状 3 分割与合成 4 分析纹理进行分类 通过识别纹理分析物理性质 如何区分纹理 5 寻找有效的纹理分类方法 发现模式、描述区域内模式 A对应图2 B对应图…

LeetCode、136. 只出现一次的数字【简单,位运算】

文章目录 前言LeetCode、136. 只出现一次的数字【简单&#xff0c;位运算】题目链接与分类思路异或一遍运算 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注于Java后端技术…

Python实现EMV指标计算:股票技术分析的利器系列(2)

Python实现EMV指标计算&#xff1a;股票技术分析的利器系列&#xff08;2&#xff09; 介绍算法解释&#xff1a; 核心代码&#xff1a;rolling函数介绍 完整代码&#xff1a;一定要看 介绍 先看看官方介绍&#xff1a; EMV(简易波动指标&#xff09; 用法 1.EMV 由下往上穿越…

【MySQL】:分组查询、排序查询、分页查询、以及执行顺序

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; MySQL从入门到进阶 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一. 分组查询1.1 语法1.2 where与having区别1.3 注意事项:1.4 案例: 二. 排序查询…

C# CAD2016 判断多边形的方向正时针或逆时针旋转

方法一&#xff1a;基于相邻顶点相对位置判断顺时针排列 // 计算当前子序列是否为顺时针排列 for (int i 1; i < outerPoints.Count; i) {int index (startVertexIndex i) % outerPoints.Count;int prevIndex (startVertexIndex i - 1) % outerPoints.Count;Point2d c…

day39 Bootstrap——容器简括

前言 前言Bootstrap5 容器容器内边距容器的边框和颜色响应式容器 前言 Bootstrap&#xff0c;来自 Twitter&#xff0c;是目前最受欢迎的前端框架。Bootstrap 是基于 HTML、CSS、JAVASCRIPT 的&#xff0c;它简洁灵活&#xff0c;使得 Web 开发更加快捷。 Bootstrap5 容器 B…