[Linux]:进程间通信(下)

img

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:Linux学习
贝蒂的主页:Betty’s blog

1. system V通信

前面我们所探究的通信方式都是基于管道文件的,而接下来我们将谈论的是在System V标准下,进程间通信的方式。

System V标准是在同一主机内的进程间通信方案,是站在OS层面专门为进程间通信设计的方案,其主要提供三个主流方案:system V共享内存system V消息队列system V信号量。

其中System V 共享内存和 System V 消息队列的目的在于传送数据。而 System V 信号量则是为确保进程间的同步与互斥而设计,虽然 System V 信号量看似与通信没有直接关联,但实际上它也属于通信范畴。

2. system V共享内存

2.1 共享内存的介绍

共享内存本质就是不同进程间能够访问同一块物理空间,从而实现进程间通信。

共享内存的实现方式是在物理内存中申请一块内存空间。接着,将这块内存空间与各个进程各自的页表分别建立映射,并在虚拟地址空间的共享区开辟空间,把虚拟地址填充到页表对应位置,从而建立虚拟地址与物理地址的对应关系。此时,不同进程便能访问同一份物理内存,此物理内存即共享内存。

画板

2.2 共享内存的相关函数

一般来说我们创建共享内存大致可以分为两步:第一步就是在物理内存上申请共享内存。第二步将申请到的物理内存挂接到对应的地址空间上。这两步都分别对应两个函数:shmgetshmat。如果想释放共享内存,步骤就刚好相反,首先第一步将共享内存与地址空间去关联,即取消映射关系,第二步将释放共享内存空间,即将物理内存归还给系统。这两步都分别对应两个函数:shmdtshmctl

2.2.1 shmget函数
  1. 函数原型:int shmget(key_t key, size_t size, int shmflg);
  2. 参数:第一个参数key,表示待创建共享内存在系统当中的唯一标识。第二个参数size,表示待创建共享内存的大小。第三个参数shmflg,表示创建共享内存的方式。
  3. 返回值:shmget调用成功,返回一个有效的共享内存标识符(用户层标识符),否则返回-1。

首先shmget需要传入的第一个参数key需要通过函数ftok获取,其原型如下:

key_t ftok(const char *pathname, int proj_id);

其中pathname代表一个已存在的路径名,proj_id代表一个项目IDftok函数可以将通过特定的算法将这两个参数转换出对应的系统标识符key,否则返回-1。

值得注意的是:

  1. 使用ftok函数生成key值可能会产生冲突,此时需要对传入ftok函数的参数进行修改。
  2. 如果不同进程间需要通信,需要采用同样的路径名和和项目ID,进而生成同一个key值,才能找到同一个共享资源。

第二个参数size一般建议是4096的整数倍,假设如果传的是4097,操作系统实际上申请的空间大小是 4096*2,虽然操作系统多申请了,但是多余的部分用户不能使用,这样就可能造成空间的浪费。

第三个参数shmflag标记位常用选项有两种:

  1. IPC_CREAT :如果申请的共享内存不存在,就创建,存在,就获取并返回。
  2. IPC_EXCL :如果申请的共享内存存在,就出错返回。

IPC_CREAT | IPC_EXCL :如果申请的共享内存不存在,就创建,存在就出错返回。这俩选项一起使用保证了,如果我们申请成功了一个共享内存,这个共享内存一定是一个新的。

#include <stdio.h>
#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/shm.h> 
#include <unistd.h>
#define MY_PATH "/home/beidi/tmp" //路径名
#define PROJ_ID 0x6666 //项目ID
#define SIZE 4096 //共享内存大小
int main()
{//获取keykey_t key = ftok(MY_PATH,PROJ_ID);if(key < 0){perror("ftok:");return 1;}int shmid = shmget(key,SIZE,IPC_CREAT|IPC_EXCL);if(shmid < 0){perror("shmget:");return 2;}printf("key: %x\n",key);printf("shmid : %d\n",shmid);return 0;
}

并且我们也可能通过指令ipcs - m查看相关信息。

其分别每一项的含义:

标题含义
key系统区别各个共享内存的唯一标识
shmid共享内存的用户层 id
owner共享内存的拥有者
perms共享内存的权限
bytes共享内存的大小
nattch关联共享内存的进程数
status共享内存的状态

因为共享内存的生命周期是随内核的,所以如果不手动回收,这个共享内存就会一直存在。除了通过特定的函数外,我们也能够通过指令ipcrm -m shmid释放指定的共享内存资源。

