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

本文主要是介绍<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!

这篇关于<Linux>(极简关键、省时省力)《Linux操作系统原理分析之Linux 进程管理 7》(11)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/432143

相关文章

防止Linux rm命令误操作的多场景防护方案与实践

《防止Linuxrm命令误操作的多场景防护方案与实践》在Linux系统中,rm命令是删除文件和目录的高效工具,但一旦误操作,如执行rm-rf/或rm-rf/*,极易导致系统数据灾难,本文针对不同场景... 目录引言理解 rm 命令及误操作风险rm 命令基础常见误操作案例防护方案使用 rm编程 别名及安全删除

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

ShardingProxy读写分离之原理、配置与实践过程

《ShardingProxy读写分离之原理、配置与实践过程》ShardingProxy是ApacheShardingSphere的数据库中间件,通过三层架构实现读写分离,解决高并发场景下数据库性能瓶... 目录一、ShardingProxy技术定位与读写分离核心价值1.1 技术定位1.2 读写分离核心价值二

深度解析Python中递归下降解析器的原理与实现

《深度解析Python中递归下降解析器的原理与实现》在编译器设计、配置文件处理和数据转换领域,递归下降解析器是最常用且最直观的解析技术,本文将详细介绍递归下降解析器的原理与实现,感兴趣的小伙伴可以跟随... 目录引言:解析器的核心价值一、递归下降解析器基础1.1 核心概念解析1.2 基本架构二、简单算术表达

深入浅出Spring中的@Autowired自动注入的工作原理及实践应用

《深入浅出Spring中的@Autowired自动注入的工作原理及实践应用》在Spring框架的学习旅程中,@Autowired无疑是一个高频出现却又让初学者头疼的注解,它看似简单,却蕴含着Sprin... 目录深入浅出Spring中的@Autowired:自动注入的奥秘什么是依赖注入?@Autowired

Redis实现高效内存管理的示例代码

《Redis实现高效内存管理的示例代码》Redis内存管理是其核心功能之一,为了高效地利用内存,Redis采用了多种技术和策略,如优化的数据结构、内存分配策略、内存回收、数据压缩等,下面就来详细的介绍... 目录1. 内存分配策略jemalloc 的使用2. 数据压缩和编码ziplist示例代码3. 优化的

从原理到实战解析Java Stream 的并行流性能优化

《从原理到实战解析JavaStream的并行流性能优化》本文给大家介绍JavaStream的并行流性能优化:从原理到实战的全攻略,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的... 目录一、并行流的核心原理与适用场景二、性能优化的核心策略1. 合理设置并行度:打破默认阈值2. 避免装箱

SpringBoot集成XXL-JOB实现任务管理全流程

《SpringBoot集成XXL-JOB实现任务管理全流程》XXL-JOB是一款轻量级分布式任务调度平台,功能丰富、界面简洁、易于扩展,本文介绍如何通过SpringBoot项目,使用RestTempl... 目录一、前言二、项目结构简述三、Maven 依赖四、Controller 代码详解五、Service