Linux_进程间通信

news2025/1/22 12:30:12
  • 管道
  • 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

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

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

相关文章

ChatGpt报错:Your authentication token is no longer valid解决办法

今天打开ChatGpt突然提示Oops&#xff01;,Your authentication token is no longer valid.&#xff0c;之前还好好的&#xff0c;环境也没变啊&#xff0c;结果弄了好久终于解决&#xff0c;于是记录一下解决过程&#xff0c;顺便总结一下关于OpenAI各种报错的解决办法。 完整…

比较6*6范围内8个点425个结构的顺序

( A, B )---6*30*2---( 1, 0 )( 0, 1 ) 让网络的输入有6个节点&#xff0c;训练集AB各由6张二值化的图片组成&#xff0c;让A中有8个点&#xff0c;让B全是0&#xff0c;收敛误差7e-4&#xff0c;收敛199次&#xff0c;统计迭代次数平均值并排序。 假设这个6*6的结构的行和列都…

JavaWeb:SpingBoot原理 --黑马笔记

1. 配置优先级 在我们前面的课程当中&#xff0c;我们已经讲解了SpringBoot项目当中支持的三类配置文件&#xff1a; application.properties application.yml application.yaml 在SpringBoot项目当中&#xff0c;我们要想配置一个属性&#xff0c;可以通过这三种方式当中…

每日一练:LeeCode-617、合并二叉树【二叉树+DFS】

本文是力扣LeeCode-617、合并二叉树【二叉树DFS】 学习与理解过程&#xff0c;本文仅做学习之用&#xff0c;对本题感兴趣的小伙伴可以出门左拐LeeCode。 给你两棵二叉树&#xff1a; root1 和 root2 。 想象一下&#xff0c;当你将其中一棵覆盖到另一棵之上时&#xff0c;两…

力扣面试题 16.21. 交换和(哈希表)

Problem: 面试题 16.21. 交换和 文章目录 题目描述思路及解法复杂度Code 题目描述 思路及解法 1.分别求取array1与array2数组每一个元素的和&#xff08;sum1与sum2&#xff09;并同时将array2的元素存入一个set集合中&#xff1b; 2.如果sum1和sum2的和为奇数&#xff0c;则不…

深度学习基础之《深度学习介绍》

一、深度学习与机器学习的区别 1、特征提取方面 机器学习&#xff1a;人工特征提取 分类算法 深度学习&#xff1a;没有人工特征提取&#xff0c;直接将特征值传进去 &#xff08;1&#xff09;机器学习的特征工程步骤是要靠手工完成的&#xff0c;而且需要大量领域专业知识…

浅谈进制的转换

本文创作灵感来自CSDN咸鱼WCY 的 咸鱼小白学嵌入式之C语言&#xff08;2.进制&#xff09; 博主更完就没更了&#xff0c;决定书接上回&#xff08;喜 进制是个啥 要理解进制&#xff0c;首先哈&#xff0c;咱得知道不同进制的含义 说到底&#xff0c;各个进制其实有点像在…

Hive SQL编译成MapReduce任务的过程

一、 Hive 底层执行架构 1&#xff09; Hive简介 Hive是Facebook实现的一个开源的数据仓库工具。将结构化的数据文件映射为数据库表&#xff0c;并提供HQL查询功能&#xff0c;将HQL语句转化为MapReduce任务运行 2&#xff09; Hive本质&#xff1a;将 HQL 转化成 MapReduce 程…

Java 基于 SpringBoot+Vue 的社区医院系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

26. 可变参数和Collection集合工具类

可变参数与Collections 1. 可变参数1. 概述2. 格式3. 代码示例4. 注意事项 2. Collections集合工具类2.1 概述2.2 方法2.3 代码示例2.4 注意事项 1. 可变参数 1. 概述 可变参数&#xff08;Variable Arguments&#xff09;是指在参数列表中允许传入不定个数的参数。在许多编程…

