<Linux>(极简关键、省时省力)《Linux操作系统原理分析之Linux 进程管理 7》(11)

《Linux操作系统原理分析之Linux 进程管理 7》(11)

  • 4 Linux 进程管理
    • 4.7 IPC 信号量机制
      • 4.7.1 信号量与信号量集合
        • 1.信号量
        • 2.信号量集合
        • 3.信号量集合的集中
      • 4.7.2 信号量集合的创建和检索
      • 4.7.3 信号量 PV 操作
      • 4.7.4 信号量操作等待队列
        • 1.信号量操作等待队列
        • 2.将信号量操作移出等待队列
      • 4.7.5 信号量控制操作
      • 4.7.6 信号量的程序例
        • 程序例 1:创建一个信号量集合,并对信号量进行 P 操作。
        • 程序例 2:通过信号量控制函数删除信号量集合。

4 Linux 进程管理

4.7 IPC 信号量机制

Linux 的信号量机制有两种:

1 其本身设置的信号量机制

2 引进 UNIX SYSTEM V 的 IPC(Internal Process Communication)中的信号量机制

IPC(Internal Process Communication)中的信号量机制其涉及到的函数和数据结构分别定义在 Linux 源文件的 ipc/sem.c 和 include/linux/sem.h

4.7.1 信号量与信号量集合

IPC 信号量机制更完善、更方便使用。

1.信号量

定义:
系统中每个信号量对应一个信号量结构体 sem,其定义如下:

Struct sem
{
Short semval; /*信号量的值*/
Unshort sempid; /*记录对信号量最后一次实施操作进程的 PID*/
}

PV 操作
(为了解决死锁)IPC 信号量机制引进了信号量集合的概念,可以使用原语一次对多个信号量进行操作。

2.信号量集合

信号量集合:把进程需要访问资源对应的信号量组成一个信号量集合,并可以使用操作原语一次性地对信号量集中的多个信号量进行 PV 操作。
信号量数组:在 IPC 信号量机制中把多个信号量组成一个信号量集合,该集合由信号量结构体 sem组成,称为信号量数组。
信号量集合描述符:系统中的每个信号量集合用一个描述符描述其特征和记载其管理信息。其定义如下:

Struct semid_ds
{
Struct_ipc_perm sem_perm; /*对信号量集合的访问权限*/
Time_t sem_otime; /*最后一次对信号量集进行操作的时间*/
Time_t sem_ctime; /*最后一次修改信号量集的时间*/
Struct sem *sem_base; /*指向信号量数组*/
Struct sem_queue *sem_pending; /*指向等待队列头*/
Struct sem_queue *sem_pending_last;/* 指向等待队列尾*/
Struct sem_undo *undo; /*进程终止时需要使用 sem_undo 结构体中的信息,对信号量集合进行有关操作*/
Unshort sem_nsems; /*信号量集合中信号量的数目*/
}
3.信号量集合的集中

IPC 对系统中的所有信号量集合进行集中管理,把所有的信号量集合描述符组织在一个 semary[]数组中,其定义如下:

Static struct semid_ds *smeary[SEMMNI];

其中 SEMMNI 为数组大小,是系统中可以设置的信号量集合的最大数目,其缺省值为 128,宏定义如下:

#define SEMMNI 128

4.7.2 信号量集合的创建和检索

1.semget()
系统为每个信号量集合设定了一个唯一的标识号 ID,IPC 提供了创建信号量结合和获取信号量集合标识号的系统调用 semget(),其原型定义如下:

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

该系统调用正常则返回值为信号量集合的标识号,出错则返回负数。
Key:要创建或要获取的信号量集合的标志键值,可以用户指定,也可使用符号常量 IPC_PRIVATE 由系统给定。
Nsems:指出要创建的信号量集合中包含的信号量的个数。
Semflgs:操作标志;指定了信号量集合的访问权限和操作模式。其取值或符号常量及意义如下:
0400 允许创建者读
0200 允许创建者写
0040 允许创建者同组用户读
0020 允许创建者同组用户写
0004 允许其它所有的进程读
0002 允许其它所有的进程写
IPC_CREAT(00001000) 创建新的信号量集合
IPC_EXCL(00002000) 检索信号量集合

