semop() - Unix、Linux系统调用
广告
名称semop, semtimedop - 信号量操作概要
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
|
int semop(int semid, struct sembuf *sops, unsigned nsops);
int semtimedop(int semid, struct sembuf *sops, unsigned nsops, struct timespec *timeout); 描述信号量集中每个信号量都有以下关联值
unsigned short semval; /* semaphore value */
unsigned short semzcnt; /* # waiting for zero */
unsigned short semncnt; /* # waiting for increase */
pid_t sempid; /* process that did last op */
|
semop() 对由 semid 指示的集合中选定的信号量执行操作。由 sops 指向的数组中的每个 nsops 元素都指定要对单个信号量执行的操作。此结构的元素的类型为 struct sembuf,包含以下成员
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
|
sem_flg 中识别的标志为 IPC_NOWAIT 和 SEM_UNDO。如果操作指定 SEM_UNDO,则在进程终止时会自动撤消该操作。包含在 sops 中的操作集是原子执行的,也就是说,这些操作同时执行,并且只有在它们都能同时执行的情况下才能执行。如果并非所有操作都能立即执行,则系统调用的行为取决于 IPC_NOWAIT 标志在各个 sem_flg 字段中的存在情况,如下所述。 每个操作都在信号量集的第 sem_num 个信号量上执行,其中集合的第一个信号量的编号为 0。根据 sem_op 的值,有三种类型的操作。 如果 sem_op 是一个正整数,则该操作会将此值添加到信号量值 (semval)。此外,如果为此操作指定了 SEM_UNDO,则系统会更新此信号量的进程撤消计数 (semadj)。此操作始终可以继续进行 — 它永远不会强制进程等待。调用进程必须对信号量集具有更改权限。 如果 sem_op 为零,则该进程必须对信号量集具有读取权限。这是一个“等待为零”操作:如果 semval 为零,则该操作可以立即继续。否则,如果在 sem_flg 中指定了 IPC_NOWAIT,则 semop() 失败,并将 errno 设置为 EAGAIN(并且不执行 sops 中的任何操作)。否则,semzcnt(直到此信号量值变为零为止的等待进程计数)将递增一,并且进程将休眠,直到发生以下情况之一:
标签 | 描述 |
o |
semval 变为 0,此时 semzcnt 的值将递减。 |
o | 信号量集被删除:semop() 失败,并将 errno 设置为 EIDRM。 |
o | 调用进程捕获信号:semzcnt 的值将递减,并且 semop() 失败,并将 errno 设置为 EINTR。 |
o | 在 semtimedop() 调用中指定的 timeout 时间限制过期:semop() 失败,并将 errno 设置为 EAGAIN。 |
如果 sem_op 小于零,则该进程必须对信号量集具有更改权限。如果 semval 大于或等于 sem_op 的绝对值,则该操作可以立即继续:sem_op 的绝对值将从 semval 中减去,并且,如果为此操作指定了 SEM_UNDO,则系统会更新此信号量的进程撤消计数 (semadj)。如果 sem_op 的绝对值大于 semval,并且在 sem_flg 中指定了 IPC_NOWAIT,则 semop() 失败,并将 errno 设置为 EAGAIN(并且不执行 sops 中的任何操作)。否则,semncnt(等待此信号量值增加的进程计数器)将递增一,并且进程将休眠,直到发生以下情况之一: |
o |
semval 变得大于或等于 sem_op 的绝对值,此时 semncnt 的值将递减,sem_op 的绝对值将从 semval 中减去,并且,如果为此操作指定了 SEM_UNDO,则系统会更新此信号量的进程撤消计数 (semadj)。 |
o | 信号量集从系统中删除:semop() 失败,并将 errno 设置为 EIDRM。 |
o | 调用进程捕获信号:semncnt 的值将递减,并且 semop() 失败,并将 errno 设置为 EINTR。 |
o | 在 semtimedop() 调用中指定的 timeout 时间限制过期:系统调用失败,并将 errno 设置为 EAGAIN。 |
成功完成时,将为由 sops 指向的数组中指定的每个信号量设置 sempid 值,以指示调用进程的进程 ID。此外,sem_otime 将设置为当前时间。
semtimedop() 的行为与 semop() 完全相同,只是在调用进程将休眠的情况下,休眠的持续时间受由 timespec 结构指定的已用时间量限制,其地址已传递到 timeout 参数中。如果已达到指定的时间限制,则 semtimedop() 失败,并将 errno 设置为 EAGAIN(并且不执行 sops 中的任何操作)。如果 timeout 参数为 NULL,则 semtimedop() 的行为与 semop() 完全相同。 返回值如果成功,semop() 和 semtimedop() 返回 0;否则,它们返回 -1,其中 errno 指示错误。错误失败时,errno 将设置为以下值之一:
标签 | 描述 |
E2BIG | 参数 nsops 大于 SEMOPM(每次系统调用允许的最大操作数)。 |
EACCES | 调用进程不具有执行指定的信号量操作所需的权限,并且不具有 CAP_IPC_OWNER 功能。 |
EAGAIN | 操作无法立即继续,并且在 sem_flg 中指定了 IPC_NOWAIT 或 timeout 中指定的时间限制已过期。 |
EFAULT | sops 或 timeout 参数中指定的地址不可访问。 |
EFBIG | 对于某些操作,sem_num 的值小于 0 或大于或等于集合中信号量的数量。 |
EIDRM | 信号量集已被删除。 |
EINTR | 在此系统调用中阻塞时,进程捕获了信号。 |
EINVAL | 信号量集不存在,或者 semid 小于零,或者 nsops 的值为非正数。 |
ENOMEM | 某些操作的 sem_flg 指定了 SEM_UNDO,并且系统没有足够的内存来分配撤消结构。 |
ERANGE | 对于某些操作,sem_op+semval 大于 SEMVMX(semval 的实现相关的最大值)。 |
备注进程的 sem_undo 结构不会在 fork(2) 系统调用中继承,但会在 execve(2) 系统调用中继承。
无论在建立信号处理程序时 SA_RESTART 标志的设置如何,semop() 都不会在被信号处理程序中断后自动重新启动。
semadj 是一个每个进程的整数,它只是指定了 SEM_UNDO 标志的所有信号量操作的(负)计数。当使用 semctl(2) 对 SETVAL 或 SETALL 请求直接设置信号量的值时,所有进程中相应的 semadj 值都将被清除。 可以使用适当的 semctl(2) 调用检索信号量的 semval、sempid、semzcnt 和 semnct 值。 以下对信号量集资源的限制会影响 semop() 调用:
标签 | 描述 |
SEMOPM | 一次 semop() 调用允许的最大操作数 (32)(在 Linux 上,此限制可以通过 /proc/sys/kernel/sem 的第三个字段读取和修改)。 |
SEMVMX | semval 的最大允许值:实现相关 (32767)。 |
该实现对退出最大调整值 (SEMAEM)、系统范围内的最大撤消结构数 (SEMMNU) 和每个进程的最大撤消条目系统参数没有固有限制。
semtimedop() 首次出现在 Linux 2.5.52 中,随后被反向移植到内核 2.4.22 中。 错误当进程终止时,其关联的 semadj 结构集将用于撤消使用 SEM_UNDO 标志执行的所有信号量操作的效果。这会引发一个难题:如果这些信号量调整中的一个(或多个)会导致尝试将信号量的值降低到零以下,则实现应该怎么做?一种可能的方法是阻塞,直到所有信号量调整都能执行。但这却是不理想的,因为它可能会强制进程终止长时间阻塞。另一种可能性是完全忽略这些信号量调整(有点类似于在为信号量操作指定 IPC_NOWAIT 时失败)。Linux 采用第三种方法:尽可能降低信号量值(即降至零),并允许进程终止立即继续。在内核 2.6.x(x <= 10)中,存在一个错误,在某些情况下,它会阻止等待信号量值变为零的进程在值实际变为零时被唤醒。此错误已在内核 2.6.11 中修复。 符合标准SVr4、POSIX.1-2001。参见
广告
|