一个信号量是一个和我们已经描述过的其它IPC不相似的IPC形式(管道、FIFO和消息队列)。一个信号量是一个计数器,用来提供多个进程对共享的数据对象的访问。

SUS包含了信号量接口的一个替代集,在它的实时扩展的信号量选项里。我们不在本文讨论这些接口。

为了得到一个共享的资源,一个进程需要做如下的事:

1、测试控制这个资源的信号量;

2、如果信号量的值为正,那么进程可以使用这个资源。在这种情况下,进程把信号量值减一,表明它已经用了一个单位的这个资源;

3、否则,如果信号量的值为0,那么进程进入睡眠,直到信号量值比0大。当进程醒来时,它返回到步骤1。

当一个进程完成了一个被一个信号量控制的共享资源的使用时,信号量值被增一。如果任何其它进程在睡眠,正等待这个信号量,那么它们被唤醒。

为了正确实现信号量,信号量值的测试和这个值的减少必须是一个原子操作。由于这个原因,信号量通常在内核里实现。

一个普遍形式的信号量被称为一个二元信号量(binary semaphore)。它控制单个资源,且它的值被初始化为1。尽管如此,通常一个信号量可以被初始化为任何正值,这个值指定多少单位的共享资源可用。

不幸的是,XSI信号量比这个更复杂。三个特性导致了这个不必要的复杂性。

1、一个信号量不是简单的单个非负值。相反,我们必须定义一个信号量为一个或多个信号量值的集合。当我们创建一个信号量时,我们指明集合进而的值的数量。

2、信号量的创建(semget)和它的初始化(semctl)无关。这是一个致命缺陷,因为我们不能自动创建一个新的信号量集并初始化这个集里所有的值。

3、因为所有形式的XSI IPC保持存在,甚至当没用进程在使用它们时,所以我们必须担心一个终止却没有释放已经被分配的信号量的程序。我们待会描述的undo特性被期望处理它。

内核为每个信号量集维护一个semid_ds结构体:

struct semid_ds {

struct  ipc_perm  sem_perm;  /* see Section 15.6.2 */

unsigned short  sem_nsems;  /*  # of semaphores in set */

time_t  sem_otime;  /* last-semop() time */

time_t  sem_ctime;  /* last-change time */

...

};

SUS定义了展示的域,但是实现可以在semid_ds结构体里定义补充的成员。

每个信号量都表示了一个匿名结构体,包含至少以下成员:

struct {

unsigned short  semval;  /* semaphore value, always >= 0 */

pid_t  sempid;  /* pid for last operation */

unsigned short semncnt;  /* # processes awaiting semval>curval */

unsigned short  semzcnt;  /* # processes awaiting semval==0 */

};

下表列出了影响信号量集的系统限量(15.6.3节)。

影响信号量的系统限量描述典型值

FreeBSDLinuxMacSolaris

任何信号量的最大值32767327673276732767

任何信号量的adjust-on-exit值的最大值16384327671638416384

系统范围的信号量集的最大数量101288738110

系统范围的信号量的最大数量60320008738160

每个信号量集的信号量的最大数量602508738125

系统范围的unfo结构体的最大数量30320008738130

每个undo结构体的undo项的最大数量10321010

每个semop调用的操作的最大数量1003210010

第一个调用的函数是semget,来得到一个信号量ID。

#include

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

成功返回信号量ID。错误返回-1。

在15.6.1节,我们描述了转换key到一个标识符的规则并讨论了一个新集合被创建还是一个已有集合被引用。当一个新的集合被创建时,semid_ds结构体的以下成员被初始化。

1、ipc_perm结构体被初始化,如15.6.2节描述的。这个结构体的mode成员被设置为对应的flag的权限位。这些权限由15.6.2节的表里的值指定。

2、sem_otime被设为0。

3、sem_ctime被设为当前时间。

4、sem_nsems被设为nsems。

集里的信号量的数量为nsems。如果一个新的集合被创建(典型地在服务器里),那么我们必须指定nsems。如果我们引用一个已有集合(客户),那么我们可以指定nsems为0。