前六项指定了信号量集合的访问权限,后两项指定了操作模式。访问权限和操作模式可以使用逻 辑与(|)组合在一起表示复合属性。

2.创建信号量集
若操作模式设定为 IPC_CREAT:

则当系统中尚没有建立与 key 对应的信号量集合,则建立这个新的集合,并返回新集合的标 识号;
当系统中已经存在与键值 key 对应的信号量集合,则返回这个集合的标识号;
当不能创建,则返回-1

若操作模式设定为 IPC_CREAT|IPC_EXCL:

与前不同的是,当系统中已经存在与键值 key 对应的信号量集合,则返回错误值-EEXIST。

例:建立一个健值为 KEY,包含 1 个信号量,允许任何进程读写的信号量集合时,调用函数的形式为: id=semget(KEY,1,0666|IPC_CREAT);
3.检索信号量集 检索信号量集
检索一个信号量集合的标识号时,只需将 semflg 中的操作模式设为 IPC_EXCL。若存在,返回集合的标识号,否则,返回-1;

4.7.3 信号量 PV 操作

IPC 中没有对信号量分别设置 P 和 V 操作原语,而是统一由具有原语性质的系统调用 semop()实现的,通常称其为信号量操作函数。其定义如下:

Int semop(int semid,struct sembuf *sops, unsigned nsops);
//Semid:实施 pv 操作的信号量集合的标识号
//Nsops:本次实施操作的信号量的个数
//Sops:指向一个信号量操作数组。因为每次对信号量集合中实施操作的信号量个数不同,不同信号量实施的操作不同,所以必须指明本次操作是对哪些信号量,实施哪些操作。该数组的元素个数就是 Nsops。
Sops 中的每个元素是一个 sembuf 结构体,它由系统定义:
Struct sembuf
{
Ushort sem_num; /*指出信号量在信号量数组中的下标*/
Short sem_op; /*指出操作的种类*/
Short sem_flg; /*指出操作的标志*/
}

说明:

Sem_op 的值决定操作的类型:
1、Sem_op 的值是负数:表示进程请求资源,则实施 P 操作,把 semval 的值减去 sem_op绝对值。
2、Sem_op 的值是正数:表示进程释放资源,则实施 V 操作,把 semval 的值加上 sem_op以上两种情况下,若对信号量集实施操作后,所有信号量的值 semval 均大于等于 0,则函数返回 0,表示进程所需的多个资源都可用,此时进程可以继续运行;否则,只要有一个 semval结果为负数,则表示进程需要的这种资源不可用,进程被阻塞,并将本次操作加入该信号量集合的等待队列。
3、Sem_op 的值为 0。此时若 semval 也是 0,则函数返回,调用 semop 的进程继续执行;若semval 非 0,则进程被阻塞。
4、sem_flg:控制进程的执行。通常取值 0;若指定为 IPC_NOWAIT,则在执行 semop操作时,即使出现需要进程阻塞的情况,也不阻塞,而是继续运行。

4.7.4 信号量操作等待队列

1.信号量操作等待队列

IPC每个信号量集合都有一个等待队列,分别由其描述符中的成员向 sem_pending 和 sem_pending_last 指向其头部和尾部。该等待队列是由 sem_queue 结构体组成的双向循环链表。Sem_queue 结构体定义如下:

Struct sem_queue
{
Struct sem_queue *next; /*指向队列后一个节点*/
Struct sem_queue *prev; /*指向队列前一个节点*/
Struct wait_queue *sleeper; /*指向被阻塞进程*/
Struct sem_undo *undo;
Int pid; /*实施操作的进程的 PID*/
Int status;
Struct semid_ds *sma; /*指出对哪个信号量集合实施操作*/
Struct sembuf *sops; /*指向是进程阻塞的操作数组*/
Int nsops; /*指出操作数组中操作的个数*/
}
2.将信号量操作移出等待队列

若某个进程在执行 semop()时没有阻塞,函数将检查该信号量集的操作等待队列:

1、若无操作等待, 则返回;
2、若有操作等待, 则依次重新执行这些操作:
1)若进程仍需等待,则操作保留在等待队列中;
2)若进程可以继续执行,则通过该 sem_queue 结构体中的 sleeper 在进程等待队列中找到该进程,并将其唤醒;再将该sem_queue 结构体从操作等待队列中删除。