2.2.2 shmat函数
  1. 函数原型:void *shmat(int shmid, const void *shmaddr, int shmflg);
  2. 参数:第一个参数shmid,表示待关联共享内存的用户级标识符。第二个参数shmaddr,指定共享内存映射到进程地址空间的某一地址,通常设置为NULL,表示让内核自己决定一个合适的地址位置。第三个参数shmflg,表示关联共享内存时设置的某些属性。
  3. 返回值:shmat调用成功,返回共享内存映射到进程地址空间中的起始地址。否则,返回(void*)-1

一般shmat函数的第三个参数传入的常用的选项有以下三种:

  1. SHM_RDONLY:关联共享内存后只进行读取操作。
  2. SHM_RND:若shmaddr不为NULL,则关联地址自动向下调整为SHMLBA的整数倍。
  3. 0:默认为读写权限。
2.2.3 shmdt函数
  1. 函数原型:int shmdt(const void *shmaddr);
  2. 参数:shmaddr为待去关联共享内存的起始地址,即调用shmat函数时得到的返回值。
  3. 返回值:shmdt调用成功,返回0。否则返回-1。
2.2.4 shmctl函数
  1. 函数原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  2. 参数:第一个参数shmid,表示所控制共享内存的用户级标识符。第二个参数cmd,表示具体的控制动作。第三个参数buf,用于获取或设置所控制共享内存的数据结构。
  3. 返回值:shmctl调用成功,返回0。否则返回-1。

其中第二个选项cmd常见有三个选项:

  1. IPC_STAT:获取共享内存的当前关联值,此时参数buf作为输出型参数。
  2. IPC_SET:在进程有足够权限的前提下,将共享内存的当前关联值设置为buf所指的数据结构中的值.
  3. IPC_RMID:删除共享内存段,此时buf可以传NULL

以下就是一个完整的共享内存的使用方式:

#include <stdio.h>
#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/shm.h> 
#include <unistd.h>
#define MY_PATH "/home/beidi/tmp" //路径名
#define PROJ_ID 0x6666 //项目ID
#define SIZE 4096 //共享内存大小
int main()
{//获取keykey_t key = ftok(MY_PATH,PROJ_ID);if(key < 0){perror("ftok:");return 1;}//开辟共享内存int shmid = shmget(key,SIZE,IPC_CREAT|IPC_EXCL|0666);if(shmid < 0){perror("shmget:");return 2;}printf("key: %x\n",key);printf("shmid : %d\n",shmid);//关联char*mem = (char*)shmat(shmid,NULL,0);if(mem == (void*)-1){perror("shmat:");return 3;}//去关联shmdt(mem);//释放共享内存shmctl(shmid,IPC_RMID,NULL);return 0;
}

2.3 共享内存与管道的对比

同样是实现客户端client与服务端server的交互,共享内存明显会比管道通信快的多。

画板

从上图观察我们就可以看出,管道通信将一个文件内容从服务端发送到客户端一共需要四次拷贝:

  1. 服务端将信息从输入文件复制到服务端的临时缓冲区中。
  2. 将服务端临时缓冲区的信息复制到管道中。
  3. 客户端将信息从管道复制到客户端的缓冲区中。
  4. 将客户端临时缓冲区的信息复制到输出文件中。

而如果是共享内存却只需要两次拷贝即可,大大提升效率。

  1. 从输入文件到共享内存。
  2. 从共享内存到输出文件。

画板

但是共享内存也有明显的缺陷,那就是没有同步与互斥这样的保护机制。

3. system V消息队列

因为system V消息队列的实用性越来越低,所以这里并不重点介绍,如果想了解详细用法,可以查官方文件。

3.1 消息队列的基本原理

消息队列本质上是在系统中创建的一个队列。该队列的每个成员为一个数据块,且每个数据块由类型和信息两部分组成。两个相互通信的进程以某种方式访问同一个消息队列。当这两个进程向对方发送数据时,均在消息队列的队尾添加数据块;而当它们获取数据块时,则都在消息队列的队头取用数据块。

画板

同样和共享内存一样,消息队列申请的资源生命周期随内核,所以我们需要特定函数或者指令释放资源。

3.2 消息队列的相关函数

  1. 消息队列的创建:int msgget(key_t key, int msgflg);
  2. 消息队列的发送:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
  3. 消息队列的获取:ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
  4. 消息队列的销毁:int msgctl(int msqid, int cmd, struct msqid_ds *buf);

其中msgsnd函数的第二个参数msgp为一个结构体:

struct msgbuf{long mtype;       /* message type, must be > 0 */char mtext[1];    /* message data */
};

该结构当中的第一个成员mtype为发送数据的类型,第二个成员mtext即为待发送的信息,当我们定义该结构时,mtext的大小可以自己指定。

4. system V信号量

4.1 信号量的介绍

进程间通信通过共享资源来实现,这虽然解决了通信的问题,但是也引入了新的问题,那就是多个执行流共用同一个临界资源,若是不对该临界资源进行保护,就可能导致各个进程从临界资源获取的数据不一致等问题。为了解决这个问题,我们引入了信号量。

System V信号量本质上是一个计数器,用于衡量临界资源中的资源数目。它对临界资源内部的资源数进行统计,同时操作系统为其提供了一种对临界资源的预定机制。所有进程在访问临界资源之前,必须先申请信号量。

比如说我们现在有100byte的临界资源,我们以10byte为单位划分,就能划分出十份临界资源,也就需要申请10个信号量。

画板

但是信号量本质也是一个临界资源,为了防止其同时被多个执行流访问,我们可以将信号量设置为1,这种信号量我们称之为二元信号量,我们可以通过以下代码来具体说明为什么二元信号量能实现临界资源的互斥:

画板

当进程 A 申请访问共享内存资源时,如果此时信号量 sem 的值为 1,那么进程 A 申请资源成功,此时需要将 sem 的值减 1。之后进程 A 便可对共享内存进行一系列操作。然而,在进程 A 访问共享内存期间,若进程 B 申请访问该共享内存资源,此时 sem 的值变为 0,进程 B 会被挂起。直到进程 A 访问共享内存结束后将 sem 的值加 1,这时才会将进程 B 唤起,随后进程 B 再对该共享内存进行访问操作。

在这种情况下,无论何时都只会有一个进程对同一份共享内存进行访问操作,从而解决了临界资源的互斥问题。

其中信号量sem减减的操作我们称为P操作,而信号量sem加加的操作我们成为V操作,PV操作就是申请与释放信号量的过程,而且进程中访问临界资源的代码我们称之为临界区。

并且我们还需要保证PV操作是原子的,即只有不做与做完两种状态,不存在正在做这种状态。

因为从汇编角度看,我们的 --++ 操作其实是不安全的,他们转成汇编,一般会对应三条汇编指令:**从内存中读取数据到 CPU 中;CPU 内进行操作;CPU 将结果写回内存。**进程在运行的时候,随时可能被切换,这就导致在多进程共享信号量下, --++ 操作可能会导致信号量的值发生错乱。为了防止这种情况,我们需要保证PV操作的原子性,即只有一条汇编指令。

4.2 信号量相关函数

  1. 信号量的创建:int semget(key_t key, int nsems, int semflg)。
  2. 信号量的获取:int semop(int semid, struct sembuf *sops, unsigned nsops)。
  3. 信号量的销毁:int semctl(int semid, int semnum, int cmd, …)。

5. system V的数据结构

在我们操作系统中,肯定会同时存在大量进程进行通信,即肯定会存在大量的system V,为了方便把这些申请的临界资源管理起来,操作系统本身肯定会维护一个关于它们的数据结构。

比如这是维护共享内存的数据结构:

struct shmid_ds 
{struct ipc_perm     shm_perm;   /* operation perms */int         shm_segsz;  /* size of segment (bytes) */__kernel_time_t     shm_atime;  /* last attach time */__kernel_time_t     shm_dtime;  /* last detach time */__kernel_time_t     shm_ctime;  /* last change time */__kernel_ipc_pid_t  shm_cpid;   /* pid of creator */__kernel_ipc_pid_t  shm_lpid;   /* pid of last operator */unsigned short      shm_nattch; /* no. of current attaches */unsigned short      shm_unused; /* compatibility */void            *shm_unused2;   /* ditto - used by DIPC */void            *shm_unused3;   /* unused */
};

这是维护消息队列的数据结构:

struct msqid_ds 
{struct ipc_perm msg_perm;struct msg *msg_first;      /* first message on queue,unused  */struct msg *msg_last;       /* last message in queue,unused */__kernel_time_t msg_stime;  /* last msgsnd time */__kernel_time_t msg_rtime;  /* last msgrcv time */__kernel_time_t msg_ctime;  /* last change time */unsigned long  msg_lcbytes; /* Reuse junk fields for 32 bit */unsigned long  msg_lqbytes; /* ditto */unsigned short msg_cbytes;  /* current number of bytes on queue */unsigned short msg_qnum;    /* number of messages in queue */unsigned short msg_qbytes;  /* max number of bytes on queue */__kernel_ipc_pid_t msg_lspid;   /* pid of last msgsnd */__kernel_ipc_pid_t msg_lrpid;   /* last receive pid */
};

