SEMCTL - Linux手册页
Linux程序员手册 第2部分
更新日期: 2020-04-11
名称
semctl-System V信号灯控制操作
语法
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semctl(int semid, int semnum, int cmd, ...);
说明
semctl()在由semid标识的System V信号量集或该集合的第semnum信号量上执行cmd指定的控制操作。 (一组中的信号灯从0开始编号。)
此函数具有三个或四个参数,具体取决于cmd。当有四个时,第四个具有union semun类型。调用程序必须按以下方式定义此并集:
union semun { int val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ unsigned short *array; /* Array for GETALL, SETALL */ struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */ };
semid_ds数据结构定义如下:
struct semid_ds { struct ipc_perm sem_perm; /* Ownership and permissions */ time_t sem_otime; /* Last semop time */ time_t sem_ctime; /* Creation time/time of last modification via semctl() */ unsigned long sem_nsems; /* No. of semaphores in set */ };
The fields of the
semid_ds
structure are as follows:
- sem_perm
- 这是一个ipc_perm结构(请参见下文),用于指定对信号集的访问权限。
- sem_otime
- 上次semop(2)系统调用的时间。
- sem_ctime
- 创建信号集的时间或上次semctl()IPCSET,SETVAL或SETALL操作的时间。
- sem_nsems
- 集合中的信号灯数量。集合中的每个信号量都由一个从0到sem_nsems-1的非负整数引用。
ipc_perm结构定义如下(突出显示的字段可使用IPC_SET设置):
struct ipc_perm { key_t __key; /* Key supplied to semget(2) */ uid_t uid; /* Effective UID of owner */ gid_t gid; /* Effective GID of owner */ uid_t cuid; /* Effective UID of creator */ gid_t cgid; /* Effective GID of creator */ unsigned short mode; /* Permissions */ unsigned short __seq; /* Sequence number */ };
ipc_perm结构的mode字段的最低9位定义了共享内存段的访问权限。权限位如下:
0400 | Read by user |
0200 | Write by user |
0040 | Read by group |
0020 | Write by group |
0004 | Read by others |
0002 | Write by others |
实际上,"写"是指信号集的"更改"。系统未使用位0100、0010和0001(执行位)。
cmd的有效值为:
- IPC_STAT
- 将信息从与Semid相关联的内核数据结构复制到arg.buf指向的semid_ds结构中。参数semnum被忽略。调用过程必须对信号集具有读取权限。
- IPC_SET
- 将arg.buf指向的semid_ds结构的某些成员的值写入与此信号量集关联的内核数据结构,同时还更新其sem_ctime成员。该结构的以下成员已更新:sem_perm.uid,sem_perm.gid和sem_perm.mode(其最低9位)。调用过程的有效UID必须与信号量集的所有者(sem_perm.uid)或创建者(sem_perm.cuid)相匹配,否则调用者必须具有特权。参数semnum被忽略。
- IPC_RMID
- 立即删除信号量集,唤醒在该集上的semop(2)调用中阻塞的所有进程(错误返回并将errno设置为EIDRM)。调用过程的有效用户ID必须与信号集的创建者或所有者匹配,或者必须为调用者特权。参数semnum被忽略。
- IPC_INFO(Linux-specific)
- 在arg .__ buf指向的结构中返回有关系统范围信号量限制和参数的信息。如果定义了_GNU_SOURCE功能测试宏,则该结构的类型为seminfo:
struct seminfo { int semmap; /* Number of entries in semaphore map; unused within kernel */ int semmni; /* Maximum number of semaphore sets */ int semmns; /* Maximum number of semaphores in all semaphore sets */ int semmnu; /* System-wide maximum number of undo structures; unused within kernel */ int semmsl; /* Maximum number of semaphores in a set */ int semopm; /* Maximum number of operations for semop(2) */ int semume; /* Maximum number of undo entries per process; unused within kernel */ int semusz; /* Size of struct sem_undo */ int semvmx; /* Maximum semaphore value */ int semaem; /* Max. value that can be recorded for semaphore adjustment (SEM_UNDO) */ };
- 可以通过/ proc / sys / kernel / sem更改semmsl,semmns,semopm和semmni设置;有关详细信息,请参见proc(5)。
- SEM_INFO(Linux-specific)
- 返回一个seminfo结构,该结构包含与IPC_INFO相同的信息,不同之处在于,以下字段返回有关信号量消耗的系统资源的信息:semusz字段返回系统上当前存在的信号量集的数量;信号量字段返回系统上所有信号量集中的信号量总数。
- SEM_STAT(Linux-specific)
- 返回与IPC_STAT相同的semid_ds结构。但是,semid参数不是信号量标识符,而是内核内部数组的索引,该索引维护有关系统上所有信号量集的信息。
- SEM_STAT_ANY(Linux-specific, since Linux 4.17)
- 返回一个seminfo结构,其中包含与SEM_STAT相同的信息。但是,不会检查sem_perm.mode的读取访问权限是否为semid,这意味着任何用户都可以使用此操作(就像任何用户可以读取/ proc / sysvipc / sem以获得相同信息一样)。
- GETALL
- 将集合的所有信号量的semval(即当前值)返回到arg.array中。参数semnum被忽略。调用过程必须对信号集具有读取权限。
- GETNCNT
- 返回集合中第semnum个信号量的semncnt值(即,等待该信号量值增加的进程数)。调用过程必须对信号集具有读取权限。
- GETPID
- 返回集合的第semnum个信号量的sempid值。这是上次对该信号量执行操作的过程的PID(但请参阅"注意")。调用过程必须对信号集具有读取权限。
- GETVAL
- 返回集合中第semnum个信号量的semval(即信号量值)。调用过程必须对信号集具有读取权限。
- GETZCNT
- 返回集合中第semnum个信号量的semzcnt值(即,等待该信号量值变为0的进程数)。调用过程必须对信号集具有读取权限。
- SETALL
- 使用arg.array为集合的所有信号量设置semval值,同时还更新与集合关联的semid_ds结构的sem_ctime成员。撤消条目(请参阅semop(2))会在所有进程中针对已更改的信号量进行清除。如果对信号量值的更改将允许其他进程中阻塞的semop(2)调用继续进行,则这些进程将被唤醒。参数semnum被忽略。调用过程必须对信号集具有更改(写入)权限。
- SETVAL
- 将集合的第semnum信号量的信号量值(semval)设置为arg.val,同时还更新与该集合关联的semid_ds结构的sem_ctime成员。在所有过程中,都会针对更改的信号量清除撤消条目。如果对信号量值的更改将允许其他进程中阻塞的semop(2)调用继续进行,则这些进程将被唤醒。调用过程必须对信号集具有更改权限。
返回值
失败时,semctl()返回-1,并带有errno指示错误。
否则,系统调用将根据cmd返回非负值,如下所示:
- GETNCNT
- semncnt的值。
- GETPID
- sempid的价值。
- GETVAL
- semval的值。
- GETZCNT
- semzcnt的值。
- IPC_INFO
- 内核内部数组中使用最高的条目的索引,该条目记录有关所有信号量集的信息。 (此信息可以与重复的SEM_STAT或SEM_STAT_ANY操作一起使用,以获得有关系统上所有信号量集的信息。)
- SEM_INFO
- 至于IPC_INFO。
- SEM_STAT
- 信号量集的标识符,其索引以Semid给出。
- SEM_STAT_ANY
- 至于SEM_STAT。
所有其他cmd值成功均返回0。
错误说明
失败时,errno将设置为以下之一:
- EACCES
- 参数cmd具有值GETALL,GETPID,GETVAL,GETNCNT,GETZCNT,IPC_STAT,SEM_STAT,SEM_STAT_ANY,SETALL或SETVAL之一,并且调用过程对信号集没有必需的权限,并且不具有CAP_IPC_OWNER功能控制其IPC名称空间的用户名称空间。
- EFAULT
- 无法访问arg.buf或arg.array指向的地址。
- EIDRM
- 信号量集已删除。
- EINVAL
- cmd或semid的值无效。或:对于SEM_STAT操作,以Semid指定的索引值引用当前未使用的数组插槽。
- EPERM
- 参数cmd的值为IPC_SET或IPC_RMID,但是调用过程的有效用户ID不是信号量集的创建者(如sem_perm.cuid中所找到)或所有者(如sem_perm.uid中所见),并且该过程确实不具备CAP_SYS_ADMIN功能。
- ERANGE
- 参数cmd具有值SETALL或SETVAL,并且要设置semval的值(对于该集合的某些信号量)小于0或大于实现限制SEMVMX。
遵循规范
POSIX.1-2001,POSIX.1-2008,SVr4。
POSIX.1将semid_ds结构的sem_nsems字段指定为unsigned short类型,并且该字段在大多数其他系统上已定义。在Linux 2.2和更早版本上也是如此定义,但是自Linux 2.4起,该字段的类型为unsigned long。
备注
在Linux或任何版本的POSIX上,都不需要包含和。但是,一些旧的实现需要包含这些头文件,并且SVID也记录了这些头文件。打算移植到这样的旧系统的应用程序可能需要包含这些头文件。
ipcs(1)程序使用IPC_INFO,SEM_STAT和SEM_INFO操作来提供有关已分配资源的信息。将来,这些内容可能会修改或移至/ proc文件系统接口。
struct semid_ds结构中的各个字段在Linux 2.2下的类型为short,而在Linux 2.4下的类型为long。要利用此优势,在glibc-2.1.91或更高版本下进行重新编译就足够了。 (内核通过cmd中的IPC_64标志区分新旧呼叫。)
在glibc的某些早期版本中,semun联合是在中定义的,但是POSIX.1要求调用者定义该联合。在未定义此并集的glibc版本中,_SEM_SEMUN_UNDEFINED宏在中定义。
信号量集的以下系统限制会影响semctl()调用:
- SEMVMX
- semval的最大值:与实现有关(32767)。
为了提高可移植性,最好始终使用四个参数调用semctl()。
The sempid value
POSIX.1将sempid定义为信号量上的" [最后一个操作的进程ID]",并明确指出此值是由成功的semop(2)调用设置的,这意味着没有其他接口会影响sempid值。
虽然某些实现符合POSIX.1中指定的行为,但其他实现则不符合。 (这里的错误可能是POSIX.1所致,因为它可能无法捕获现有实现行为的全部范围。)各种其他实现也为其他更新信号量值的操作更新了sempid:SETVAL和SETALL操作,以及由于使用SEM_UNDO标志而在进程终止时执行的信号量调整(请参见semop(2))。
Linux还更新了用于SETVAL操作和信号量调整的sempid。但是,在Linux 4.5之前(包括Linux 4.5)中,内核并没有为SETALL操作更新sempid。这已在Linux 4.6中得到纠正。
示例
请参见shmop(2)。
另外参见
ipc(2),semget(2),semop(2),功能(7),sem_overview(7),sysvipc(7)
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。