【Linux】Linux进程间通信(四)

在这里插入图片描述

​📝个人主页:@Sherry的成长之路
🏠学习社区:Sherry的成长之路(个人社区)
📖专栏链接:Linux
🎯长路漫漫浩浩,万事皆有期待

上一篇博客:【Linux】Linux进程间通信(三)

文章目录

  • system V进程间通信
    • System V消息队列
      • 消息队列的基本原理
      • 消息队列数据结构
      • 消息队列的创建
      • 消息队列的释放
      • 向消息队列发送数据
      • 从消息队列获取数据
    • System V信号量
      • 信号量相关概念
      • 信号量数据结构
      • 信号量相关函数
      • 进程互斥
    • system V IPC联系
  • 总结:

system V进程间通信

System V消息队列

消息队列的基本原理

消息队列实际上就是在系统当中创建了一个队列,队列当中的每个成员都是一个数据块,这些数据块都由类型和信息两部分构成,两个互相通信的进程通过某种方式看到同一个消息队列,这两个进程向对方发数据时,都在消息队列的队尾添加数据块,这两个进程获取数据块时,都在消息队列的队头取数据块。在这里插入图片描述

其中消息队列当中的某一个数据块是由谁发送给谁的,取决于数据块的类型。

总结一下:

消息队列提供了一个从一个进程向另一个进程发送数据块的方法。
每个数据块都被认为是有一个类型的,接收者进程接收的数据块可以有不同的类型值。
和共享内存一样,消息队列的资源也必须自行删除,否则不会自动清除,因为system V IPC资源的生命周期是随内核的。

消息队列数据结构

当然,系统当中也可能会存在大量的消息队列,系统一定也要为消息队列维护相关的内核数据结构。

消息队列的数据结构如下:

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 */
};

可以看到消息队列数据结构的第一个成员是msg_perm,它和shm_perm是同一个类型的结构体变量,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;
};

记录一下:

共享内存的数据结构msqid_ds和ipc_perm结构体分别在/usr/include/linux/msg.h和/usr/include/linux/ipc.h中定义。

消息队列的创建

创建消息队列我们需要用msgget函数,msgget函数的函数原型如下:

int msgget(key_t key, int msgflg);

说明一下:

创建消息队列也需要使用ftok函数生成一个key值,这个key值作为msgget函数的第一个参数。
msgget函数的第二个参数,与创建共享内存时使用的shmget函数的第三个参数相同。
消息队列创建成功时,msgget函数返回的一个有效的消息队列标识符(用户层标识符)

消息队列的释放

释放消息队列我们需要用msgctl函数,msgctl函数的函数原型如下:

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

说明一下:
msgctl函数的参数与释放共享内存时使用的shmctl函数的三个参数相同,只不过msgctl函数的第三个参数传入的是消息队列的相关数据结构。

向消息队列发送数据

向消息队列发送数据我们需要用msgsnd函数,msgsnd函数的函数原型如下:

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

msgsnd函数的参数说明:

第一个参数msqid,表示消息队列的用户级标识符。
第二个参数msgp,表示待发送的数据块。
第三个参数msgsz,表示所发送数据块的大小
第四个参数msgflg,表示发送数据块的方式,一般默认为0即可。

msgsnd函数的返回值说明:
msgsnd调用成功,返回0。
msgsnd调用失败,返回-1。

其中msgsnd函数的第二个参数必须为以下结构:

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

注意: 该结构当中的第二个成员mtext即为待发送的信息,当我们定义该结构时,mtext的大小可以自己指定。

从消息队列获取数据

从消息队列获取数据我们需要用msgrcv函数,msgrcv函数的函数原型如下:

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

msgrcv函数的参数说明:

第一个参数msqid,表示消息队列的用户级标识符。
第二个参数msgp,表示获取到的数据块,是一个输出型参数。
第三个参数msgsz,表示要获取数据块的大小
第四个参数msgtyp,表示要接收数据块的类型。