semctl函数作为各种信号量操作的杂烩。

#include

int semctl(int semid, int semnum, int cmd, ... /* union semun arg */);

返回(如下)。

第4个参数是可选的,取决于请求的命令,如果有的话,它的类型是senum,一个各种命令相关参数的联合体:

union semun {

int  val;  /* for SETVAL */

struct semid_ds  *buf;  /* for IPC_START and IPC_SET */

unsigned short  *array;  /* for GETALL and SETALL */

};

注意可选参数是真实的联合体,而不是联合体的指针。

cmd参数指定以下10个命令的某一个,在semid指定的集合上执行。引用一个特定信号量值的五个命令使用semnum来指明这个集合的一个成员。semnum的值在0和nsems-1之间,包括两者。

IPC_STAT:等到这个集合的semid_ds结构体,把它存储到arg.buf所指的结构体里。

IPC_SET:

设置sem_perm.uid、sem_perm.gid、和sem_perm.mode域,从这个集合的semid_ds结构体里的arg.buf指向

的结构体。这个命令只可以被其用户用户ID等于sem_perm.cuid或sem_permuid,或有超级用户权限的进程执行。

IPC_RMID:

从系统删除信号量集。这个删除是立即的。任何其它正在使用这个信号量的进程将在下次在这个信号量上尝试操作时得到一个EIDRM的错误。这个命令只可以被

其用户用户ID等于sem_perm.cuid或sem_permuid,或有超级用户权限的进程执行。

GETVAL:返回成员semnum的semval值。

SETVAL:设置semnum成员的semval值。这个值由arg.val指定。

GETPID:返回成员semnum的sempid值。

GETNCNT:返回成员semnum的semncnt值。

GETZCNT:返回成员semmum的semzcnt值。

GETALL:得到集合里所有的信号量的值。这些值被存储在arg.array所指的数组里。

SETALL:设置集合里所有的信号量的值为arg.array所指的值。

对于除了GETALL之外的所有GET命令,函数返回对应的值。对于其它的命令,返回值是0。

函数semop自动执行在一个信号量集合上的一个操作数组。

#include

int semop(int semid, struct sembuf semoparray[], size_t nops);

成功返回0,错误返回-1。

semoparray参数是一个指向一个信号量操作数组的指针,由sembuf结构体表示:

struct sembuf {

unsigned  short  sem_num;  /* member # in set (0, 1, ... nsems - 1) */

short  sem_op;  /* operation (negative, 0, or positive) */

short  sem_flg;  /*  IPC_NOWAIT, SEM_UNDO */

};

nops参数指明数组里的操作(元素)的数量。

集合的每个成员上的操作由对应的sem_op值指定。这个值可以是负数、0、或正数。(在以下的讨论里,我们引用一个信号量的“undo”标志。这个标准对应于SEM_UNDO位,在对应的sem_flg成员里。)

1、最简单的情况是当sem_op为正的值。这情况对应于进程资源的返回。这个sem_op的值被加入到信号量的值。如果undo标志被指定,那么sem_op也从进程的信号量调整值里被减去。

2、

如果sem_op为负数,那么我们想得到信号量控制的资源。如果信号量的值大于或等于sem_op的绝对值(资源可用),那么sem_op的绝对值从信号

量的值里被减去。这保证了信号量的结果值大于或等于0。如果undo标志被指明,那么sem_op的绝对值也被加到进程的信号量的调整值里。

如果信号量的值比sem_op的绝对值小(资源不可用),那么以下的条件应用:

a、如果IPC_NOWAIT被指定,那么semop返回EAGAIN的错误。

b、如果IPC_NOWAIT没有被指定,那么这个信号量的semncnt值被增加(因为调用者即将睡眠),而调用进程被挂起,直到以下条件发生:

i、

信号量的值变得大于或等于sem_op的绝对值(也就是说,一些其它进程已经释放了一些资源)。这个信号量的semncnt值被减少(因为调用进程已经完

成等待),sem_op的绝对值也从信号量的值里被减去。如果undo标志被指定,那么sem_op的绝对值也为这个进程加到信号量的调整值里。

