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 中包含的操作集以原子方式执行,也就是说,这些操作同时执行,并且只有在所有操作都可以同时执行时才执行。如果并非所有操作都可以立即执行,则系统调用的行为取决于 sem_flg 的各个字段中是否存在 IPC_NOWAIT 标志,如下所示。 每个操作都对信号量集的第 sem_num 个信号量执行,其中信号量的第一个编号为 0。有三种类型的操作,由 sem_op 的值区分。 如果 sem_op 是正整数,则此操作会将此值添加到信号量值 (semval)。此外,如果为此操作指定了 SEM_UNDO,则系统会更新此信号量的进程撤消计数 (semadj)。此操作始终可以继续进行 - 它永远不会强制进程等待。调用进程必须对信号量集具有更改权限。 如果 sem_op 为零,则进程必须对信号量集具有读取权限。这是一个“等待为零”操作:如果 semval 为零,则操作可以立即继续进行。否则,如果在 sem_flg 中指定了 IPC_NOWAIT,则 semop() 失败,并设置 errno 为 EAGAIN(并且不执行 sops 中的任何操作)。否则,semzcnt(等待此信号量值变为零的进程计数)加 1,并且进程休眠,直到发生以下情况之一
标签 | 描述 |
o |
semval 变为 0,此时 semzcnt 的值减 1。 |
o | 信号量集被移除:semop() 失败,并设置 errno 为 EIDRM。 |
o | 调用进程捕获信号:semzcnt 的值减 1,并且 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(等待此信号量值增加的进程计数器)加 1,并且进程休眠,直到发生以下情况之一 |
o |
semval 变为大于或等于 sem_op 的绝对值,此时 semncnt 的值减 1,sem_op 的绝对值从 semval 中减去,并且,如果为此操作指定了 SEM_UNDO,则系统会更新此信号量的进程撤消计数 (semadj)。 |
o | 信号量集从系统中移除:semop() 失败,并设置 errno 为 EIDRM。 |
o | 调用进程捕获信号:semncnt 的值减 1,并且 semop() 失败,并设置 errno 为 EINTR。 |
o | semtimedop() 调用中指定的 timeout 时间限制到期:系统调用失败,并设置 errno 为 EAGAIN。 |
成功完成后,由 sops 指向的数组中指定的每个信号量的 sempid 值将设置为调用进程的进程 ID。此外,sem_otime 将设置为当前时间。
semtimedop() 的行为与 semop() 完全相同,只是在调用进程将休眠的情况下,休眠的持续时间受 timeout 参数中传递的 timespec 结构指定的经过时间量的限制。如果指定的期限已到,则 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) 系统调用中继承。
semop() 在被信号处理程序中断后永远不会自动重新启动,无论在建立信号处理程序时 SA_RESTART 标志的设置如何。
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。参见
广告
|