【Linux】进程通信篇Ⅱ:共享内存、消息队列、信号量

文章目录

  • 一、共享内存
    • 1.1 一些接口
      • 1. shmget 函数:申请一个 system v 的共享内存块
      • 2. ftok 函数:设置唯一标识码
      • 3. shmctl 函数:控制 system v 的共享内存块(可以删除、查看...)
      • 4. shmat 函数:将进程与共享内存块 关联\ 挂接(attach)
      • 5. shmdt 函数:将进程与共享内存块 去关联(detach)
    • 1.2 一些命令
      • 1. ipcs - - 查看三种 ipc 资源
      • 2. ipcrm -- 删除某种 ipc 资源
    • 1.3 结论
  • 二、消息队列
      • 1. msgget 函数:创建消息队列
      • 2. msgctl 函数
      • 3. msgsnd 和 msgrcv 函数,发送和接收消息
  • 三、信号量
      • 1. semget
      • 2. semctl 函数:可以获取信号量的相关属性
      • 3. semop 函数:对信号量进行操作
  • 总结:


一、共享内存

我们知道,进程间通信的本质就是:让不同的进程,看到同一份资源

这里要介绍的同一份资源就是:内存块,即 共享内存(shared memory,简写为 shm)

共享内存的原理:
1.创建(key 和 共享内存)
2.关联进程 和 取消关联
3.释放共享内存

内存中的每块共享内存,会有一个 struct shm 结构体,里面放着共享内存的全部属性,OS 通过这个结构体建立链表关系来对所有的共享内存进行管理,就等于把管理 shm 的问题转化成了管理链表的问题。

故:

共享内存
=
共享内存的内核数据结构 (伪代码:struct shm)
+
真正开辟的内存空间

1.1 一些接口

1. shmget 函数:申请一个 system v 的共享内存块

头文件:

#include <sys/ipc.h>
#include <sys/shm.h>// umask的头文件如下
#include <sys/types.h>
#include <sys/stat.h>

int shmget(key_t key, size_t size, int shmflg);

参数 key:

  • 使用 ftok 函数设置的唯一标识码,他虽由用户设置,却是在内核中使用的


参数 size

  • 需要申请共享内存块的大小,单位为字节,不足 PAGE 页(4KB)时,会向上对齐到 PAGE 页


参数 shmflg:

  • 选项 IPC_CREAT and IPC_EXCL

  • 单独使用 IPC_CREAT:创建一个共享内存,如果共享内存不存在,就创建,如果已经存在就获取已经存在的共享内存并返回。

  • IPC_CREAT | IPC_EXCL :IPC_EXCL 必须要配合 IPC_CREAT 使用,创建一个共享内存,如果共享内存不存在,就创建,如果已经存在就出错返回
    意味着,一起使用时,如果创建成功,对应的shm,一定是最新的!

  • IPC_CREAT | IPC_EXCL | 0666 :上面的基础上,添加权限(可以配合函数 umask(0) 使用)


返回值:

  • 成功会返回一个共享内存标识符,失败返回 -1

2. ftok 函数:设置唯一标识码

头文件

#include <sys/types.h>
#include <sys/ipc.h>

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

参数 pathname

  • 用户设置的路径


参数 proj_id

  • 用户设置的项目 id


返回值:

  • 根据用户传入的参数,结合一定的算法,返回一个冲突概率很低的值。ket_t 就是一个 32 位的整数,是对 int 的封装

3. shmctl 函数:控制 system v 的共享内存块(可以删除、查看…)

头文件

#include <sys/ipc.h>
#include <sys/shm.h>   

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数 shmid

  • 需要的共享内存块的 shmid