这是维护信号量的数据结构:

struct semid_ds 
{struct ipc_perm sem_perm;       /* permissions .. see ipc.h */__kernel_time_t sem_otime;      /* last semop time */__kernel_time_t sem_ctime;      /* last change time */struct sem  *sem_base;      /* ptr to first semaphore in array */struct sem_queue *sem_pending;      /* pending operations to be processed */struct sem_queue **sem_pending_last;    /* last pending operation */struct sem_undo *undo;          /* undo requests on this array */unsigned short  sem_nsems;      /* no. of semaphores in array */
};

其中我们发现无论时共享内存,消息队列,还是信号量数据结构的第一个成员都是一个ipc_perm类型的结构体变量,定义如下:

struct ipc_perm
{__kernel_key_t  key;__kernel_uid_t  uid;__kernel_gid_t  gid;__kernel_uid_t  cuid;__kernel_gid_t  cgid;__kernel_mode_t mode;unsigned short  seq;
};

其中就包含了我们关键的系统表示符key

其中msqid_dsmsqid_dssemid_ds``与ipc_perm结构体分别在/usr/include/linux/sem.h/usr/include/linux/ipc.h中定义。

所以操作系统管理system VIPC资源,本质就是对ipc_perm的管理,在Linux中,就是通过一个柔性数组所管理的。

画板

我们如果想访问某个IPC资源,只需要通过数组下标找到对应资源,再进行强转即可。比如我们访问共享内存中的sh_ctime成员,只需(struct shmid_ds* )array[下标]->sh_ctime。而前面我们所使用的用户层标识符shmidmsqidsemid本质上就是内核中柔性数组的下标。

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

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

相关文章

深入解析代理模式:静态代理、JDK 动态代理和 CGLIB 的全方位对比!

代理模式&#xff08;Proxy Pattern&#xff09;是一种结构型设计模式&#xff0c;它提供了对象的替身&#xff0c;即代理对象来控制对实际对象的访问。通过代理对象&#xff0c;可以在不修改目标对象的情况下&#xff0c;扩展或控制其功能。例如&#xff0c;代理模式可以用于延…

Cortex-A7的GIC(通用中断控制器):边沿触发和电平触发中断的区别

0 资料 ARM Generic Interrupt Controller Architecture version 2.0 Architecture Specification1 边沿触发和电平触发中断的区别 1.1 边沿触发和电平触发中断官方解释 边沿触发&#xff08;Edge-triggered&#xff09; This is an interrupt that is asserted on detectio…

DFS:深搜+回溯+剪枝实战解决OJ问题

✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来! 文章目录 目录 文章目录 前言 一 排列、子集问题 1.1 全排列I 1.2 子集I 1.3 找出所有子集的异或总和 1.4 全排列II 1.5 字母大小写全排列 1.6 优美的排列 二 组合问题 2.1 电话号码的数字组合 …

物联网架构

1 三层架构 三层架构就像我们拿着一个设备&#xff0c;通过网络直接连接到服务器获取结果&#xff0c;步骤简单。 举个例子&#xff1a;智能家居的温度监控系统 1. 感知层&#xff08;设备与传感器&#xff09; 在智能家居系统中&#xff0c;温度传感器被安装在家里的各个房间…

战斗机检测系统源码分享

战斗机检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Visio…

4.提升客户服务体验:ChatGPT在客服中的应用(4/10)

本文大纲旨在指导撰写一篇全面探讨ChatGPT如何通过优化客户服务流程、提供实际应用案例和用户反馈&#xff0c;以提升客户服务体验的深入博客文章。 引言 在当今竞争激烈的商业环境中&#xff0c;客户服务已成为企业成功的关键因素。优质的客户服务不仅能够增强客户满意度和忠…

第十一章 【后端】商品分类管理微服务(11.1)——创建父工程

第十一章 【后端】商品分类管理微服务 11.1 创建父工程 项目名称:EasyTradeManagerSystem:Easy 表示简单易用,Trade 表示交易,Manager 表示管理,System 表示系统,强调系统在商品交易管理方面的便捷性,简称 etms。 新建工程 yumi-etms yumi-etms 作为所有模块的父工程,…

