常用于进程间同步
API 介绍
- 1 semget
sys/sem.h
int semget(key_t key, int nsems, int flag);
执行成功返回信号量ID, 出错返回-1,同时errno标识错误原因
参数key 通过ftok获得
参数 nsems 是信号量集合中信号量的个数;
在创建新集合时(一般在服务器进程中),必须制定该参数,一旦创建了该信号量,就不能更改其信号量个数,只要不删除该信号量,
即使重新调用该函数创建该键值的信号量,该函数只是返回以前创建的值,不会重新创建;
如果是引用现有集合(客户进程),只是访问而不创建,则可以设置为0;
参数flag 标识该信号量的读写权限
如果key所代表的信号灯已经存在,且semget指定了IPC_CREAT|IPC_EXCL标志,那么即使参数nsems与原来信号灯的数目不 等,返回的也是EEXIST错误;
如果semget只指定了IPC_CREAT标志,那么参数nsems必须与原来的值一致。
- 2 semop
sys/sem.h
int semop(int semid, struct sembuf semoparray[], size_t nops);
函数执行成功返回0, 失败返回-1,errno指示错误类型
参数semid是semget的返回值semid
参数semoparray是一个指针,指向一个有sembuf结构表示的信号量操作数组
参数 nops表示信号量操作数组的长度。
struct sembuf {
unsigned short sem_num;
short sem_op;
short sem_flg;
}
sem_num其实叫做sem_idx更容易理解,表示在信号量数组中的index, 取值范围是[0, nops);
sem_op表示要对信号量执行申请资源或者归还资源的操作;
若sem_op大于0, 信号量加上它的值,相当于进程释放信号量控制的资源,释放资源;
若sem_op小于0,信号量加上它的值,相当于进程申请信号量控制的资源,申请资源,若进程设置IPC_NOWAIT则进程再没有可用资源情况下,进程阻塞,否则直接返回;
若sem_op等于0,表示调用进程希望等到该信号量值变为0,若没有设置IPC_NOWAIT, 那么调用进程将进入睡眠状态,直到信号量的值为0,否则进程直接返回;
sem_flg, 可取IPC_NOWAIT SEM_UNDO这两个值
IPC_NOWAIT,在对信号量的操作不能执行的情况下,该操作立即返回
SEM_UNDO,当操作的进程推出后,该进程对sem进行的操作将被取消,这是比较重要的一个标志位。如果设置了该标志位,那么在进程没有释放共享资源就退 出时,内核将代为释放。如果为一个信号灯设置了该标志,内核都要分配一个sem_undo结构来记录它,为的是确保以后资源能够安全释放。事实上,如果进 程退出了,那么它所占用就释放了,但信号灯值却没有改变,此时,信号灯值反映的已经不是资源占有的实际情况,在这种情况下,问题的解决就靠内核来完成;
不过也看到过这样的解释,sem_flg = 0表示“在对信号量的操作不能执行的情况下,该操作阻塞到可以执行为止”;
- 3 semctl
sys/sem.h
int semctl(int semid, int semnum, int cmd, .../*union semun arg */);
返回值不确定,失败返回-1,成功时的返回值跟具体的cmd有关。
参数semid 信号量id,通过semget获得
参数semnum更应该叫sem_idx表示在信号量数组中的index, 取值范围是[0, nops);
参数cmd表示要进行的操作
IPC_STAT | 获取信号灯信息,信息由arg.buf返回 |
IPC_SET | 设置信号灯信息,待设置信息保存在arg.buf中 |
IPC_RMID | 将信号量集从系统中删除 |
GETVAL | 返回semnum所代表信号灯的值 |
SETVAL | 设置semnum所代表信号灯的值为arg.val |
GETPID | 返回最后一个对semnum所代表信号灯执行semop操作的进程ID |
GETNCNT | 返回等待semnum所代表信号灯的值增加的进程数,相当于目前有多少进程在等待semnum代表的信号灯所代表的共享资源 |
GETZCNT | 返回等待semnum所代表信号灯的值变成0的进程数 |
GETALL | 返回所有信号灯的值,结果保存在arg.array中,参数sennum被忽略 |
SETALL | 通过arg.array更新所有信号灯的值;同时,更新与本信号集相关的semid_ds结构的sem_ctime成员 |
第4个参数根据cmd的不同,设置不同的值。
注意semun的定义要在你的程序中自己定义,不然会报错"stoarget size of xx is unknown"
sample, 实现pv操作
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define SEM_PATH "/home"
#define MAX_ITER 3
/*
* 如果不把semun的定义在此重新定义一下,编译时会报错误
* union semun storage size of xx is unknown
* */
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
static int sem_p(int semid)
{
struct sembuf pv_op;
/*now ask for available resource: p operation */
pv_op.sem_num = 0;
pv_op.sem_op = -1;
pv_op.sem_flg = SEM_UNDO;
if (semop(semid, &pv_op, 1) == -1)
printf("semop ask for resource error\n");
}
static int sem_v(int semid)
{
struct sembuf pv_op;
/*now free resource v operation */
pv_op.sem_num = 0;
pv_op.sem_op = 1;
pv_op.sem_flg = SEM_UNDO;
if (semop(semid, &pv_op, 1) == -1)
if (errno == EIDRM)
printf("the semaphore set was removed\n");
}
int main(int argc, char **argv)
{
int semid;
int i = 0, init_ok = 0, tmp_e = 0;
struct semid_ds sem_info;
union semun arg;
key_t key = ftok(SEM_PATH, 'a');
//create a semaphore set that only includes one semphore.
semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 00666);
if (semid < 0) {
tmp_e = errno;
perror("semget");
if (tmp_e == EEXIST) {
/*
errno is undefined after a successful library call( including perror call)
so it is saved in tmp_e.
*/
/*
只包含了IPC_CREAT标志, 参数nsems(这里为1)必须与原来的信号灯数目一致
*/
semid = semget(key, 1, IPC_CREAT | 00666);
arg.buf = &sem_info;
for (i = 0; i < MAX_ITER; i++) {
if (semctl(semid, 0, IPC_STAT, arg) == -1) {
perror("semctl error");
i = MAX_ITER;
} else {
if (arg.buf->sem_otime != 0) {
i = MAX_ITER;
init_ok = 1;
} else {
sleep(1);
}
}
}
if (!init_ok) {
arg.val = 1;
if (semctl(semid, 0, SETVAL, arg) == -1)
perror("semctl setval error");
}
} else {
perror("semget error, process exit");
exit(1);
}
} else {
arg.val = 1;
if (semctl(semid, 0, SETVAL, arg) == -1)
perror("semctl setval error");
}
/*STAT infomation */
arg.buf = &sem_info;
if (semctl(semid, 0, IPC_STAT, arg) == -1)
printf("semctl IPC STAT");
printf("owner's uid is %d\n", arg.buf->sem_perm.uid);
printf("owner's gid is %d\n", arg.buf->sem_perm.gid);
printf("creater's uid is %d\n", arg.buf->sem_perm.cuid);
printf("creater's gid is %d\n", arg.buf->sem_perm.cgid);
sem_p(semid);
sleep(3);
sem_v(semid);
if (semctl(semid, 0, IPC_RMID) == -1)
perror("semctl IPC_RMID");
else
printf("remove sem ok\n");
}