参数 cmd:

  • 选项 IPC_STAT:把用户传入 shmid 的相应内核数据结构信息复制到 buf 中(在调用者有读权限的情况下,才能成功
  • 选项 IPC_RMID:删除 shmid 为传入值的共享内存块


输出型参数 buf:

  • 需要得到 ipc 信息时传一个相应类型的值用来接收结果


返回值:

  • 失败返回 -1,成功则根据 cmd 传入的选项返回对应的值
//The buf argument is a pointer to a shmid_ds structure, defined in <sys/shm.h> as follows:struct shmid_ds {struct ipc_perm shm_perm;    /* Ownership and permissions */size_t          shm_segsz;   /* Size of segment (bytes) */time_t          shm_atime;   /* Last attach time */time_t          shm_dtime;   /* Last detach time */time_t          shm_ctime;   /* Last change time */pid_t           shm_cpid;    /* PID of creator */pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */shmatt_t        shm_nattch;  /* No. of current attaches */...};//The ipc_perm structure is defined as follows (the highlighted fields  are  settable  using IPC_SET):struct ipc_perm {key_t          __key;    /* Key supplied to shmget(2) */uid_t          uid;      /* Effective UID of owner */gid_t          gid;      /* Effective GID of owner */uid_t          cuid;     /* Effective UID of creator */gid_t          cgid;     /* Effective GID of creator */unsigned short mode;     /* Permissions + SHM_DEST andSHM_LOCKED flags */unsigned short __seq;    /* Sequence number */};

4. shmat 函数:将进程与共享内存块 关联\ 挂接(attach)

头文件

#include <sys/types.h>
#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg);

参数 shmid

  • 需要的共享内存块的 shmid


参数 shmaddr:

  • 用户可以选择虚拟地址作为共享内存块的起始地址
  • 用户一般不定义,设为 nullptr 让 OS 自主定义即可


参数 shmflg:

  • 选项 SHM_RDONLY:只读
  • 0:可以读写


返回值:

  • 挂接成功,返回共享内存块的虚拟地址的起始地址

5. shmdt 函数:将进程与共享内存块 去关联(detach)

头文件

#include <sys/types.h>
#include <sys/shm.h>

int shmdt(const void *shmaddr);

参数 shmaddr:

  • 共享内存块的起始地址


返回值:

  • 去关联成功返回 0,失败返回 -1

1.2 一些命令

1. ipcs - - 查看三种 ipc 资源

ipcs 就是进程间通信(ipc)资源

ipcs:可以查看 消息队列、共享内存亏块、信号量
-m:查看 共享内存块(memory)
-s:查看 信号量(semaphore)

perms:权限
nattach:当前 ipc 挂接的进程数

2. ipcrm – 删除某种 ipc 资源

ipcrm:删除一个 消息队列、共享内存亏块、信号量
-m:删除一个共享内存块,后接 shmid

1.3 结论

  1. 两个进程管道通信一次,需要进行两次复制。而共享内存间的通信,可以让进程们直接在自己映射的地址空间中访问,减少了拷贝次数()

  2. 管道单方面关闭读写端会有相应的保护,而共享内存没有保护机制(同步互斥)。管道通过系统接口通信,共享内存直接通信

  3. 互斥:任何一个时刻,都只允许一个执行流在进行共享资源的访问,叫做加锁

  4. 我们把任何一个时刻,都只允许一个执行流在进行访问的共享资源,叫做 临界资源。凡是访问临界资源的代码,叫做临界区,控制进出临界区的手段造就了临界资源。



二、消息队列

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

头文件

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg)

参数 key:

  • 使用 ftok 函数设置的唯一标识码


参数 msgflg:

  • 选项 IPC_CREAT and IPC_EXCL

  • 单独使用 IPC_CREAT:创建一个共享内存,如果共享内存不存在,就创建,如果已经存在就获取已经存在的共享内存并返回。

  • IPC_CREAT | IPC_EXCL :IPC_EXCL 必须要配合 IPC_CREAT 使用,创建一个共享内存,如果共享内存不存在,就创建,如果已经存在就出错返回
    意味着,一起使用时,如果创建成功,对应的shm,一定是最新的!

  • IPC_CREAT | IPC_EXCL | 0666 :上面的基础上,添加权限(可以配合函数 umask(0) 使用)


返回值:

  • 成功则返回消息队列的标识符

2. msgctl 函数

头文件

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

参数 msqid

  • 需要的消息队列的 msqid


参数 cmd:

  • 选项 IPC_STAT:把用户传入 msqid 的相应内核数据结构信息复制到 buf 中(在调用者有读权限的情况下,才能成功
  • 选项 IPC_RMID:删除 msqid 为传入值的共享内存块


输出型参数 buf:

  • 需要得到 ipc 信息时传一个相应类型的值用来接收结果


返回值:

  • 成功返回 >= 0 的值,失败返回 -1
The msqid_ds data structure is defined in <sys/msg.h> as follows:struct msqid_ds {struct ipc_perm msg_perm;     /* Ownership and permissions */time_t          msg_stime;    /* Time of last msgsnd(2) */time_t          msg_rtime;    /* Time of last msgrcv(2) */time_t          msg_ctime;    /* Time of last change */unsigned long   __msg_cbytes; /* Current number of bytes inqueue (nonstandard) */msgqnum_t       msg_qnum;     /* Current number of messagesin queue */msglen_t        msg_qbytes;   /* Maximum number of bytesallowed in queue */pid_t           msg_lspid;    /* PID of last msgsnd(2) */pid_t           msg_lrpid;    /* PID of last msgrcv(2) */};The ipc_perm structure is defined as follows (the highlighted fields  are  settable  using
IPC_SET):struct ipc_perm {key_t          __key;       /* Key supplied to msgget(2) */uid_t          uid;         /* Effective UID of owner */gid_t          gid;         /* Effective GID of owner */uid_t          cuid;        /* Effective UID of creator */gid_t          cgid;        /* Effective GID of creator */unsigned short mode;        /* Permissions */unsigned short __seq;       /* Sequence number */};

3. msgsnd 和 msgrcv 函数,发送和接收消息

头文件

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

参数 msqid:

  • 发送和接收访问的同一个消息队列

参数 msgp:

  • 发送或接收的数据块

参数 msgsz:

  • 发送或接收数据块的大小

参数 msgflg:

  • 选项,一般填 0 即可

参数 msgtyp:

  • msgbuf 里面的 mtype
// The msgp argument is a pointer to caller-defined structure of the following general form:struct msgbuf {long mtype;       /* message type, must be > 0 */char mtext[1];    /* message data */
};