ii、信号量从系统上被删除。在这种情况下,函数返回一个EIDRM错误。

iii、一个信号被进程捕获,而且信号处理机返回。在这种情况下,这个信号量的semncnt的值减少(因为调用进程不再等待),函数返回一个EINTR的错误。

3、如果sem_op为0,那么这表示这个调用进程想等待,直到信号量的值变为0。

如果信号量的值当前为0,那么函数立即返回。

如果信号量的值不是0,那么以下条件应用:

a、如果IPC_NOWAIT被指定,那么返回一个EAGAIN错误。

b、如果IPC_NOWAIT没有被指定,那么这个信号量的semzcnt值被增加(因为调用者即将睡眠),调用进程被挂起,直到以下某个情况发生:

i、信号量的值变为0。这个信号量的semzcnt的值减少(因为调用进程已经完成等待)。

ii、信号量从系统中被删除。这种情况下,函数返回一个EIDRM的错误。

iii、一个信号被进程捕获且信号处理机返回。这种情况下,这个信号量的semzcnt值减少(因为调用进程不再等待),函数返回一个EINTR的错误。

semop函数自动操作,它或者完成数组里的所有操作,或者一个都不完成。

在exit时的信号量调整(Semaphore Adjustment on exit)

如我们之前提到的,如果一个进程在终止时没有释放分配的信号量会有问题。每当我们为一个信号量操作指定SEM_UNDO标志并分配资源(一个小于0的

sem_op)时,内核记住了多们从那个特定信号量里分配了多少资源(sem_op的绝对值)。当进程终止时,不管是否自愿,内核检查进个进程是否有任何

突出的信号量调整值,如果有,那么把这个调整值应用到对应的信号量上。

如果我们使用semctl设置一个信号量的值,使用SETVAL或SETALL命令,那么这个信号量在所有进程里的调整值都被设置0。

例子--信号量和记录锁的计时比较

如果我们在多个进程间共享单个资源,那么我们可以使用信号量或者记录锁。比较这两种技术之间的计时区别是有趣的。

使

用一个信号量,我们创建一个由单个成员组成的信号量集,并把信号量的值初始化为1。要分配这个资源,我们用一个-1的sem_op来调用semop,要释

放资源,我们执行一个+1的sem_op。我们也为每个操作指定SEM_UNDO,来处理一个进程终止而不释放资源的情况。

使用记录锁时,我们创建一个空文件并使用文件的第一个字节(它不必存在)作为锁字节。为了分配内存,我们得到这个字节的写锁,为了释放它,我们解锁这个字节。记录锁的特性保证了如果一个进程握住一个锁时终止,那么这个锁会自动被内核释放掉。

下表展示了在Linux上执行这两个锁技术所需的时间。在每种情况下,资源被分配然后被释放100000资。这由三个不同的进程同时完成。下表是三个进程总的秒数。

Linux上的两种锁的计时比较操作用户系统挂钟带有undo的信号量0.380.480.86

建议记录锁0.410.951.36

在Linux上,记录锁相比于信号量锁在挂钟时间上有大约60%的惩罚。

即使记录锁比信号量锁慢,然而如果我们正锁住单个资源(比如一个共享内存段)而不需要XSI信号量的所有昂贵的特性,那么记录锁会更好。原因是它用起来简单得多,而且系统在一个进程终止时会关照任何苟延残喘的锁。