4.7.5 信号量控制操作

IPC 信号量机制提供了可以对信号量集合进行多种控制操作的系统调用 semctl(),它能实现对信号量的初始化、查询、修改、删除等功能。

Int semctl(int semid,int semnum,int cmd,union semun arg);
Semid:指向操作对象,即信号量集合标记号。
Semnum:信号量的索引号,指明信号量在信号量数组中的下标。
Cmd:指定各种不同操作。
Arg:用于传递执行各种控制操作时所需的参数。
union semun 是 IPC 定义 的一个联合体。其定义 :

Union semum
{
Int val;
Struct semid_ds *buf;
Unshort *array;
Struct seminfo *_buf;
Void*_pad;
}

cmd,对应的符号常量和意义所在:

符号常量意义
IPC_STAT读取信号量集合 semid_ds 结构体的内容,并把它写进参数 arg 给出的 semnum 联合体中成员项 buf 指定的 semid_ds 结构中。此时无视参数 Semnum。
IPC_SET修改信号量集合 semid_ds 结构体的成员项 sem_perm 结构中的某些成员项的值;同时sem_ctime 被自动更新。只有信号量集合创建者、同组用户和超级用户可以执行该操作。修改值取自参数 arg给出的 semun 联合体中 buf 指定的 semid_ds 结构体的第一个成员项。
IPC_RMID从内核中删除信号量集合。只有信号量集合创建者和超级用户。在该信号量集合中等该信号量操作的所有进程被唤醒,并得到错误信息 EIDRM。
GETPID获取信号量数组中以参数 semnum 做为索引值指定信号量的 sempid 值。它是对信号量最后一个执行 semop()操作的进程的 PID。
SETVAL设置信号量数组中以参数 semnum 做为索引值指定信号量的值。Arg 的 semun 联合体成员项 val 中给出要设置的值。
GETVAL获取信号量数组中以参数 semnum 做为索引值指定信号量的 semval 值。其值写入 arg 的semun 联合体成员项 val 中。该值也作为函数返回值返回。
SETALL设置信号量集合中所有信号量的值,设置值存放在 Arg 的 semun 联合体成员项 array 中。
GETALL获取信号量集合中所有信号量的值,并存放在 Arg 的 semun 联合体成员项 array 中。
GETNCNT得到等待以 semnum 为索引指出的信号量的值为非 0 的进程的个数。

4.7.6 信号量的程序例

