版本

semtimedop()最早出现在Linux 2.5.52中,随后又被移植到内核2.4.22中。 Glibc对semtimedop()的支持最早出现在2.3.3版中。

SEMOP - Linux手册页

Linux程序员手册 第2部分
更新日期: 2020-04-11

错误说明

失败时,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最大值。

说明

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-System V信号量操作

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/

返回值

如果成功,则semop()和semtimedop()返回0;否则,返回0。否则,它们返回-1,并带有errno指示错误。

遵循规范

POSIX.1-2001,POSIX.1-2008,SVr4。

示例

以下代码段使用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()的另一个示例。

另外参见

克隆(2),semctl(2),semget(2),sigaction(2),功能(7),sem_overview(7),sysvipc(7),time(7)

备注

在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)。

信号量的semvalsempid,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)和每个进程的最大撤消项系统参数数没有固有限制。

语法

#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

BUGS

进程终止时,将使用其关联的semadj结构集来撤消其使用SEM_UNDO标志执行的所有信号量操作的效果。这就带来了一个困难:如果这些信号量调整中的一个(或多个)会导致尝试将信号量的值减小到零以下,那么实现应怎么做?一种可能的方法是阻塞,直到可以执行所有的信号量调整为止。然而,这是不期望的,因为这可能迫使过程终止任意长时间地阻塞。另一种可能性是可以完全忽略此类信号量调整(类似于在为信号量操作指定IPC_NOWAIT时失败)。 Linux采用第三种方法:尽可能减小信号量值(即减小到零),并允许进程立即终止。

在内核2.6.x中,x

日期:2019-08-20 17:59:19 来源:oir作者:oir