linux 信号量最大值,第十五章*进程间通信(八)--信号量(Semaphores)相关推荐

  1. linux键盘设置的文件在哪个文件夹,「正点原子Linux连载」第十五章按键输入试验...

    原标题:「正点原子Linux连载」第十五章按键输入试验 第十五章按键输入试验 前面几章试验都是讲解如何使用I.MX6U的GPIO输出控制功能,I.MX6U的IO不仅能作为输出,而且也可以作为输入.I. ...

  2. 【正点原子Linux连载】第十五章点亮LED-摘自【正点原子】I.MX6U嵌入式Linux C应用编程指南V1.1

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  3. APUE读书笔记-第十五章-进程间通信

    管道 创建管道(pipe函数) #include <unistd.h> int pipe(int fd[2); fd[0]为读打开,fd[1]为写打开 局限性 (1)管道是半双工的,数据只 ...

  4. 【正点原子Linux连载】第三十五章 Linux内核顶层Makefile详解 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  5. 25 linux ndk 头文件_正点原子Linux第二十五章RTC实时时钟实验

    1)资料下载:点击资料即可下载 2)对正点原子Linux感兴趣的同学可以加群讨论:935446741 3)关注正点原子公众号,获取最新资料更新 第二十五章RTC实时时钟实验 实时时钟是很常用的一个外设 ...

  6. 鸟哥的Linux私房菜(基础篇)- 第二十五章、 Linux 备份策略

    第二十五章. Linux备份策略 最近升级日期:2009/09/18 万一不幸你的 Linux 被黑客入侵了.或是你的 Linux 系统由於硬件关系 (不论是天灾还是人祸) 而挂掉了!这个时候,请问如 ...

  7. 鸟哥的Linux私房菜(基础篇)- 第十五章、磁碟配额(Quota)与进阶文件系统管理

    第十五章.磁碟配额(Quota)与进阶文件系统管理 最近升级日期:2009/09/10 如果您的 Linux 服务器有多个用户经常存取数据时,为了维护所有使用者在硬盘容量的公平使用,磁碟配额 (Quo ...

  8. 【正点原子Linux连载】第四十五章 pinctrl和gpio子系统实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  9. 【正点原子MP157连载】第二十五章 pinctrl和gpio子系统实验-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  10. 【正点原子MP157连载】第三十五章 设备树下的platform驱动编写-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

最新文章

  1. JavaScript引擎V8 5.1遵循了更多的ECMAScript规范并支持WASM
  2. 建模步骤_Revit软件介绍?Revit参数化构件建模步骤
  3. easyui中tab页中js脚本无法加载的问题及解决方法
  4. 非域计算机上模拟域用户,App-V如何让非域内(工作组)PC 也能享受应用程序虚拟化...
  5. 成功使用Windows Live Writer 2010发布日志
  6. php打印函数链,如何通过在PHP中使用包含该链的字符串来链接调用函数
  7. 【elasticsearch】elasticsearch 7 index.lifecycle.rollover_alias does not point to index
  8. Shell脚本学习-数组
  9. B. Suffix Structures 模拟吧,情况比较多要想周全
  10. 推荐一个字体工具:Fontmin
  11. [Luogu P2522] [HAOI2011]Problem b (莫比乌斯反演)
  12. 关于电量采集芯片(库仑计)DS2781相关操作及配置
  13. 人工智能数学基础--概率与统计4:联合分布与边缘分布
  14. ​杭州,苏州,成都哪个最宜居?
  15. GIS-空间分析(4)
  16. Cisco nat inside接口,outside接口,nvi接口的区别
  17. 知识图谱基础知识之三——知识图谱的构建过程
  18. 苹果应用内购买(IAP)—从入门到放弃
  19. 中电金信2022春季校园招聘火热开启
  20. 鸿蒙开发者beta活动,华为官宣:12月16日举行鸿蒙2.0手机开发者Beta活动

热门文章

  1. 定个小目标——做一款自己的游戏
  2. 定个理财小目标:8w到100w实盘
  3. 为什么说vue没有完全遵循mvvm
  4. 从基本组件到结构创新,67页论文解读深度卷积神经网络架构
  5. 167. 两数之和 II - 输入有序数组(java)
  6. java首行缩进两个字符,CSS 首行缩进两个文字
  7. python生成Androd deviceid
  8. Vue 大量数据展示卡顿解决方案(长列表优化)
  9. oracle存货转资产,存货转固定资产账务处理
  10. JS实现页面快捷键功能