1.使用 IDEA 过程中的英语积累 - File 菜单(每一次重点积累 5 个单词)

前言 学习可以不局限于传统的书籍和课堂&#xff0c;各种生活的元素也都可以做为我们的学习对象&#xff0c;本文将利用 IDEA 页面上的各种英文元素来做英语的积累&#xff0c;如此做有 3 大利 这些软件在我们工作中是时时刻刻接触的&#xff0c;借此做英语积累再合适不过&…

QT + WebAssembly + Vue环境搭建

Qt6.7.2安装工具 emsdk安装 git clone https://github.com/emscripten-core/emsdk.git cd emsdk emsdk install 3.1.50 emsdk activate 3.1.50 Qt Creator配置emsdk 效果 参考 GitHub - BrockReece/vue-wasm: Vue web assembly loader Emscripten cmake多版本编译-CSDN博客 …

vue使用TreeSelect设置带所有父级节点的回显

Element Plus的el-tree-select组件 思路&#xff1a; 选中节点时&#xff0c;给选中的节点赋值 pathLabel&#xff0c;pathLabel 为函数生成的节点名字拼接&#xff0c;数据源中不包含。 在el-tree-select组件中设置 props“{ label: ‘pathLabel’ }” 控制选中时input框中回…

如何使用ssm实现企业人事管理系统+vue

TOC ssm628企业人事管理系统vue 研究背景 自计算机发展以来给人们的生活带来了改变。第一代计算机为1946年美国设计&#xff0c;最开始用于复杂的科学计算&#xff0c;占地面积、开机时间要求都非常高&#xff0c;经过数十几的改变计算机技术才发展到今天。现如今已是电子时…

Holynix: v1

确认物理地址 00:0C:29:BC:05:DE ip扫描 arp-scan -l 端口扫描 nmap 192.168.48.167 访问一下80端口 burp抓包 找到一个登录框 想着burp抓包试试 将抓到的包放入kali中的文件中使用sqlmap注入试试 sqlmap 存在sql注入 sqlmap -r password --batch --random-agent 发现…

卷积神经网络经典模型架构简介

【图书推荐】《PyTorch深度学习与企业级项目实战》-CSDN博客 《PyTorch深度学习与企业级项目实战&#xff08;人工智能技术丛书&#xff09;》(宋立桓&#xff0c;宋立林)【摘要 书评 试读】- 京东图书 (jd.com) ImageNet是一个包含超过1 500万幅手工标记的高分辨率图像的数据…

CAS 和 synchronized 的优化过程

&#x1f349; 目录 CAS 的实现 CAS 的工作原理 优化过程 CAS 的应用 1) 实现原子类 2&#xff09;实现自旋锁 CAS 的 ABA 问题 synchronized 的 原理 synchronized 基本特点 加锁工作过程 其他优化操作 1. 锁消除 2. 锁粗化 CAS&#xff08;Compare-And-Swap&…

2024ICPC网络赛第一场

A 最终答案与中国队能力值的排名有关&#xff0c;具体每个情况手推一下&#xff0c;用 if else 即可通过。 #include <bits/stdc.h> using namespace std;int main() {ios::sync_with_stdio(false); cin.tie(0);int t, a[40];cin >> t;while (t--) {int num 0;f…

Arduino IDE离线配置第三方库文件-ESP32开发板

简洁版可以使用uget等&#xff0c;将文件下载到对应文件夹下&#xff0c;然后安装。 esp32之arduino配置下载提速 录屏 Arduino IDE离线配置第三方库文件ESP32 资源 Linux https://download.csdn.net/download/ZhangRelay/89749063 第三方开发板 非默认支持的开发板 linu…

Ubuntu24.04部署docker

1、更新软件 apt update 2、安装curl apt install apt-transport-https curl 3、导入阿里云GPG秘钥 curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg 4、添加Docker阿里云仓库到Ubuntu 24.04的…

Python编码系列—Python适配器模式:无缝集成的桥梁

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

LLM - 理解 多模态大语言模型 (MLLM) 的指令微调与相关技术 (四)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/142063880 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 完备(F…

如何看待IBM中国研发部裁员

如何看待IBM中国研发部裁员&#xff1f; 近日&#xff0c;IBM中国宣布撤出在华两大研发中心&#xff0c;引发了IT行业对于跨国公司在华研发战略的广泛讨论。这一决定不仅影响了众多IT从业者的职业发展&#xff0c;也让人思考全球化背景下中国IT产业的竞争力和未来发展方向。面对…