msgrcv函数的返回值说明:
msgsnd调用成功,返回实际获取到mtext数组中的字节数。
msgsnd调用失败,返回-1。

System V信号量

信号量相关概念

由于进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系叫做进程互斥。
系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。
在进程中涉及到临界资源的程序段叫临界区。
IPC资源必须删除,否则不会自动删除,因为system V IPC的生命周期随内核。

信号量数据结构

在系统当中也为信号量维护了相关的内核数据结构。

信号量的数据结构如下:

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类型的结构体变量,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;
};

记录一下:
共享内存的数据结构msqid_ds和ipc_perm结构体分别在/usr/include/linux/sem.h和/usr/include/linux/ipc.h中定义。

信号量相关函数

信号量集的创建

创建信号量集我们需要用semget函数,semget函数的函数原型如下:

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

说明一下:

创建信号量集也需要使用ftok函数生成一个key值,这个key值作为semget函数的第一个参数。
semget函数的第二个参数nsems,表示创建信号量的个数。
semget函数的第三个参数,与创建共享内存时使用的shmget函数的第三个参数相同。
信号量集创建成功时,semget函数返回的一个有效的信号量集标识符(用户层标识符)

信号量集的删除

删除信号量集我们需要用semctl函数,semctl函数的函数原型如下:

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

信号量集的操作

对信号量集进行操作我们需要用semop函数,semop函数的函数原型如下:

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

进程互斥

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

保护临界资源的本质是保护临界区,我们把进程代码中访问临界资源的代码称之为临界区,信号量就是用来保护临界区的,信号量分为二元信号量和多元信号量。

比如当前有一块大小为100字节的资源,我们若是一25字节为一份,那么该资源可以被分为4份,那么此时这块资源可以由4个信号量进行标识。
在这里插入图片描述

信号量本质是一个计数器,在二元信号量中,信号量的个数为1(相当于将临界资源看成一整块),二元信号量本质解决了临界资源的互斥问题,以下面的伪代码进行解释:

在这里插入图片描述

根据以上代码,当进程A申请访问共享内存资源时,如果此时sem为1(sem代表当前信号量个数),则进程A申请资源成功,此时需要将sem减减,然后进程A就可以对共享内存进行一系列操作,但是在进程A在访问共享内存时,若是进程B申请访问该共享内存资源,此时sem就为0了,那么这时进程B会被挂起,直到进程A访问共享内存结束后将sem加加,此时才会将进程B唤起,然后进程B再对该共享内存进行访问操作。

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

实际上,代码中计数器sem减减的操作就叫做P操作,而计数器加加的操作就叫做V操作,P操作就是申请信号量,而V操作就是释放信号量。
在这里插入图片描述

system V IPC联系

通过对system V系列进程间通信的学习,可以发现共享内存、消息队列以及信号量,虽然它们内部的属性差别很大,但是维护它们的数据结构的第一个成员确实一样的,都是ipc_perm类型的成员变量。

这样设计的好处就是,在操作系统内可以定义一个struct ipc_perm类型的数组,此时每当我们申请一个IPC资源,就在该数组当中开辟一个这样的结构。
在这里插入图片描述

也就是说,在内核当中只需要将所有的IPC资源的ipc_perm成员组织成数组的样子,然后用切片的方式获取到该IPC资源的起始地址,然后就可以访问该IPC资源的每一个成员了。

总结:

今天我们继续学习了Linux进程间通信的相关知识,了解了system V进程间通信等 。接下来,我们将继续学习Linux的其他知识。希望我的文章和讲解能对大家的学习提供一些帮助。

当然,本文仍有许多不足之处,欢迎各位小伙伴们随时私信交流、批评指正!我们下期见~

在这里插入图片描述

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

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

相关文章

基于嵌入式的智能智能通风系统

