<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

相关文章

Java线程池核心参数原理及使用指南

《Java线程池核心参数原理及使用指南》本文详细介绍了Java线程池的基本概念、核心类、核心参数、工作原理、常见类型以及最佳实践,通过理解每个参数的含义和工作原理,可以更好地配置线程池,提高系统性能,... 目录一、线程池概述1.1 什么是线程池1.2 线程池的优势二、线程池核心类三、ThreadPoolE

使用Redis实现会话管理的示例代码

《使用Redis实现会话管理的示例代码》文章介绍了如何使用Redis实现会话管理,包括会话的创建、读取、更新和删除操作,通过设置会话超时时间并重置,可以确保会话在用户持续活动期间不会过期,此外,展示了... 目录1. 会话管理的基本概念2. 使用Redis实现会话管理2.1 引入依赖2.2 会话管理基本操作

Springboot请求和响应相关注解及使用场景分析

《Springboot请求和响应相关注解及使用场景分析》本文介绍了SpringBoot中用于处理HTTP请求和构建HTTP响应的常用注解,包括@RequestMapping、@RequestParam... 目录1. 请求处理注解@RequestMapping@GetMapping, @PostMappin

Linux内核定时器使用及说明

《Linux内核定时器使用及说明》文章详细介绍了Linux内核定时器的特性、核心数据结构、时间相关转换函数以及操作API,通过示例展示了如何编写和使用定时器,包括按键消抖的应用... 目录1.linux内核定时器特征2.Linux内核定时器核心数据结构3.Linux内核时间相关转换函数4.Linux内核定时

Linux镜像文件制作方式

《Linux镜像文件制作方式》本文介绍了Linux镜像文件制作的过程,包括确定磁盘空间布局、制作空白镜像文件、分区与格式化、复制引导分区和其他分区... 目录1.确定磁盘空间布局2.制作空白镜像文件3.分区与格式化1) 分区2) 格式化4.复制引导分区5.复制其它分区1) 挂载2) 复制bootfs分区3)

Spring Boot Interceptor的原理、配置、顺序控制及与Filter的关键区别对比分析

《SpringBootInterceptor的原理、配置、顺序控制及与Filter的关键区别对比分析》本文主要介绍了SpringBoot中的拦截器(Interceptor)及其与过滤器(Filt... 目录前言一、核心功能二、拦截器的实现2.1 定义自定义拦截器2.2 注册拦截器三、多拦截器的执行顺序四、过

Java 队列Queue从原理到实战指南

《Java队列Queue从原理到实战指南》本文介绍了Java中队列(Queue)的底层实现、常见方法及其区别,通过LinkedList和ArrayDeque的实现,以及循环队列的概念,展示了如何高效... 目录一、队列的认识队列的底层与集合框架常见的队列方法插入元素方法对比(add和offer)移除元素方法

C++ scoped_ptr 和 unique_ptr对比分析

《C++scoped_ptr和unique_ptr对比分析》本文介绍了C++中的`scoped_ptr`和`unique_ptr`,详细比较了它们的特性、使用场景以及现代C++推荐的使用`uni... 目录1. scoped_ptr基本特性主要特点2. unique_ptr基本用法3. 主要区别对比4. u

SQL 注入攻击(SQL Injection)原理、利用方式与防御策略深度解析

《SQL注入攻击(SQLInjection)原理、利用方式与防御策略深度解析》本文将从SQL注入的基本原理、攻击方式、常见利用手法,到企业级防御方案进行全面讲解,以帮助开发者和安全人员更系统地理解... 目录一、前言二、SQL 注入攻击的基本概念三、SQL 注入常见类型分析1. 基于错误回显的注入(Erro

Spring IOC核心原理详解与运用实战教程

《SpringIOC核心原理详解与运用实战教程》本文详细解析了SpringIOC容器的核心原理,包括BeanFactory体系、依赖注入机制、循环依赖解决和三级缓存机制,同时,介绍了SpringBo... 目录1. Spring IOC核心原理深度解析1.1 BeanFactory体系与内部结构1.1.1