三、信号量

信号量 / 信号灯(semaphore),本质 就是一个计数器,是一个描述资源数量的计数器

举个例子:

  • 比如我们任何一个执行流,像访问临界资源中的一个子资源的时候,不能直接访问,需要 先申请信号量资源(P操作),此时 count-- 。只要申请信号量成功,未来就一定能拿到一个子资源。(类似摇号)

  • 然后进入进程自己的临界区,访问对应的临界资源。

  • 使用完成后,进程释放信号量资源(V操作),只要将计数器增加 count++,就表示将我们对应的资源进行了归还。

至此,进程通过执行代码来申请,意味着,所有进程都得先看到信号量,信号量就是一个共享资源。(信号量保护共享资源,自己却又是一个共享资源)

故,信号量必须保证自己的 ++ - - 是原子的
也,信号量被归类到了进程间通信

信号量部分未完待续~

1. semget

头文件

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);

参数 key:

  • 使用 ftok 函数设置的唯一标识码,他虽由用户设置,却是在内核中使用的


参数 nsems:

  • 申请信号量的个数(叫做信号量集)


参数 semflg:

  • 选项 IPC_CREAT and IPC_EXCL

  • 单独使用 IPC_CREAT:创建一个共享内存,如果共享内存不存在,就创建,如果已经存在就获取已经存在的共享内存并返回。

  • IPC_CREAT | IPC_EXCL :IPC_EXCL 必须要配合 IPC_CREAT 使用,创建一个共享内存,如果共享内存不存在,就创建,如果已经存在就出错返回
    意味着,一起使用时,如果创建成功,对应的shm,一定是最新的!

  • IPC_CREAT | IPC_EXCL | 0666 :上面的基础上,添加权限(可以配合函数 umask(0) 使用)


返回值:

  • 成功会返回一个信号量计数器标识符,失败返回 -1

2. semctl 函数:可以获取信号量的相关属性

头文件

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semctl(int semid, int semnum, int cmd, ...);

参数 semid:

  • 需要的信号量的semid


参数 semnum:

  • 信号量编号


