SYSTEM CALL: semctl(); PROTOTYPE: int semctl ( int semid, int semnum, int cmd, union semun arg ); RETURNS: positive integer on success -1 on error: errno = EACCESS (permission denied) EFAULT (invalid address pointed to by arg argument) EIDRM (semaphore set was removed) EINVAL (set doesn't exist, or semid is invalid) EPERM (EUID has no privileges for cmd in arg) ERANGE (semaphore value out of range) NOTES: Performs control operations on a semaphore set
这两个系统调用都使用 cmd 参数来指定要在 IPC 对象上执行的命令。剩余的区别在于这两个调用的最后一个参数。在 msgctl 中,最后一个参数代表内核使用的内部数据结构的副本。回想一下,我们使用此结构来检索有关消息队列的内部信息,以及设置或更改队列的权限和所有权。对于信号量,支持额外的操作命令,因此需要更复杂的数据类型作为最后一个参数。union 的使用使许多新手信号量程序员感到非常困惑。我们将仔细剖析这个结构,以努力防止任何混淆。
的第一个参数semctl()是键值(在我们的例子中是由调用semget返回的)。第二个参数(semun)是操作目标指向的信号量编号。本质上,这可以被认为是信号量集中的一个索引,集合中的第一个(或唯一一个)信号量用值零 (0) 表示。
的cmd参数表示要对集合执行的命令。正如您所看到的,熟悉的 IPC_STAT/IPC_SET 命令以及大量特定于信号量集的额外命令都存在
检索集合的 semid_ds 结构,并将其存储在 semun 联合体中 buf 参数的地址中。
设置集合的 semid_ds 结构的 ipc_perm 成员的值。从 semun 联合体的 buf 参数中获取值。
从内核中移除集合。
用于获取集合中所有信号量的值。整数值存储在联合体的 array 成员指向的无符号短整数数组中。
返回当前等待资源的进程数。
返回执行上次 semop 调用的进程的 PID。
返回集合中单个信号量的值。
返回当前等待 100% 资源利用率的进程数。
使用集合中的值将所有信号量值设置为联合体的 array 成员中包含的匹配值。
将集合中单个信号量的值设置为联合体的 val 成员。
的arg参数表示类型为semun的实例。这个特定的联合体在linux/sem.h中声明如下
/* arg for semctl system calls. */ union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */ ushort *array; /* array for GETALL & SETALL */ struct seminfo *__buf; /* buffer for IPC_INFO */ void *__pad; };
在执行 SETVAL 命令时使用。指定要将信号量设置为何值。
在 IPC_STAT/IPC_SET 命令中使用。表示内核中使用的内部信号量数据结构的副本。
GETALL/SETALL 命令中使用的指针。应指向整数值数组,该数组用于设置或检索集合中的所有信号量值。
其余参数 __buf 和 __pad 在内核中的信号量代码内部使用,对应用程序开发人员几乎没有用处或根本没用。事实上,这两个参数是 Linux 操作系统特有的,在其他 UNIX 实现中找不到。
由于这个特定的系统调用可以说是所有 System V IPC 调用中最难掌握的,我们将检查它的多个实际示例。
以下代码片段返回传递的信号量的值。当使用 GETVAL 命令时,最后一个参数(联合体)将被忽略
int get_sem_val( int sid, int semnum ) { return( semctl(sid, semnum, GETVAL, 0)); }
#define MAX_PRINTERS 5 printer_usage() { int x; for(x=0; x<MAX_PRINTERS; x++) printf("Printer %d: %d\n\r", x, get_sem_val( sid, x )); }
void init_semaphore( int sid, int semnum, int initval) { union semun semopts; semopts.val = initval; semctl( sid, semnum, SETVAL, semopts); }
回想一下 msgtool 项目,IPC_STAT 和 IPC_SET 命令用于更改队列的权限。虽然信号量实现中支持这些命令,但它们的用法略有不同,因为内部数据结构是从联合体的成员中检索和复制的,而不是作为一个单独的实体。您能找到此代码中的错误吗?
/* Required permissions should be passed in as text (ex: "660") */ void changemode(int sid, char *mode) { int rc; struct semid_ds mysemds; /* Get current values for internal data structure */ if((rc = semctl(sid, 0, IPC_STAT, semopts)) == -1) { perror("semctl"); exit(1); } printf("Old permissions were %o\n", semopts.buf->sem_perm.mode); /* Change the permissions on the semaphore */ sscanf(mode, "%o", &semopts.buf->sem_perm.mode); /* Update the internal data structure */ semctl(sid, 0, IPC_SET, semopts); printf("Updated...\n"); }
回想一下,IPC_SET/IPC_STAT 命令使用联合体的 buf 成员,它是指向 semid_ds 类型的指针。指针是指针是指针是指针!buf 成员必须指向某个有效的存储位置,以便我们的函数正常工作。考虑这个修改后的版本
void changemode(int sid, char *mode) { int rc; struct semid_ds mysemds; /* Get current values for internal data structure */ /* Point to our local copy first! */ semopts.buf = &mysemds; /* Let's try this again! */ if((rc = semctl(sid, 0, IPC_STAT, semopts)) == -1) { perror("semctl"); exit(1); } printf("Old permissions were %o\n", semopts.buf->sem_perm.mode); /* Change the permissions on the semaphore */ sscanf(mode, "%o", &semopts.buf->sem_perm.mode); /* Update the internal data structure */ semctl(sid, 0, IPC_SET, semopts); printf("Updated...\n"); }