基于嵌入式的智能智能通风系统 功能说明 通过微信小程序控制窗户的开关状体以及倒计时开关和定时开关,小程序上实时显示当前温度湿度和光照强度。 功能展示 02智能通风系统 Mqtt服务器 http://www.yoyolife.fun/iot:Mqtt服务器,我是在这里注…

前端react入门day03-react获取dom与组件通信

(创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹) 目录 受控表单绑定 React中获取DOM 组件通信 父传子 父传子-基础实现 父传子-props说明 父传子 - 特殊的…

J4 - ResNet与DenseNet结合

🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 | 接辅导、项目定制 目录 环境模型设计模型效果展示总结与心得体会 环境 系统: Linux语言: Python3.8.10深度学习框架: Pytorch2.0.0cu118显卡:GT…

构建坚固网络,SD-WAN网络配置指南

在新兴的网络技术中,软件定义广域网(SD-WAN)作为关键角色脱颖而出,被广泛应用于构建高效可靠的企业网络。利用SD-WAN技术,企业能够更好地管理和优化其网络基础设施,实现更高效的连接和数据传输。本文将深入…

STM32标准库开发——串口发送/单字节接收

USART基本结构 串口发送信息 启动串口一的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);初始化对应串口一的时钟,引脚,将TX引脚设置为复用推挽输出。 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef GPIO_In…

BigeMap在Unity3d中的应用,助力数字孪生

1. 首先需要用到3个软件,unity,gis office 和 bigemap离线服务器 Unity下载地址:点击前往下载页面(Unity需要 Unity 2021.3.2f1之后的版本) Gis office下载地址:点击前往下载页面 Bigemap离线服务器 下载地址: 点击前往下载页面 Unity用于数字孪生项…

比吸收率(SAR)

本文旨在介绍比吸收率(Specific Absorption Rate)的基本知识。搬运自https://www.antenna-theory.com。英语够用的朋友可以直接移步。感谢网站创始人Peter Joseph Bevelacqua教授的无私奉献。 ------------------我是分隔线------------------- 比吸收…

【征服redis6】Redis的内存淘汰详解

目录 1.redis的基本策略 2.Redis中的缓存淘汰策略 3.Redis内存不足的情况 4.几种淘汰策略的实现原理 5.项目实践与优化策略 5.1 配置案例 5.2 项目优化策略参考 数据库存储会将数据保存到磁盘中,而Redis的核心数据是在内存中的,而Redis本身主要用来…

【Java练习题汇总】《第一行代码JAVA》多线程篇,汇总Java练习题——线程及多线程概念、Thread 类及 Runnable 接口、线程状态、synchronized同步操作...

Java练习题 多线程篇 1️⃣ 多线程篇2️⃣ 答案 1️⃣ 多线程篇 一、填空题 Java 多线程可以依靠________ 、________ 和________ 三种方式实现。多个线程操作同一资源的时候需要注意________,依靠________ 关键字实现,实现手段是:_______…

「 网络安全常用术语解读 」杀链Kill Chain详解

1. 简介 早在2009年,Lockheed Martin公司就提出了杀链(Kill Chain)理论,现在也称之为攻击者杀链(Attacker Kill Chain)。杀链其实就是攻击者进行网络攻击时所采取的步骤。杀链模型包括7个步骤:1侦察 -> 2武器化 -> 3交付 -> 4利用 …

C语言——整数和浮点数在内存中的存储

目录 一、整数在内存中的存储 二、大小端字节序和字节序判断 2.1 什么是大小端? 2.2 为什么有大小端? 2.3 练习 2.3.1 练习1 2.3.2 练习2 三、浮点数在内存中的存储 3.1练习 3.2 浮点数的存储 3.2.1浮点数存的过程 3.2.2浮点数取的过程 3.3 题目解…

消息中间件之Kafka(一)

1.简介 高性能的消息中间件,在大数据的业务场景下性能比较好,kafka本身不维护消息位点,而是交由Consumer来维护,消息可以重复消费,并且内部使用了零拷贝技术,性能比较好 Broker持久化消息时采用了MMAP的技…