参数 cmd:

  • 选项 IPC_STAT:把用户传入 msqid 的相应内核数据结构信息复制到 buf 中(在调用者有读权限的情况下,才能成功
  • 选项 IPC_RMID:删除 msqid 为传入值的共享内存块


返回值:

  • 成功返回 >= 0 的值,失败返回 -1
//This  function  has  three  or four arguments, depending on cmd.  When there are four, the
//fourth has the type union semun.  The calling program must define this union as follows:union semun {int              val;    /* Value for SETVAL */struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */unsigned short  *array;  /* Array for GETALL, SETALL */struct seminfo  *__buf;  /* Buffer for IPC_INFO(Linux-specific) */};//The semid_ds data structure is defined in <sys/sem.h> as follows:struct semid_ds {struct ipc_perm sem_perm;  /* Ownership and permissions */time_t          sem_otime; /* Last semop time */time_t          sem_ctime; /* Last change time */unsigned long   sem_nsems; /* No. of semaphores in set */};//The ipc_perm structure is defined as follows (the highlighted fields  are  settable  usingIPC_SET):struct ipc_perm {key_t          __key; /* Key supplied to semget(2) */uid_t          uid;   /* Effective UID of owner */gid_t          gid;   /* Effective GID of owner */uid_t          cuid;  /* Effective UID of creator */gid_t          cgid;  /* Effective GID of creator */unsigned short mode;  /* Permissions */unsigned short __seq; /* Sequence number */};

3. semop 函数:对信号量进行操作

头文件

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semop(int semid, struct sembuf *sops, unsigned nsops);

参数 sops:

  • 需要用户自己定义后传入,设置结构体内容,以此达到对信号量的 PV 操作等等
//Each semaphore in a System V semaphore set has the following associated values:unsigned short  semval;   /* semaphore value */unsigned short  semzcnt;  /* # waiting for zero */unsigned short  semncnt;  /* # waiting for increase */pid_t           sempid;   /* ID of process that did last op *///semop() performs operations on selected semaphores in the set indicated by semid.  
//Each of the nsops elements in the array pointed to by sops specifies an operation to be  performed on a single semaphore.  
//The elements of this structure are of type struct sembuf, containing the following members:unsigned short sem_num;  /* semaphore number */short          sem_op;   /* semaphore operation */short          sem_flg;  /* operation flags */

总结:

共享内存、消息队列、信号量 这三种 ipc 都有各自的内核数据结构体,而结构体的第一个成员都是 struct ipc_perm xx_perm

可以理解为 OS 将这三种 ipc 都放进了一个 struct ipc_perm* ipc_id_arr[] 指针数组中进行管理(这里只是做理解解释,实际上更复杂)。

OS 通过指针,可以找到每个结构体(同时也是每个结构的第一个成员,即 struct ipc_perm xx_perm),在其中找到 key 值就可以确定。

要访问里面的内容时,以共享内存举例

((struct shmid_ds*)ipc_id_arr[n])->other...

对指针进行强转,就可以访问到其中内容了,这也是一种多态。

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

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

相关文章

一种多策略下RabbitMQ的延时队列实现

1.为什么会用到延时队列? 场景: 最近在开发一款系统中遇到这样一个场景,A系统开通套餐需要把套餐信息以邮件的形式发送给相关工作人员,经过人工审核通过后,在B系统里面开通,A系统会调B系统套餐列表接口查询套餐是否开通成功,开通成功则从A系统去完成订单,假如超过设定时间未开…

韦东山-电子量产工具项目:显示单元

所有代码都已通过测试跑通&#xff0c;其中代码结构如下&#xff1a; 一、include文件夹 1.1 disp_manager.h #ifndef _DISP_MANAGER_H //防止头文件重复包含,只要右边的出现过&#xff0c;就不会再往下编译 #define _DISP_MANAGER_H //区域结构体 typedef struct DispBuff …

[element-ui] el-table表格合并 span-method

用rowIndex, columnIndex 找到要合并的开始单元格 return {rowspan: 1,colspan: 1 } 表示表格不变 return {rowspan: 2,colspan: 1 } 表示表格向下合并一个单元格 return {rowspan: 1,colspan: 2 } 表示表格向右合并一个单元格 return {rowspan: 0,colspan: 0 } 表示删除此单元…

leetcode810. 黑板异或游戏(博弈论 - java)

黑板异或游戏 lc 810 - 黑板异或游戏题目描述博弈论 动态规划 lc 810 - 黑板异或游戏 难度 - 困难 原题链接 - 黑板异或游戏 题目描述 黑板上写着一个非负整数数组 nums[i] 。 Alice 和 Bob 轮流从黑板上擦掉一个数字&#xff0c;Alice 先手。如果擦除一个数字后&#xff0c;剩…

谈谈网络协议的定义、组成和重要性

个人主页&#xff1a;insist--个人主页​​​​​​ 本文专栏&#xff1a;网络基础——带你走进网络世界 本专栏会持续更新网络基础知识&#xff0c;希望大家多多支持&#xff0c;让我们一起探索这个神奇而广阔的网络世界。 目录 一、网络协议的定义 二、网络协议的组成 1、…

出于网络安全考虑,印度启用本土操作系统”玛雅“取代Windows

据《印度教徒报》报道&#xff0c;印度将放弃微软系统&#xff0c;选择新的操作系统和端点检测与保护系统。 备受期待的 "玛雅操作系统 "将很快用于印度国防部的数字领域&#xff0c;而新的端点检测和保护系统 "Chakravyuh "也将一起面世。 不过&#xf…

C++--类型转换

1.什么是类型转换 在传统C语言中&#xff0c;由强制类型转换和隐式类型转换&#xff0c;隐式类型转换&#xff0c;编译器在在编译阶段自动处理&#xff0c;能转换则转换&#xff0c;强制类型转换由用户自己转换。 缺陷&#xff1a; 转换的可视性比较差&#xff0c;所有的转换形…

Go语言中关键字type的多重应用场景详解

当谈及Go语言中的关键字type时&#xff0c;我们通常会想到用于定义结构体和接口的常见用法。然而&#xff0c;"type"关键字实际上有许多其他用法&#xff0c;本文将对其中几种常见用法进行简要总结记录。 定义结构体和方法 在Go中&#xff0c;我们可以使用type来定…

运维监控学习笔记5

Linux的内存是虚拟内存&#xff0c;是物理内存和交换分区swap。 内存&#xff1a; 页&#xff1a;4K&#xff0c; 硬盘&#xff1a;块。 寻址&#xff1a; 空间&#xff1a;内存的合并。大页内存。 free命令&#xff1a; [rootvm1 ~]# free -htotal used fre…

javap获取Kotlin方法JNI方法签名

获取Kotlin方法签名和JAVA不一样的地方就是需要使用Kotlin 命令行编译器生成.class文件&#xff1a; 编写一个Kotlin类&#xff0c;添加JNI方法&#xff1a; class TestLib {external fun init(callBack: CallBack)interface CallBack{fun onData(count:Int,data:String)} }在…

cesium学习记录08-鼠标绘制多边形

上一篇学习了实体的一些基础知识&#xff0c;这一篇来学习鼠标绘制实体多边形的实现 一、方法一&#xff1a; 1&#xff0c;结果显示 贴地&#xff1a; 不贴地&#xff1a; 2&#xff0c;方法全部代码&#xff1a; 主方法&#xff1a; /*** 绘制多边形* param {Object} op…

华为OD机试 - 公共子串计算(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#xff09;》。 刷的越多&…

VictoriaMetrics部署及vmalert集成钉钉告警

1、部署VictoriaMetrics cd /usr/local wget https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v1.65.0/victoria-metrics-amd64-v1.65.0.tar.gz mkdir victoria-metrics && tar -xvzf victoria-metrics-amd64-v1.65.0.tar.gz && \ mv …

论AI GPT跨境贸易架构及其应用

摘要 2023年初,我司启动了智慧化跨境贸易供应链一体化平台的建设工作。我在该项目中担任系统架构设计师的职务,主要负责设计平台系统架构和安全体系架构。该平台以移动信息化发展为契机,采用”平台+AI”的模式解决现有应用的集中移动化需求。平台整体的逻辑复杂,对系统的高…

react之Hooks的介绍、useState与useEffect副作用的使用

react之Hooks的介绍、useState与useEffect副作用的使用 一、Hooks的基本介绍二、useState的使用2.1 简单使用2.2 数组结构简化2.3 状态的读取和修改2.3 组件的更新过程 三、useEffect的使用3.1 副作用介绍3.2 基本使用3.3 依赖3.4 不要对依赖项撒谎3.5 依赖项可以是空数组3.6 清…

ZZULIOJ 1193: 单科成绩排序(结构体专题),Java

ZZULIOJ 1193: 单科成绩排序&#xff08;结构体专题&#xff09;&#xff0c;Java 题目描述 有一学生成绩表&#xff0c;包括学号、姓名、3门课程成绩。请按要求排序输出&#xff1a;若输入1&#xff0c;则按第1门课成绩降序输出成绩表&#xff0c;若输入为i&#xff08;1<…

清风数学建模——拟合算法

拟合算法 文章目录 拟合算法概念 确定拟合曲线最小二乘法的几何解释求解最小二乘法matlab求解最小二乘法如何评价拟合的好坏计算拟合优度的代码 概念 在前面的篇幅中提到可以使用插值算法&#xff0c;通过给定的样本点推算出一定的曲线从而推算出一些想要的值。但存在一些问题…

解决内网GitLab 社区版 15.11.13项目拉取失败

问题描述 GitLab 社区版 发布不久&#xff0c;搭建在内网拉取项目报错&#xff0c;可能提示 unable to access https://github.comxxxxxxxxxxx: Failed to connect to xxxxxxxxxxxxxGit clone error - Invalid argument error:14077438:SSL routines:SSL23_GET_S 15.11.13ht…

QT网络编程之TCP

QT网络编程之TCP TCP 编程需要用到俩个类: QTcpServer 和 QTcpSocket。 #------------------------------------------------- # # Project created by QtCreator 2023-08-

mysql截取最后一个字符之前的数据

1、mysql截取最后一个字符之前的数据 select --截取斜杠之前的数据REVERSE(SUBSTR(REVERSE(SPNH-dfg-2012) ; --截取斜杠后的数据 INSTR(REVERSE(SPNH-fg-2012),-)1))2、mysql获取最后一个字符后的数据 select SUBSTRING_INDEX(SPNH-dfg-2012,-,-1) 3、mysql更新某个字段…