- 浏览: 142401 次
文章分类
最新评论
在XSI IPC通信之消息队列一节中我们介绍了消息队列,对比消息队列,本文将介绍信号量的一些常用操作。
信号量与管道、FIFO以及消息队列等IPC机制不同,它是一个用于为多进程提供对共享数据对象的访问的计数器。为了获得共享资源,进程需要执行下列操作。
(1)测试控制该资源的信号量。
(2)若此信号量的值为正,则进程可以使用该资源,并会将信号量的值减一,表示使用了一个资源单位。
(3)否则,若此信号量的值为0,则进程进入休眠状态,直到信号量值大于0才被唤醒,之后又会返回到步骤(1)。
当进程不再使用由一个信号量控制的共享资源时,该信号量值增 1,同时会唤醒正在等待此信号量的休眠进程。为了正确地实现信号量,信号量值的测试及加减操作都应当是原子操作,为此,信号量通常是在内核中实现的。
常用的信号量形式被称为二元信号量,它控制单个资源,其初始值为 1。但一般而言,信号量的初值可以是任意一个正值,表明有多少个共享资源单位可供共享应用。
不过,XSI 信号量与此相比要复杂得多。以下3种特性造成了这种不必要的复杂性。
(1)信号量并非是单个非负值,而必须定义为含有一个或多个信号量值的集合。当创建信号量时,要指定集合中信号量值的数量。
(2)信号量的创建是独立于它的初始化的。这是一个致命的缺点,因为不能原子地创建一个信号量集合,并且对该集合中的各个信号量值赋初值。
(3)即使没有进程正在使用各种形式的XSI IPC,它们仍然是存在的。有的程序在终止时并没有释放已经分配给它的信号量,后面要介绍的 undo 功能就是处理这种情况的。
下表列出了影响信号量集合的系统限制。
内核为每个信号量集合维护着一个 semid_ds 结构,一般至少包含下面这些字段。
每个信号量由一个无名结构表示,它至少包含下列成员。
在使用 XSI 信号量时,需要先调用 semget 函数来获得或创建一个信号量 ID。semctl 函数包含了多种信号量操作。semop 函数自动执行信号量集合上的操作数组。
使用 semget 函数创建一个新集合时,会对 semid_ds 结构的下列成员赋初值。
* 按XSI IPC 相似特征介绍一节中所述来初始化 ipc_perm 结构,该结构中的 mode 成员被设置为 flag 中的相应权限位。
* sem_otime 设置为 0。
* sem_ctime 设置为当前时间。
* sem_nsems 设置为 nsems。
参数 nsems 是该集合中的信号量数。如果是引用现有集合,可将其指定为 0。
semctl 函数的第 4 个参数是可选的,它的类型是 semun 联合体,是多个命令特定参数的联合,是否使用取决于所请求的命令。
cmd 参数指定下列 10 种命令中的一种,其中有 5 种是针对一个特定的信号量值的,它们用 semnum 指定该信号量集合中的一个成员。semnum 的值在 0 和 semget 函数的参数
nsems-1 之间。
* IPC_STAT:对此集合取 semid_ds 结构,并存储在由 arg.buf 指向的结构中。
* IPC_SET:按 arg.buf 指向的结构中的值,设置与此集合相关的结构中的 sem_perm.uid、sem_perm.gid 和 sem_perm.mode 字段。此命令只能由两种进程执行:一种是其有效用户 ID 等于 sem_perm.cuid 或 sem_perm.uid 的进程;另一种是具有超级用户特权的进程。
* IPC_RMID:从系统中立即删除该信号量集合。删除后其他进程在下次试图对此信号量集合进行操作时,将出错返回 EIDRM。该命令也只能由上面提及的两种进程执行。
* GETVAL:返回成员 semnum 的 semval 值。
* SETVAL:设置成员 semnum 的 semval 值为 arg.val 指定的值。
* GETPID:返回成员 semmum 的 sempid 值。
* GETNCNT:返回成员 semnum 的 semncnt 值。
* GETZCNT:返回成员 semnum 的 semzcnt 值。
* GETALL:取该集合中所有的信号量值,并存储在 arg.array 指向的数组中。
* SETALL:将该集合中所有的信号量值设置成 arg.array 指向的数组中的值。
对于除 GETALL 以外的所有 GET 命令,semctl 函数都返回相应值;对于其他命令,若成功则返回 0,否则返回 -1 并设置 errno。
semop 函数中的 semoparray 参数是一个由 sembuf 结构组成的信号量操作数组。nops 参数规定了该数组中操作的数量。对集合中每个成员的操作由 sembuf 结构中相应的 sem_op 值规定,它可以是负值、0 或正值,对各个情况的处理如下所述。
(1)sem_op 为正值:这对应于进程释放的占用的资源数。sem_op 值会加到信号量的值上。如果指定了 undo 标志(该标志对应于相应的 sem_flg 成员的 SEM_UNDO 位),则也从该进程的此信号量调整值中减去 sem_op。
(2)sem_op 为负值:这表示要获取由该信号量控制的资源。如若该信号量的值大于等于 sem_op 的绝对值(说明具有所需的资源),则从信号量值中减去 sem_op 的绝对值。如果指定了 undo 标志,则 sem_op 的绝对值也加到该进程的此信号量调整值上。如若信号量值小于 sem_op 的绝对值,则适用下列条件。
a)若指定了 IPC_NOWAIT,则 semop 函数出错返回 EAGAIN。
b)若未指定 IPC_NOWAIT,则该信号量的 semncnt 值加 1(因为调用进程将进入休眠),然后调用进程被挂起直至发生下列事件之一。
i. 此信号量值变成大于等于 sem_op 的绝对值(即某个进程已释放了某些资源)。此信号量的 semncnt 值减 1,并从信号量值中减去 sem_op 的绝对值。如果指定了 undo 标志,则 sem_op 的绝对值也加到该进程的此信号量调整值上。
ii. 从系统中删除了此信号量,此时 semop 函数出错返回 EIDRM。
iii. 进程捕捉到一个信号,并从信号处理程序返回。此时此信号量的 semncnt 值减 1(因为调用进程不再等待),并且 semop 函数出错返回 EINTR。
(3)sem_op 为 0:这表示调用进程希望等待到该信号量值变成 0。如果当前信号量值是 0,则 semop 函数立即返回。如果当前信号量值非 0,则适用下列条件。
a)若指定了 IPC_NOWAIT,则出错返回 EAGAIN。
b)若未指定 IPC_NOWAIT,则该信号量的 semzcnt 值加 1(因为调用进程将进入休眠),然后调用进程被挂起直至发生下列事件之一。
i. 此信号量值变成 0,此时会将此信号量的 semzcnt 值减 1。
ii. 从系统中删除了此信号量,此时 semop 函数出错返回 EIDRM。
iii. 进程捕捉到一个信号,并从信号处理程序返回。此时此信号量的 semzcnt 值减 1(因为调用进程不再等待),并且 semop 函数出错返回 EINTR。
semop 函数具有原子性,它或者执行数组中的所有操作,或者一个也不做。
前面说过,如果在进程终止时,它占用了经由信号量分配的资源,那么就会成为一个问题。无论何时只要为信号量操作指定了 SEM_UNDO 标志,然后分配资源(sem_op 值小于 0),那么内核就会记住对于该特定信号量,分配给调用进程多少资源(sem_op 的绝对值)。当该进程终止时,内核就会检验该进程是否还有尚未处理的信号量调整值,如果有,则按调整值对相应信号量进行处理。
如果用带有 SETVAL 或 SETALL 命令的 semctl 设置一个信号量的值,则在所有进程中,该信号量的调整值都将设置为 0。
信号量与管道、FIFO以及消息队列等IPC机制不同,它是一个用于为多进程提供对共享数据对象的访问的计数器。为了获得共享资源,进程需要执行下列操作。
(1)测试控制该资源的信号量。
(2)若此信号量的值为正,则进程可以使用该资源,并会将信号量的值减一,表示使用了一个资源单位。
(3)否则,若此信号量的值为0,则进程进入休眠状态,直到信号量值大于0才被唤醒,之后又会返回到步骤(1)。
当进程不再使用由一个信号量控制的共享资源时,该信号量值增 1,同时会唤醒正在等待此信号量的休眠进程。为了正确地实现信号量,信号量值的测试及加减操作都应当是原子操作,为此,信号量通常是在内核中实现的。
常用的信号量形式被称为二元信号量,它控制单个资源,其初始值为 1。但一般而言,信号量的初值可以是任意一个正值,表明有多少个共享资源单位可供共享应用。
不过,XSI 信号量与此相比要复杂得多。以下3种特性造成了这种不必要的复杂性。
(1)信号量并非是单个非负值,而必须定义为含有一个或多个信号量值的集合。当创建信号量时,要指定集合中信号量值的数量。
(2)信号量的创建是独立于它的初始化的。这是一个致命的缺点,因为不能原子地创建一个信号量集合,并且对该集合中的各个信号量值赋初值。
(3)即使没有进程正在使用各种形式的XSI IPC,它们仍然是存在的。有的程序在终止时并没有释放已经分配给它的信号量,后面要介绍的 undo 功能就是处理这种情况的。
下表列出了影响信号量集合的系统限制。
内核为每个信号量集合维护着一个 semid_ds 结构,一般至少包含下面这些字段。
struct semid_ds{ struct ipc_perm sem_perm; unsigned short sem_nsems; // # of semaphores in set time_t sem_otime; // last-semop() time time_t sem_ctime; // last-change time /* ... */ };
每个信号量由一个无名结构表示,它至少包含下列成员。
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 /* ... */ };
在使用 XSI 信号量时,需要先调用 semget 函数来获得或创建一个信号量 ID。semctl 函数包含了多种信号量操作。semop 函数自动执行信号量集合上的操作数组。
#include <sys/sem.h> int semget(key_t key, int nsems, int flag); /* 返回值:若成功,返回信号量ID;否则,返回-1 */ int semctl(int semid, int semnum, int cmd, .../* union semun arg */); /* 返回值:(见下面所述)*/ union semun{ int val; /* for SETVAL */ struct semid_ds *buf; /* for IPC_STAT and IPC_SET */ unsigned short *array; /* for GATALL and SETALL */ }; int semop(int semid, struct sembuf semoparray[], size_t nops); /* 返回值:若成功,返回 0;否则,返回-1 */ struct sembuf{ unsigned short sem_num; // member # in set (0, 1, ..., nsems-1) short sem_op; // operation (negative, 0, or pasitive) short sem_flg; // IPC_NOWAIT, SEM_UNDO };
使用 semget 函数创建一个新集合时,会对 semid_ds 结构的下列成员赋初值。
* 按XSI IPC 相似特征介绍一节中所述来初始化 ipc_perm 结构,该结构中的 mode 成员被设置为 flag 中的相应权限位。
* sem_otime 设置为 0。
* sem_ctime 设置为当前时间。
* sem_nsems 设置为 nsems。
参数 nsems 是该集合中的信号量数。如果是引用现有集合,可将其指定为 0。
semctl 函数的第 4 个参数是可选的,它的类型是 semun 联合体,是多个命令特定参数的联合,是否使用取决于所请求的命令。
cmd 参数指定下列 10 种命令中的一种,其中有 5 种是针对一个特定的信号量值的,它们用 semnum 指定该信号量集合中的一个成员。semnum 的值在 0 和 semget 函数的参数
nsems-1 之间。
* IPC_STAT:对此集合取 semid_ds 结构,并存储在由 arg.buf 指向的结构中。
* IPC_SET:按 arg.buf 指向的结构中的值,设置与此集合相关的结构中的 sem_perm.uid、sem_perm.gid 和 sem_perm.mode 字段。此命令只能由两种进程执行:一种是其有效用户 ID 等于 sem_perm.cuid 或 sem_perm.uid 的进程;另一种是具有超级用户特权的进程。
* IPC_RMID:从系统中立即删除该信号量集合。删除后其他进程在下次试图对此信号量集合进行操作时,将出错返回 EIDRM。该命令也只能由上面提及的两种进程执行。
* GETVAL:返回成员 semnum 的 semval 值。
* SETVAL:设置成员 semnum 的 semval 值为 arg.val 指定的值。
* GETPID:返回成员 semmum 的 sempid 值。
* GETNCNT:返回成员 semnum 的 semncnt 值。
* GETZCNT:返回成员 semnum 的 semzcnt 值。
* GETALL:取该集合中所有的信号量值,并存储在 arg.array 指向的数组中。
* SETALL:将该集合中所有的信号量值设置成 arg.array 指向的数组中的值。
对于除 GETALL 以外的所有 GET 命令,semctl 函数都返回相应值;对于其他命令,若成功则返回 0,否则返回 -1 并设置 errno。
semop 函数中的 semoparray 参数是一个由 sembuf 结构组成的信号量操作数组。nops 参数规定了该数组中操作的数量。对集合中每个成员的操作由 sembuf 结构中相应的 sem_op 值规定,它可以是负值、0 或正值,对各个情况的处理如下所述。
(1)sem_op 为正值:这对应于进程释放的占用的资源数。sem_op 值会加到信号量的值上。如果指定了 undo 标志(该标志对应于相应的 sem_flg 成员的 SEM_UNDO 位),则也从该进程的此信号量调整值中减去 sem_op。
(2)sem_op 为负值:这表示要获取由该信号量控制的资源。如若该信号量的值大于等于 sem_op 的绝对值(说明具有所需的资源),则从信号量值中减去 sem_op 的绝对值。如果指定了 undo 标志,则 sem_op 的绝对值也加到该进程的此信号量调整值上。如若信号量值小于 sem_op 的绝对值,则适用下列条件。
a)若指定了 IPC_NOWAIT,则 semop 函数出错返回 EAGAIN。
b)若未指定 IPC_NOWAIT,则该信号量的 semncnt 值加 1(因为调用进程将进入休眠),然后调用进程被挂起直至发生下列事件之一。
i. 此信号量值变成大于等于 sem_op 的绝对值(即某个进程已释放了某些资源)。此信号量的 semncnt 值减 1,并从信号量值中减去 sem_op 的绝对值。如果指定了 undo 标志,则 sem_op 的绝对值也加到该进程的此信号量调整值上。
ii. 从系统中删除了此信号量,此时 semop 函数出错返回 EIDRM。
iii. 进程捕捉到一个信号,并从信号处理程序返回。此时此信号量的 semncnt 值减 1(因为调用进程不再等待),并且 semop 函数出错返回 EINTR。
(3)sem_op 为 0:这表示调用进程希望等待到该信号量值变成 0。如果当前信号量值是 0,则 semop 函数立即返回。如果当前信号量值非 0,则适用下列条件。
a)若指定了 IPC_NOWAIT,则出错返回 EAGAIN。
b)若未指定 IPC_NOWAIT,则该信号量的 semzcnt 值加 1(因为调用进程将进入休眠),然后调用进程被挂起直至发生下列事件之一。
i. 此信号量值变成 0,此时会将此信号量的 semzcnt 值减 1。
ii. 从系统中删除了此信号量,此时 semop 函数出错返回 EIDRM。
iii. 进程捕捉到一个信号,并从信号处理程序返回。此时此信号量的 semzcnt 值减 1(因为调用进程不再等待),并且 semop 函数出错返回 EINTR。
semop 函数具有原子性,它或者执行数组中的所有操作,或者一个也不做。
前面说过,如果在进程终止时,它占用了经由信号量分配的资源,那么就会成为一个问题。无论何时只要为信号量操作指定了 SEM_UNDO 标志,然后分配资源(sem_op 值小于 0),那么内核就会记住对于该特定信号量,分配给调用进程多少资源(sem_op 的绝对值)。当该进程终止时,内核就会检验该进程是否还有尚未处理的信号量调整值,如果有,则按调整值对相应信号量进行处理。
如果用带有 SETVAL 或 SETALL 命令的 semctl 设置一个信号量的值,则在所有进程中,该信号量的调整值都将设置为 0。
发表评论
-
打开伪终端设备
2018-07-09 20:50 1252在伪终端概述一节中已对 PTY进行了初步的介绍。尽管 ... -
伪终端概述
2018-06-02 11:05 1550伪终端就是指,一个应用程序看上去像一个终端,但事实上它 ... -
终端窗口大小和 termcap
2018-05-29 22:39 800多数 UNIX 系统都提供了一种跟踪当前终端窗口大小的 ... -
终端规范模式和非规范模式
2018-05-29 00:25 950终端规范模式很简单:发一个读请求,当一行已经输入后,终 ... -
终端标识
2018-05-23 11:18 569尽管控制终端的名字在多数 UNIX 系统上都是 /de ... -
波特率和行控制函数
2018-05-22 07:53 944虽然大多数终端设 ... -
终端属性和选项标志
2018-05-20 07:40 709tcgetattr 和 tcsetattr ... -
终端特殊输入字符
2018-05-17 06:33 815终端支持下表所示的特殊输入字符。 为了更改 ... -
终端 I/O 综述
2018-05-10 07:56 438终端设备可认为是由内核中的终端驱动程序控制的。每个终端 ... -
POSIX 信号量
2018-05-09 00:03 579在XSI IPC通信之信 ... -
XSI IPC 通信之共享存储
2018-04-25 07:18 947在XSI IPC通信之消息队列和XSI IPC通信之信 ... -
XSI IPC通信之消息队列
2018-04-15 10:54 497消息队列是消息的链接表,存储在内核中,由消息队列标识符 ... -
XSI IPC 相似特征介绍
2018-02-08 23:48 485有 3 种称作 XSI IPC ... -
IPC 通信之 FIFO
2018-02-06 22:55 421FIFO 也被称为命名管道,未命名的管道只能在两个相关 ... -
IPC 通信之管道
2018-01-30 22:22 389管道是 UNIX 系统 IPC 的最古老但也是最常用的 ... -
readv/writev 函数及存储映射 I/O
2018-01-19 00:57 889readv 和 writev 函数可用于在一次函数调用 ... -
POSIX 异步 I/O
2018-01-16 21:33 455POSIX 异步 I/O 接口为对不同类型的文件进行异 ... -
fcntl 记录锁
2018-01-06 23:48 615记录锁的功能是:当有进程正在读或修改文件的某个部分时, ... -
守护进程惯例
2018-01-06 23:52 439UNIX 系统中,守护进程遵循下列通用惯例。 ... -
守护进程编写规则与出错记录
2017-12-26 01:53 454在编写守护进程程 ...
相关推荐
XSI IPC包括了三种主要机制:消息队列、信号量和共享内存。这些机制允许进程之间进行同步和通信,是多线程和多进程编程中的关键组件。 **1. 消息队列** 消息队列是一种存储消息的数据结构,进程可以将消息发送到...
其中,信号量结合共享存储区(ShareMemory)和消息传递统称为XSI IPC(扩展系统接口进程间通信),提供了一种高效的进程间通信机制。 #### 共享存储与信号量:一种高效IPC方案 文章进一步探讨了信号量与共享存储区...
15. 进程间通信:详细讨论了几种UNIX进程间通信的机制,如管道、协同进程、命名管道(FIFO)、XSIIPC(包括消息队列、共享内存和信号量)以及这些机制在C/S模型中的应用比较。 16. 网络IPC:套接字:最后一章介绍了...
15.8 信号量 15.9 共享存储 15.10 客户进程-服务器进程属性 15.11 小结 习题 第16章 网络IPC:套接字 16.1 引言 16.2 套接字描述符 16.3 寻址 16.3.1 字节序 16.3.2 地址格式 ...
- 包括消息队列、信号量和共享内存等高级IPC机制。 - XSI接口增强了系统的并行处理能力。 **实时系统接口(Real-Time System Interface)** - 实时系统接口为实时应用提供了必要的支持。 - 包括实时调度策略、优先级...
15.8 信号量 422 15.9 共享存储 427 15.10 客户进程-服务器进程属性 432 15.11 小结 434 习题 434 第16章 网络IPC:套接字 437 16.1 引言 437 16.2 套接字描述符 437 16.3 寻址 439 16.3.1 字节序...
415 15.6.2 权限结构 416 15.6.3 结构限制 417 15.6.4 优点和缺点 417 15.7 消息队列 418 15.8 信号量 422 15.9 共享存储 427 15.10 客户进程-服务器进程属性 432 15.11 小结 434 习题 434 第16章...
15.8 信号量422 15.9 共享存储427 15.10 客户进程-服务器进程属性432 15.11 小结434 习题434 第16章网络ipc:套接字437 16.1 引言437 16.2 套接字描述符437 16.3 寻址439 16.3.1 字节序440 16.3.2 地址...
15.8 信号量422 15.9 共享存储427 15.10 客户进程-服务器进程属性432 15.11 小结434 习题434 第16章网络ipc:套接字437 16.1 引言437 16.2 套接字描述符437 16.3 寻址439 16.3.1 字节序440 16.3.2 地址...
15. **`<sys/sem.h>`**:信号量。 16. **`<sys/shm.h>`**:共享内存。 17. **`<sys/time.h>`**:时间处理。 18. **`<sys/timeb.h>`**:时间扩展类型。 19. **`<sys/uio.h>`**:向量I/O操作。 #### 五、POSIX标准...
- `<sys/sem.h>`:信号量 - `<sys/shm.h>`:共享内存 - `<sys/statvfs.h>`:文件系统信息 - `<sys/time.h>`:时间类型 - `<sys/timeb.h>`:附加的日期和时间定义 - `<sys/uio.h>`:矢量I/O操作 - `<aio.h>`:...