UNIX多用户系统下信号量操作详解( 二 )


int sid; /*信号量集合标识符*/
int count; /*信号量集合中元素个数*/
ushort *arg; /*命令参数*/

该命令把数组arg中的前count个值依次赋给集合中各信号量,一次可设定多个信号量的初值 。

若用SETVAL命令,其格式为:

int semctl(sid,semnum,SETVAL,arg)
int sid; /*信号量集合标识符*/
int semnum; /*信号量元素编号*/
int arg; /*命令参数*/

该命令将arg的值赋给集合中第semnum个信号量,一次仅能设定一个信号量的初值 。

③查询信号量集合的当前值

查询信号量集合的当前值可用GETALL或GETVAL命令 。若用GETALL命令,其格式为:

int semctl(sid,count,GETALL,arg)
int sid; /*信号量集合标识符*/
int count; /*信号量集合中元素个数*/
ushort *arg; /*命令参数*/

该命令把信号量集合中各信号量的当前值返回到数组arg中 。

若用GETVAL命令,其格式为:

int semctl(sid,semnum,GETVAL,0)
int sid; /*信号量集合标识符*/
int semnum; /*信号量元素编号*/

该命令把集合中第semnum个信号量的当前值作为调用的返回值 。

④查询某个信号量的等待进程数

当一个进程要执行信号量操作时若条件不具备则被阻塞,有关信号量的等待进程数也相应变化 。

通过GETNCNT命令可查询等待信号量增值的进程数,其格式如下:

int semctl(sid,semnum,GETNCNT,0)
int sid; /*信号量集合标识符*/
int semnum; /*信号量元素编号*/

该命令把等待第semnum个信号量增值的进程数作为调用的返回值 。

通过GETZCNT命令可查询等待信号量值为0的进程数,其格式如下:

int semctl(sid,semnum,GETZCNT,0)
int sid; /*信号量集合标识符*/
int semnum; /*信号量元素编号*/

该命令把等待第semnum个信号量值为0的进程数作为调用的返回值 。

至于其它的控制命令,因不常用而不再累述 。

系统调用semop用来对信号量集合中的一个或多个信号量进行操作,操作命令由用户提供的操作结构数组来定义,该结构如下:

struct sembuf{
short sem_num; /*信号量在集合中的下标*/
short sem_op; /*操作值*/
short sem_flg; /*操作标志*/
};

系统从用户地址空间读信号量操作结构数组,并核实信号量下标的合法性及进程是否具备读或修改信号量所必需的权限 。若权限不够则调用失败;若进程必须睡眠,则它将已操作过的信号量恢复为该系统调用开始时的值,然后它就睡眠,直到它等待的事件发生时再重新执行该系统调用 。由于系统将操作数组保存在一个全局数组中,因此若它必须重新执行该调用的话,它必须重新从用户空间读该数组 。这样,操作按原语方式执行--或一次做完或根本不做 。

系统根据操作值来改变信号量的值:①若操作值为正,系统就增加信号量的值并唤醒所有等待信号量增值的进程;②若操作值是0,系统就检查信号量的值:如果为0,就继续数组中的其它操作;否则把等待信号量的值为0的睡眠进程数加1,然后睡眠;③若操作值为负且其绝对值不超过信号量的值,系统就把操作值(一个负数)加到信号量值上,如果结果为0则系统就唤醒所有等待信号量的值为0的睡眠进程;④若信号量的值小于操作值的绝对值,系统就让进程睡眠在"等待信号量增值"这一事件上 。

当进程在信号量操作过程中睡眠时,它睡眠在可中断级上,因此当它接收到软中断信号时就被唤醒了 。用户可在操作标志中设置IPC_NOWAIT标志以防止进程睡眠 。

如果进程执行了一个信号量操作,锁住了某些资源,却没有恢复信号量的值就退出了(如收到kill信号),那么就可能出现危险情况 。为了避免这类问题,用户可在操作标志中设置SEM_UNDO标志 。当进程退出时,系统便撤除该进程做过的每个信号量操作的影响 。

推荐阅读