当前位置: 首页 > news >正文

【Linux】System V信号量与IPC资源管理简易讲解

本文承接上文:【Linux】System V共享内存实战:零拷贝加速进程通信!-CSDN博客

信号量基础概念与操作函数

信号量概念

信号量是一种用于进程间同步的机制,可以类比为一个计数器,用于控制多个进程或线程对共享资源的访问。System V信号量属于IPC(进程间通信)的一种,允许多个进程协调操作,防止竞态条件和数据不一致。

信号量操作函数

  1. semget函数:用于创建或获取一个信号量集,函数原型为int semget(key_t key, int nsems, int semflg);,其中key是信号量集的键值,nsems是要创建的信号量数目,semflg是权限标志。

  2. semop函数:用于对信号量集中的一个或多个信号量执行操作,函数原型为int semop(int semid, struct sembuf *sops, unsigned nsops);,其中semid是信号量集的标识符,sops是指向sembuf结构数组的指针,nsops是操作数目。

  3. semctl函数:用于控制信号量集,如获取或设置信号量值、删除信号量集等,函数原型为int semctl(int semid, int semnum, int cmd, ... /* union semctl_arg arg */ );,其中semid是信号量集的标识符,semnum是信号量编号,cmd是控制命令。

信号量的优缺点与最佳实践

优点

  • 原子操作semop函数可原子性地执行多个信号量操作,确保同步的可靠性。

  • 持久性:信号量在进程退出后仍存在,需显式删除,适用于需要长期存在的同步机制。

  • 灵活性:支持多个信号量组成信号量集,可同时控制多个资源的访问。

缺点

  • 复杂性:使用需要多个函数配合,且需处理多种错误情况,代码相对复杂。

  • 性能开销:每次操作都需要与内核交互,频繁操作可能导致性能下降。

最佳实践

  • 合理设置初始值:根据资源的可用数量初始化信号量值,如互斥访问时设为1。

  • 避免死锁:在多个信号量操作中,确保所有进程按相同顺序操作,防止死锁。

  • 及时删除信号量集:进程结束时显式删除不再需要的信号量集,避免资源泄漏。

  • 结合共享内存使用:利用信号量保护共享内存中的数据访问,实现高效的数据共享与同步。

IPC指令操作

  • ipcs指令:用于显示和管理IPC对象(包括信号量、共享内存和消息队列)。例如,ipcs -s显示所有信号量集,ipcs -s -i <semid>显示指定信号量集的详细信息。

  • ipcrm指令:用于删除System V IPC对象。例如,ipcrm -s <semid>删除指定的信号量集。

操作系统对IPC资源的统一管理

操作系统维护了一个可变长指针数组struct kern_ipc_pern* p[0],数组中的每个指针分别指向共享内存、消息队列、信号量的结构体中的第一个成员。这种设计实现了对IPC资源(共享内存、消息队列、信号量)的统一管理,通过指针数组的偏移访问不同类型的IPC资源,体现了C语言中的多态特性。

在操作系统中,通过遍历kern_ipc_pern* p[0]数组,检查每个结构体中的key值来避免IPC资源的冲突。同时,ipc_perm结构体中的mode字段用于区分不同的IPC资源类型。

先理解一些概念:

公共资源:多个执行流(进程),能看到的一份资源:共享资源

临界资源:被保护起来的资源---保护的方式:同步和互斥 --- 用互斥的方式保护共享资源

互斥: 任何时刻只能有一个进程在访问公共资源

资源:一定需要被程序员访问 --- 资源被访问,就是通过代码访问

代码 = 访问共享资源的代码 (临界区)+ 不访问共享资源的代码(非临界区)

所谓的对共享资源进行保护 这个共享资源变成临界资源,本质是对访问共享资源的代码(临界区)进行保护

信号量概念:

信号量,信号灯是用于保护共享资源的,共享资源经过保护变成临界资源。

信号量:本质是一个计数器。对临界资源进行预先申请的计数器。申请成功,那么公共资源当中就一定有一部分是这个进程的。

将一个整体的公共资源,不再整体使用,分成许多块,在一个进程每个小块被不同的多个进程进行访问,允许不同的多个执行流进来访问同一个共享资源的局部性资源。在公共资源的基础上保证一定的并发度。信号量的本质:就是在这种情况的时候如何对对这些小资源的管理

类比:

看电影买票的本质

对资源的预定机制,对于电影院的一场电影本来只有25个座位,但是却卖出了26张票,最担心超过资源个数的卖票。在票不够的时候就需要等待。

电影院:共享资源(临界资源)

买票:申请信号量

票数:信号量的初始值

申请信号量的本质就是对公共资源的一种预定机制

因此当需要访问共享内存的时候

1.申请信号量

2.访问共享内存

3.释放信号量

现在存在着一个超级VIP厅(公共资源),只有一个座位,当一个人买下之后(申请信号量),在看电影的时候,任何人就不能再购买购买这个厅的电影票(任何进程再无法申请信号量)