程序例 1:创建一个信号量集合,并对信号量进行 P 操作。
/*mksem.c*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
Int main(void{
Int semid; /*信号量集合标识号*/
Int nsems=1; /*信号量集合中信号量的个数*/
Int flags =6/*对信号量集合的访问权限,允许所有进程进行读写*/
Struct sembuf buf;/*信号量操作数组*/
/*创建信号量集*/
Semid = semget(IPC_PRIVATE,nsems,flags);
If(semid<0) /*若返回值为负,则出错*/
{
Printf(“semapher ceate failer! \n”); 
Exit(EXIT_FAILURE)}
Printf(“semapher ceated:%d\n”,semid); 
/*设置操作数组个成员项的值*/
Buf.sem_num=0; /*下标为 0,因为只有一个信号量*/
Buf.sem_op =1; /*信号量加 1 操作*/
Buf.sem_flg=IPC_NOWAIT; /*进程不阻塞*/
If((semop(semid,&buf, nsems)<0/*执行信号量操作*/
{
/*信号量操作失败*/
Printf(“semapher operation failer! \n”); 
Exit(EXIT_FAILURE)}
System(“ipcs –s”); /*执行键盘命令 ipcs –s,显示 IPC 信号量*/
Exit(EXIT_SUCCESS)/*成功退出*/
}

程序运行结果如下:

$./mksem
semapher ceated:520
-----semapher arrays------
Key semid owner perms nsems status
0x00000000 520 wang 666 1
程序例 2:通过信号量控制函数删除信号量集合。
/*sctlsem.c*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
Int main(int argc,char *argv[ ]{
Int semid; /*信号量集合标识号*/
If(argc!=2{
Puts(“USAGE:sctl<semaphore id>”);
exit(EXIT_FAILURE)}
Semid=atoi(argv[1]); /*从命令行参数得到信号量集合标识号*/
/*删除信号量集合*/
If((semctl(semid,0IPC_RMID))<0/*调用信号量控制函数*/
{
/*返回负值,失败退出*/
Printf(“semapher control failer! \n”); 
exit(EXIT_FAILURE)}
Else
{
Puts(“semapher removed! ”);
System(“ipcs –s”); /*执行键盘命令 ipcs –s,显示 IPC 信号量*/
}
Exit(EXIT_SUCCESS)/*成功退出*/
}

程序运行时,需要给出所要删除的信号量集合的标识号,程序运行结果如下:

$./ sctlsem.c 520
semapher removed!

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

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

相关文章

整数反转 Golang leecode_7

拿到手第一反应还是暴力&#xff0c;直接从低位到高位把数一个个取出来&#xff0c;然后乘以每一位的权重&#xff0c;构成一个新的反转后的整数 res 返回&#xff0c;代码如下 package mainimport ("fmt""math" )func reverse(x int) int {if x > -10…

Java 基础学习(三)循环流程控制与数组

1 循环流程控制 1.1 循环流程控制概述 1.1.1 什么是循环流程控制 当一个业务过程需要多次重复执行一个程序单元时&#xff0c;可以使用循环流程控制实现。 Java中包含3种循环结构&#xff1a; 1.2 for循环 1.2.1 for循环基础语法 for循环是最常用的循环流程控制&#xff…

redis运维(二十二)redis 的扩展应用 lua(四)

一 最佳实践 ① 铺垫 最佳实践&#xff1a;1、把redis操作所需的key通过KEYS进行参数传递2、其它的lua脚本所需的参数通过ARGV进行传递. redis lua脚本原理 Redis Lua脚本的执行原理 ② 删除指定的脚本缓存 ③ redis集群模式下使用lua脚本注意事项 1、常见报错现象 C…

SpringBoot 项目中获取 Request 的四种方法

SpringBoot 项目中获取 Request 的四种方法 方法1、Controller中加参数来获取request 注意&#xff1a;只能在Controller中加入request参数。 一般&#xff0c;我们在Controller中加参数获取HttpServletRequest&#xff0c;如下所示&#xff1a; RestController RequestMap…

『 Linux 』进程优先级

文章目录 什么是优先级Linux下的进程优先级PRI与NI使用top查看进程以及对进程的优先级的修改 进程优先级的其他概念竞争性与独立性并发与并行 什么是优先级 优先级,顾名思义,即在同一环境下不同单位对同一个资源的享有顺序; 一般优先级高的单位将优先占有该资源; 在进程当中进…

Python中如何用栈实现队列

目录 一、引言 二、使用两个栈实现队列 三、性能分析 四、应用场景 五、代码示例 六、优缺点总结 一、引言 队列&#xff08;Queue&#xff09;和栈&#xff08;Stack&#xff09;是计算机科学中常用的数据结构。队列是一种特殊的线性表&#xff0c;只允许在表的前端进行…

Leetcode 380. O(1) 时间插入、删除和获取随机元素

文章目录 题目代码&#xff08;11.28 首刷看解析&#xff09; 题目 Leetcode 380. O(1) 时间插入、删除和获取随机元素 代码&#xff08;11.28 首刷看解析&#xff09; 1.length:表示的是数组的长度 数组 2.length():表示的是字符串的长度 字符串 3.size():表示的是集合中有多…

万宾科技可燃气体监测仪科技作用全览

燃气管网在运行过程中经常会遇到燃气管道泄漏的问题&#xff0c;燃气泄漏甚至会引起爆炸&#xff0c;从而威胁人民的生命和财产安全&#xff0c;因此对燃气管网进行定期巡检是十分必要的工作。但是传统的人工巡检已不能满足城市的需要&#xff0c;除了选择增加巡检人员之外&…

#Js篇:前端的设计模式有哪些

常见的前端设计模式 单例模式 保证一个类仅有一个实例&#xff0c;并提供一个访问他的全局访问点&#xff1b;vuex常用于管理全局状态、配置信息等。 工厂模式 一个用于创建对象的接口&#xff0c;让子类决定实例化哪个类&#xff1b; 创建复杂对象或对象的组合。 策略模…

Web UI自动化测试框架

WebUI automation testing framework based on Selenium and unittest. 基于 selenium 和 unittest 的 Web UI自动化测试框架。 特点 提供更加简单API编写自动化测试。提供脚手架&#xff0c;快速生成自动化测试项目。自动生成HTML测试报告生成。自带断言方法&#xff0c;断言…

笔记:Pika Labs 3D 动画生成工具

Pika Labs 一款3D 动画生成工具 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/134657306 目 录 1. 简介2. 准备2.1 安装 discord2.2 加入 Discord 频道 3. Pika 使用指南2.1 快速开始2.2 从图像到视频2.3 Pika Bot按钮2.4 提示&#xff08;Prompt&a…

Spark Streaming的基本数据流

先来介绍一下按照动静对数据的区分 静态数据 ​ 静态数据&#xff08;Static Data&#xff09;指的是在一段时间内不会或很少发生变化的数据。这种类型的数据通常是固定的&#xff0c;并且不会随着时间的推移而更新或仅偶尔更新。静态数据的典型例子包括配置文件、参考表、历…

面试题:说一下MyBatis动态代理原理?

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1.MyBatis简介2.使用步骤2.1、引入依赖2.2、配置文件2.3、接口定义2.4、加载执行 3.原理解析 1.MyBatis简介 MyBatis是一个ORM工具&#xff0c;封装了JDBC的操作&a…

Redis 主从架构,Redis 分区,Redis哈希槽的概念,为什么要做Redis分区

文章目录 Redis 主从架构redis replication 的核心机制redis 主从复制的核心原理过程原理Redis集群的主从复制模型是怎样的&#xff1f;生产环境中的 redis 是怎么部署的&#xff1f;机器是什么配置&#xff1f;你往内存里写的是什么数据&#xff1f;说说Redis哈希槽的概念&…

前端入门(四)Ajax、Promise异步、Axios通信、vue-router路由

文章目录 AjaxAjax特点 Promise 异步编程&#xff08;缺&#xff09;Promise基本使用状态 - PromiseState结果 - PromiseResult Axios基本使用 Vue路由 - vue-router单页面Web应用&#xff08;single page web application&#xff0c;SPA&#xff09;vue-router基本使用路由使…

Rabbitmq发送邮件并消费邮件

&#x1f4d1;前言 本文主要是【Rabbitmq】——Rabbitmq发送邮件并消费邮件的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1…

jvm的相关知识点

Java Virtual Machine&#xff08;JVM&#xff09;是Java程序的运行环境&#xff0c;是Java技术的核心和关键之一。JVM负责执行Java字节码&#xff0c;并提供了一种平台无关性的执行环境&#xff0c;使得Java程序可以在不同的硬件和操作系统上运行。 下面是关于JVM的一些重要知…

spring应用在afterPropertiesSet方法中获取ApplicationContext

在afterPropertiesSet方法中获取ApplicationContext是可以的。Spring容器在初始化bean后&#xff0c;会自动调用afterPropertiesSet方法。在这个方法中&#xff0c;您可以获取到ApplicationContext对象。 以下是一个示例代码&#xff1a; import org.springframework.context…

【数学】旋转矩阵

参考链接 OpenGL from OpenGL.GL import * from OpenGL.GLUT import * from math import * import numpy as np def draw_axes():glClear(GL_COLOR_BUFFER_BIT)# 绘制坐标轴glColor3f(1.0, 1.0, 1.0) # 设置坐标轴颜色为白色glBegin(GL_LINES)glVertex2f(-1.0, 0.0) # x 轴g…

Python中使用matplotlib库绘图中如何给图形的图例设置中文字体显示

问题&#xff1a;当使用matplotlib绘图时遇到绘图&#xff0c;图例显示不出来中文字体 解决方式&#xff1a; 1&#xff09;加载字体管理库 from matplotlib.font_manager import FontProperties 2&#xff09;设置系统上字体的路径 font FontProperties(fname"C:\\W…