信号量集

2015-05-16 01:26:38   最后更新: 2015-08-12 18:13:09   访问数量:1271




信号量与其他的 IPC (管道、FIFO、消息队列、域套接字)都有所不同,他是一个计数器,用于多进程对共享数据对象的访问

当进程需要获得共享资源时,需要进行下列操作:

 

 

当然了,对信号量的测试及减 1 的操作必须是原子操作,因此,通常信号量是内核实现的

 

最常用的信号量初始值为 1,被称为“二元信号量”或“双态信号量”,控制单个资源,但是一般而言,信号量的初始值可以为任意正数,用来说明有多少个共享资源单位可供共享应用

 

XSI 的信号量集要比信号量复杂一些:

  1. 信号量集是一个含有一个或多个信号量的集合
  2. 创建信号量集与其赋初值要分开进行,这个特性容易造成一些问题
  3. 如果进程没有释放信号量集,即使进程已经退出,信号量集仍然存在(其他 XSI IPC 也有同样的问题)

 

内核为每个信号量集设置了一个 semid_ds 结构:

struct semid_ds { struct ipc_perm sem_perm; // 信号量集权限结构 unsigned short sem_nsems; // 信号量集中的信号量数目 time_t sem_otime; // 上次操作时间 time_t sem_ctime; // 上次改变时间 ... ... }

 

 

各系统的具体实现会包含额外的字段

 

每个信号量由一个无名结构表示,至少包含下列成员:

struct { unsigned short semval; // 信号量的值 pid_t sempid; // 最后操作该信号量的进程 ID unsigned short semncnt; // 因为信号量值过大而等待的进程数 unsigned short semzcnt; // 因为信号量值等于 0 而等待的进程数 ... ... }

 

 

int semget(key_t key, int nsems, int flag);

 

定义于 sys/sem.h 中

调用成功返回信号量集 ID,否则返回 -1

 

该函数将 key 变换为信号量集标识符,并返回,与消息队列创建函数 msgget 一样:

  1. 如果 key 取值为 IPC_PRIVATE,则创建新的 IPC 结构
  2. 如果指定的 key 当前未与任何 IPC 结构结合,并且 flag 中指定了 IPC_CREAT 位,则用该 key 创建新的 IPC 结构
  3. 如果指定的 key 当前未与任何 IPC 结构结合,并且 flag 中未指定 IPC_CREAT 位,则函数返回出错
  4. 如果指定的 key 当前已经与 IPC 结构结合,并且 flag 中未指定 IPC_EXECL 位,则返回对应 IPC 结构,否则返回 EEXIST

 

参数 nsems 用于初始化该信号量集描述结构的 sem_nsems 字段

 

semctl 函数可以执行多种操作,与 ioctl、semctl、shmctl 非常类似,都被称为“垃圾桶函数”

 

int semctl(int semid, int semnum, int cmd [, semun arg ]);

 

定义于 sys/sem.h 中

根据 cmd 参数的不同,返回值有所不同

 

可选参数 arg -- semun 类型

第四个参数 arg 是一个联合体 semun 类型的参数,根据 cmd 参数取值的不同而可选

union semun { int val; // 用于 cmd 参数取值为 SETVAL struct semid_ds *buf; // 用于 cmd 参数取值为 IPC_STAT 或 IPC_SET unsigned short *array; // 用于 cmd 参数取值为 GETALL 或 SETALL }

 

 

cmd 参数取值

参数 cmd 决定了 semctl 函数的用途、arg 参数的实际类型与函数的返回值

cmd 参数是下列 10 种命令中的一种:

semctl 函数 cmd 参数取值
取值意义
IPC_STAT将 semid 指定的信号量集的 semid_ds 结构存放到 arg.buf 指向的结构中
IPC_SET按照 arg.buf 指向的结构中的值设置 semid 指定的信号量集的 semid_ds 结构(可以改变 sem_perm.uid、sem_perm.gid、sem_perm.mode),执行此命令的进程有效用户 ID 必须等于 sem_perm.cuid 或 sem_perm.uid 或者该进程具有超级用户权限
IPC_RMID从系统中删除该信号量集,信号量集会被立即删除,仍在使用次信号量集的其他进程对该信号量集的下次操作会返回 EIDRM,执行此命令的进程有效用户 ID 必须等于 sem_perm.cuid 或 sem_perm.uid 或者该进程具有超级用户权限
GETVAL返回信号量集成员 semnum 的 semval 值
SETVAL将信号量集成员 semnum 的 semval 值设定为 arg.val
GETPID返回信号量集成员 semnum 的 sempid 值
GETNCNT返回信号量集成员 semnum 的 semncnt 值
GETZCNT返回信号量集成员 semnum 的 semzcnt 值
GETALL将信号量集中所有信号量的值存放到 arg.array 指向的数组中
SETALL将信号量集设定为 arg.array 数组存储数值信号量的集合

 

函数 semop 执行信号量集上的操作,即本文开头处图片中的过程,是一个原子操作

int semop(int semid, struct sembuf semoparray[], size_t nops);

 

定义于 sys/sem.h

调用成功返回 0,否则返回 -1

 

参数说明 -- sembuf 结构数组 semoparray

参数 semoparray 是一个信号量操作数组,标识对每个信号量的操作

struct sembuf { unsigned short sem_num; // 该操作对应信号量集中信号量编号 short sem_op; // 指定对信号量的操作 int sem_flg; // 信号量操作标志,可选 IPC_NOWAIT、SEM_UNDO }

 

 

函数说明

对集合中成员的具体操作由 semoparray 中存储的元素的 sem_op 字段指定

 

 

上述操作是原子性的

 






读书笔记      技术帖      linux      unix      龙潭书斋      apue      unix环境高级编程      进程间通信      进程      线程      ipc            线程同步      死锁      xsi      信号量      原子操作      信号量集      xsi ipc      资源      semid_ds      semop      semget      semctl     


京ICP备15018585号