这就是互斥的概念,公共资源整体被使用,在我访问期间,其余进程无法访问这个资源,这里的信号量计数器只会有1,0--->这就是二元信号量。

信号量分为:

二元信号量:把整个资源当成一个整体,在一个进程申请成功并访问的时候,其他进程无法申请信号量。 ---> 互斥

多元信号量:一个整体的资源被分成很多分信号量计数器 > 1

是否能使用全局变量gcount = 25来表示信号量,不可以

1.全局变量不能被所有的进程看到

2.gcount++--,非原子

原子:

平时洗澡的时候,有洗澡前,洗澡中,洗澡后,三个状态 ---> 非原子性

在做一些事情,要么就不做,要做就做完,没有任何中间状态 --->原子性

对于int a =10,此时开辟空间,将空间里的值赋为10,这个动作在开辟空间时,就已经初始化为10—> 这个在汇编语句上实际上只需要一句,这就是原子性,而++,--,会存在多条,首先需要把全局变量mov到cpu,在cpu内部再进行inc 加1,再写回内存,因此不具有原子性。

对于IPC信号量,就和共享内存一样,需要让不同的进程看到同一个“计数器”。

这意味着,信号量也是一个公共资源,用于保护临界资源安全的,前提是先要保护自己的安全。

保证信号量的安全

申请信号量 对应                 --操作           保证信号量是安全的   因此被设置为原子操作 P操作

访问公共资源(共享内存)

释放信号量 对应                 ++操作         保证信号量是安全的   因此被设置为原子操作 V操作

举例:

现在的P = 1,1号进程对共享资源进行访问P = 0,2号进程来访问,由于P = 0,这个进程就需要等待,1号进程退出V = 1,P = 0,这个时候2号进程才能对资源进行访问

信号量操作函数

1. semget函数:创建或获取信号量集

semget函数用于创建一个新的信号量或获取已存在的信号量。在linux系统中,允许用户申请多个信号量,多个信号量就称之为信号量集,使用数组来维护的。

其函数原型为:

int semget(key_t key, int nsems, int semflg);
  • key:信号量集的键值,类似于共享内存的键值,用于唯一标识一个信号量集。可以通过ftok函数生成。

  • nsems:要创建的信号量数目。若获取已存在的信号量集,此参数应设为0。

  • semflg:权限标志,指定对信号量集的访问权限,如0666表示读写权限。若创建新信号量集,可使用IPC_CREAT标志。

2. semop函数:执行信号量操作-对信号量集中的一个或多个信号量执行操作

semop函数用于对信号量集中的一个或多个信号量执行操作,其函数原型为:

int semop(int semid, struct sembuf *sops, unsigned nsops);
  • semid:信号量集的标识符,由semget返回。

  • sops:指向sembuf结构数组的指针,每个结构定义一个操作。

  • nsops:操作数目,即sops数组的元素个数。

sembuf结构定义如下:

struct sembuf {
    unsigned short sem_num; // 信号量编号
    short sem_op;           // 操作值
    short sem_flg;          // 操作标志
};
  • sem_num:指定信号量集中的哪个信号量。

  • sem_op:操作类型,若为负值表示等待(P操作),若为正值表示发送(V操作),若为0表示等待信号量变为0。

  • sem_flg:操作标志,常用IPC_NOWAIT(若操作不能立即完成则返回错误)和SEM_UNDO(系统自动记录操作,进程异常终止时恢复信号量值)。

3. semctl函数:控制信号量集-获取或设置信号量值、删除信号量集

semctl函数用于对信号量集执行控制操作,如获取或设置信号量值、删除信号量集等。其函数原型为:

int semctl(int semid, int semnum, int cmd, ... /* union semctl_arg arg */ );
  • semid:信号量集的标识符。

  • semnum:信号量编号,指定操作针对的信号量。

  • cmd:控制命令,常见的有:

    • GETVAL:获取指定信号量的当前值。

    • SETVAL:设置指定信号量的值。

    • IPC_RMID:删除信号量集。

    • IPC_STAT:获取信号量集的状态信息。

  • arg:可选参数,根据cmd不同而不同。

优点

  1. 原子操作semop函数可原子性地执行多个信号量操作,确保同步的可靠性。

  2. 持久性:信号量在进程退出后仍存在,需显式删除,适用于需要长期存在的同步机制。

  3. 灵活性:支持多个信号量组成信号量集,可同时控制多个资源的访问。

缺点

  1. 复杂性:使用需要多个函数配合,且需处理多种错误情况,代码相对复杂。

  2. 性能开销:每次操作都需要与内核交互,频繁操作可能导致性能下降。

最佳实践

  1. 合理设置初始值:根据资源的可用数量初始化信号量值,如互斥访问时设为1。

  2. 避免死锁:在多个信号量操作中,确保所有进程按相同顺序操作,防止死锁。

  3. 及时删除信号量集:进程结束时显式删除不再需要的信号量集,避免资源泄漏。

  4. 结合共享内存使用:利用信号量保护共享内存中的数据访问,实现高效的数据共享与同步。