多模态论文串讲·上【论文精读·46】只用 Transformer encoder 的一些方法viLT、clip、ALBEF、VLMO

目录 简单回顾一下 viLT 简单回顾CLIP 总结后提出改进 1 ALBEF 1.1 主体方法 1.1.1 模型结构 1.1.2 目标函数 1 ITCloss&#xff1a;align before fuse的align 2 ITM loss 3 MLM loss 1.1.3 动量蒸馏 1.2 预训练数据集 1.3 下游任务描述 1.4 实验结果 1 消融实验…

洛谷: [CSP-J 2023] 小苹果

题目描述 小 Y 的桌子上放着 n n n 个苹果从左到右排成一列&#xff0c;编号为从 1 1 1 到 n n n。 小苞是小 Y 的好朋友&#xff0c;每天她都会从中拿走一些苹果。 每天在拿的时候&#xff0c;小苞都是从左侧第 1 1 1 个苹果开始、每隔 2 2 2 个苹果拿走 1 1 1 个苹果…

【JAVA WEB】JavaScript--函数 作用域 对象

目录 函数 语法格式 示例 定义没有参数列表&#xff0c;也没有返回值的一个函数 定义一个有参数列表 &#xff0c;有返回值的函数 关于参数个数 函数表达式 作用域 作用域链 对象 基本概念 创建对象 1.使用 字面量 创建对象 2.使用new Object()创建对象 3.使…

在huggingface中如何使用 google/flan-t5-large

参考&#xff1a; https://huggingface.co/google/flan-t5-large 1&#xff1a; from huggingface_hub.hf_api import HfFolder HfFolder.save_token(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) from transformers import pipeline from transformers import T5Tokenizer, T5F…

【selenium】

selenium是一个Web的自动化测试工具&#xff0c;最初是为网站自动化测试而开发的。Selenium可以直接调用浏览器&#xff0c;它支持所有主流的浏览器。其本质是通过驱动浏览器&#xff0c;完成模拟浏览器操作&#xff0c;比如挑战&#xff0c;输入&#xff0c;点击等。 下载与打…

以谷歌浏览器为例 讲述 JavaScript 断点调试操作用法

今天来说个比较实用的东西 用浏览器开发者工具 对 javaScript代码进行调试 我们先创建一个index.html 编写代码如下 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content&…

谷粒商城【成神路】-【6】——商品维护

目录 &#x1f9c2;1.发布商品 &#x1f953;2.获取分类关联品牌 &#x1f32d;3.获取分类下所有分组和关联属性 &#x1f37f;4.商品保存功能 &#x1f9c8;5.sup检索 &#x1f95e;6.sku检索 1.发布商品 获取用户系统等级~&#xff0c;前面生成了后端代码&#xff…

产品交付双轮驱动思维模型下的思考的研发工具

一、产品交付双轮驱动思维模型 之前读过这样双轮驱动思维模型&#xff0c;其思维模型如下图所示&#xff0c;双轮驱动思维模型是一个产品价值交付模型&#xff0c;总的理念是以“真北业务价值”为导向&#xff0c;以“产品快速交付”为动力&#xff0c;将“业务价值”与“产品…

01.数据结构篇-链表

1.找出两个链表的交点 160. Intersection of Two Linked Lists (Easy) Leetcode / 力扣 例如以下示例中 A 和 B 两个链表相交于 c1&#xff1a; A: a1 → a2↘c1 → c2 → c3↗ B: b1 → b2 → b3 但是不会出现以下相交的情况&#xff0c;因为每个节点只有一个…

HCIA-HarmonyOS设备开发认证V2.0-3.2.轻量系统内核基础-软件定时器

目录 一、软件定时器基本概念二、软件定时器运行机制三、软件定时器状态四、软件定时器模式五、软件定时器开发流程六、软件定时器使用说明七、软件定时器接口八、代码分析&#xff08;待续...&#xff09;坚持就有收获 一、软件定时器基本概念 软件定时器&#xff0c;是基于系…