SEMOP - Linux手册页
Linux程序员手册 第2部分
更新日期: 2020-04-11
名称
semop,semtimedop-System V信号量操作
语法
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semop(int semid, struct sembuf *sops, size_t nsops); int semtimedop(int semid, struct sembuf *sops, size_t nsops, const struct timespec *timeout);
glibc的功能测试宏要求(请参阅feature_test_macros(7)):
semtimedop():_GNU_SOURCE
说明
System V信号量集中的每个信号量具有以下关联值:
unsigned short semval; /* semaphore value */ unsigned short semzcnt; /* # waiting for zero */ unsigned short semncnt; /* # waiting for increase */ pid_t sempid; /* PID of process that last
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值。此操作始终可以进行-绝不会强制线程等待。调用过程必须对信号集具有更改权限。
如果sem_op为零,则该进程必须对信号集具有读取权限。这是一个"等待零"操作:如果semval为零,则该操作可以立即进行。否则,如果在sem_flg中指定了IPC_NOWAIT,则semop()会失败,并将errno设置为EAGAIN(并且不会执行sops中的任何操作)。否则,semzcnt(等待该信号量的值变为零的线程数)将增加1,并且线程将休眠,直到发生以下情况之一:
- *
- semval变为0,这时semzcnt的值减小。
- *
- 信号量集已删除:semop()失败,errno设置为EIDRM。
- *
- 调用线程捕获到一个信号:semzcnt的值递减,而semop()失败,errno设置为EINTR。
如果sem_op小于零,则该进程必须对信号量集具有更改权限。如果semval大于或等于sem_op的绝对值,则该操作可以立即进行:从semval中减去sem_op的绝对值,并且,如果为此操作指定了SEM_UNDO,则系统会将sem_op的绝对值添加到此信号量的信号量调整(semadj)值。如果sem_op的绝对值大于semval,并且在sem_flg中指定了IPC_NOWAIT,则semop()会失败,并将errno设置为EAGAIN(并且不会执行sops中的任何操作)。否则,semncnt(等待该信号量的值增加的线程的计数器)将增加1,并且线程将休眠,直到发生以下情况之一:
- *
- semval变得大于或等于sem_op的绝对值:如上所述,现在进行操作。
- *
- 信号量集已从系统中删除:semop()失败,并且errno设置为EIDRM。
- *
- 调用线程捕获到一个信号:semncnt的值递减,并且srnop()失败,errno设置为EINTR。
成功完成后,将sops指向的数组中指定的每个信号量的sempid值设置为调用者的进程ID。另外,sem_otime设置为当前时间。
semtimedop()
semtimedop()的行为与semop()相同,不同之处在于在调用线程将进入睡眠的情况下,该睡眠的持续时间受到timespec结构指定的经过时间的限制,该结构的时间在timeout参数中传递。 (此睡眠间隔将四舍五入为系统时钟的粒度,并且内核调度延迟意味着该间隔可能会少量溢出。)如果已达到指定的时间限制,则semtimedop()会失败,并将errno设置为EAGAIN(并且不会执行任何在sops中的操作)。如果timeout参数为NULL,则semtimedop()的行为与semop()完全相同。
请注意,如果semtimedop()被信号中断,导致调用失败并显示错误EINTR,则超时内容将保持不变。
返回值
如果成功,则semop()和semtimedop()返回0;否则,返回0。否则,它们返回-1,并带有errno指示错误。
错误说明
失败时,errno设置为以下之一:
- E2BIG
- 参数nsops大于SEMOPM,即每个系统调用允许的最大操作数。
- EACCES
- 调用进程没有执行指定的信号量操作所需的权限,并且在控制其IPC名称空间的用户名称空间中不具有CAP_IPC_OWNER功能。
- EAGAIN
- 操作无法立即进行,并且在sem_flg中指定了IPC_NOWAIT,或者在超时中指定了时间限制。
- EFAULT
- 在sops或timeout参数中指定的地址不可访问。
- EFBIG
- 对于某些操作,sem_num的值小于0或大于或等于该集中的信号灯数量。
- EIDRM
- 信号量集已删除。
- EINTR
- 在此系统调用中被阻塞时,线程捕获了一个信号。参见signal(7)。
- EINVAL
- 信号量集不存在,或者semid小于零,或者nsops具有非正值。
- ENOMEM
- 某些操作的sem_flg指定了SEM_UNDO,并且系统没有足够的内存来分配撤消结构。
- ERANGE
- 对于某些操作sem_op + semval大于SEMVMX,取决于实现的semval最大值。
版本
semtimedop()最早出现在Linux 2.5.52中,随后又被移植到内核2.4.22中。 Glibc对semtimedop()的支持最早出现在2.3.3版中。
遵循规范
POSIX.1-2001,POSIX.1-2008,SVr4。
备注
在Linux或任何版本的POSIX上,都不需要包含和。但是,一些旧的实现需要包含这些头文件,并且SVID也记录了这些头文件。打算移植到这样的旧系统的应用程序可能需要包含这些头文件。
进程的sem_undo结构不会被fork(2)生成的子级继承,但是会在execve(2)系统调用中继承。
semop()永远不会在被信号处理程序中断后自动重新启动,无论建立信号处理程序时是否设置SA_RESTART标志。
信号量调整(semadj)值是每个进程,每个信号量的整数,它是对指定SEM_UNDO标志的信号量执行的所有操作的求和。每个进程都有一个semadj值列表-使用SEM_UNDO对其进行操作的每个信号量都有一个值。当进程终止时,每个按信号量的信号量值将添加到相应的信号量,从而消除该进程的操作对信号量的影响(但请参见下面的BUGS)。当使用SETVAL或SETALL请求直接将信号量的值设置为semctl(2)时,将清除所有进程中的相应信号量值。 clone(2)CLONE_SYSVSEM标志允许多个进程共享一个semadj列表。有关详细信息,请参见clone(2)。
信号量的semval,sempid,semzcnt和semnct值都可以使用适当的semctl(2)调用来检索。
Semaphore limits
信号量集资源的以下限制会影响semop()调用:
- SEMOPM
- 一个semop()调用允许的最大操作数。在Linux 3.19之前,此限制的默认值为32。从Linux 3.19开始,此默认值为500。在Linux上,可以通过/ proc / sys / kernel / sem的第三个字段读取和修改此限制。注意:此限制不应提高到1000以上,因为分配内存以复制sops数组时semop()由于内核内存碎片而失败的风险。
- SEMVMX
- semval的最大允许值:与实现有关(32767)。
该实现对于退出最大值的调整(SEMAEM),系统范围的最大撤消结构数(SEMMNU)和每个进程的最大撤消项系统参数数没有固有限制。
BUGS
进程终止时,将使用其关联的semadj结构集来撤消其使用SEM_UNDO标志执行的所有信号量操作的效果。这就带来了一个困难:如果这些信号量调整中的一个(或多个)会导致尝试将信号量的值减小到零以下,那么实现应怎么做?一种可能的方法是阻塞,直到可以执行所有的信号量调整为止。然而,这是不期望的,因为这可能迫使过程终止任意长时间地阻塞。另一种可能性是可以完全忽略此类信号量调整(类似于在为信号量操作指定IPC_NOWAIT时失败)。 Linux采用第三种方法:尽可能减小信号量值(即减小到零),并允许进程立即终止。
在内核2.6.x中,x
示例
以下代码段使用semop()原子地等待信号量0的值变为零,然后将信号量值增加1。
struct sembuf sops[2]; int semid; /* Code to set semid omitted */ sops[0].sem_num = 0; /* Operate on semaphore 0 */ sops[0].sem_op = 0; /* Wait for value to equal 0 */ sops[0].sem_flg = 0; sops[1].sem_num = 0; /* Operate on semaphore 0 */ sops[1].sem_op = 1; /* Increment value by one */ sops[1].sem_flg = 0; if (semop(semid, sops, 2) == -1) { perror("semop"); exit(EXIT_FAILURE); }
可以在shmop(2)中找到使用semop()的另一个示例。
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。