IPC指令操作:

ipcs:用于显示和管理 IPC对象(包括信号量、共享内存和消息队列)。

  • 显示信号量集

    ipcs -s

    该命令列出系统中所有的信号量集,显示其键值、信号量集ID、所有者等信息。

  • 显示指定信号量集的详细信息

    ipcs -s -i <semid>

    其中<semid>是信号量集的标识符,该命令显示信号量集的详细信息,如信号量数、权限、创建者等。

ipcrm:用于删除System V IPC对象。

  • 删除信号量集

    ipcrm -s <semid>

    该命令删除指定的信号量集,需确保有足够权限。

OS是如何统一管理共享内存(shm),消息队列(msg),信号量(sem)

设计了一套标准来管理。

1.System V

2.XXXget,XXXctl

3.XXXid_+ds,struct ipc_perm

操作系统维护了一个可变长指针数组:struct kern_ipc_pern* p[0],数组中的每一个指针分别指向共享内存、消息队列、信号量,结构体中的第一个成员,也就是存的他们的第一个成员的地址,这意味着我们把IPC资源:共享内存(shm),消息队列(msg),信号量(sem)的管理统一了,变成了对kern_ipc的管理。他们结构体中所存的第一个成员变量,例如shmid也就是struct kern_ipc_pern* p[0]数组的下标。

观察到key所在的位置,在操作系统中,他是怎么知道这个ipc资源和其他的资源是否冲突?

只需要遍历kern_ipc_pern* p[0]数组,检查每个结构体当中的kern_ipc_pern当中的key值是否冲突。想访问不同ipc资源结构体中的变量时,直接强转指针数组里的指针,就能访问到。这就是使用C语言实现的多态。

这个数组的指针怎么知道自己指向的是哪一种ipc资源,因此ipc_perm里一定会有字段,指向目标类型,这个字段叫做mode。具体实现可以参照:这篇博客当中的设计传递位图标记位的函数。【Linux】 基础IO之操作与文件描述符fd全解析:从C语言到系统调用底层实现_io导出0kb文件-CSDN博客

结语:

       随着这篇关于题目解析的博客接近尾声,我衷心希望我所分享的内容能为你带来一些启发和帮助。学习和理解的过程往往充满挑战,但正是这些挑战让我们不断成长和进步。我在准备这篇文章时,也深刻体会到了学习与分享的乐趣。    

         在此,我要特别感谢每一位阅读到这里的你。是你的关注和支持,给予了我持续写作和分享的动力。我深知,无论我在某个领域有多少见解,都离不开大家的鼓励与指正。因此,如果你在阅读过程中有任何疑问、建议或是发现了文章中的不足之处,都欢迎你慷慨赐教。               

        你的每一条反馈都是我前进路上的宝贵财富。同时,我也非常期待能够得到你的点赞、收藏,关注这将是对我莫大的支持和鼓励。当然,我更期待的是能够持续为你带来有价值的内容,让我们在知识的道路上共同前行。

相关文章:

  • Dubbo 通信流程 - 服务的调用
  • TCP可靠传输与慢启动机制
  • 项目上传github——SSH连接配置文档
  • 无参数读文件RCE
  • STRUCTBERT:将语言结构融入预训练以提升深度语言理解
  • AWS Aurora存算分离架构
  • Java可变参数:灵活的函数调用方式
  • 前端Material-UI面试题及参考答案
  • 洛谷题单1-P1001 A+B Problem-python-流程图重构
  • 初识 spring ai 之rag、mcp、tools calling使用
  • 存储效能驱动业务价值:星飞全闪关键业务场景性能实测报告
  • 解释 Webpack 中的模块打包机制,如何配置 Webpack 进行项目构建?
  • 调用deepseek大模型时智能嵌入函数
  • 使用 Spring AI Aliabab Module RAG 构建 Web Search 应用
  • 中药材图像分类,解锁小样本高精度建模秘籍-MATLAB赋能科研:基于AlexNet的迁移学习
  • 力扣hot100——最长连续序列(哈希unordered_set)
  • 力扣 第 153 场双周赛 讲题
  • 【redis】集群 数据分片算法:哈希求余、一致性哈希、哈希槽分区算法
  • 通过万能SPI设备驱动spidev.c来操作挂接在SPI总线上的SPI设备(DAC模块)【spidev.c代码详解、SPI控制器及SPI设备的设备树语句解析】
  • 工具——(常用的软件)视频编辑器
  • 银行网站维护是做哪些/链接提交
  • wordpress taobaoke/石家庄seo网站排名
  • 网站获取用户/竞价网
  • 门户网站制作需要多少钱/蚁坊软件舆情监测系统
  • 赣州网站制作/免费网站 推广网站
  • 行业网站渠道选择和内容运营/怎么样才能引流客人进店