Linux内核信号量集用结构体semid_ds结构体表示,semid_ds的结构体定义如下:
/* Data structure describing a set of semaphores. */ struct semid_ds { struct ipc_perm sem_perm; /* operation permission struct */ __time_t sem_otime; /* last semop() time */ __syscall_ulong_t __glibc_reserved1; __time_t sem_ctime; /* last time changed by semctl() */ __syscall_ulong_t __glibc_reserved2; __syscall_ulong_t sem_nsems; /* number of semaphores in set */ __syscall_ulong_t __glibc_reserved3; __syscall_ulong_t __glibc_reserved4; };
每个信号量则描述为:
struct sem{ int semval ; int sempid ; int semcnt ; int semzcnt; };
信号量的基本操作包括创建信号量、信号量的值操作、获取或设置信号量属性,对应的相关函数的分别是semget、semop、semctl。
1.创建信号量集
semget函数用于创建信号量,如果参数key指定的信号量集已经存在,则就返回该信号量集。
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget(key_t key, int nsems, int flag);
key:一个整数类型的键值,用来命名某个特定的信号量集。
nsems:指定打开或者新创建的信号量集包含的信号量数目。
falg:9个位的权限标志。
返回值:成功返回信号量集描述字,否则返回-1。
2.信号量值操作
信号量本质上是一个计数器,进程可以使用函数semop来增加或者减少信号量值,以表示释放或者申请共享资源。
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semop(int sem_id, struct sembuf * sops, unsigned int nsops);
sem_id:semget函数返回的信号量集描述字。
nsops:本次操作的信号量数目,也是sops指向的数组的大小。
sops:指向一个类型为sembuf的结构体数组。
sembuf结构体:
/* Structure used for argument to `semop' to describe operations. */ struct sembuf { unsigned short int sem_num; /* semaphore number */ short int sem_op; /* semaphore operation */ short int sem_flg; /* operation flag */ };
如果sem_op为负数,就从信号量值中减去sem_op的绝对值,表示进程获取资源;如果sem_op为正数,就把它加到信号量上,表示归还资源;如果sem_op为0,则调用进程睡眠,直到信号量值为0。sem_flag一般设置为0。
3.获取或者设置信号量属性
系统中的每个信号量集都对应一个struct sem_ds结构体,该结构体记录信号量集的各种信息,存放于内核空间。为了设置、获取信号量集的各种信息及属性,在用户空间中有一个联合体union semnu与之对应。
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_INF (Linux-specific) */ };
信号量属性操作的函数原型:
#include <linux/sem.h> int semctl(int semid, int semnum, int cmd, union semun arg);
semid:信号量集描述字。
semnum:待操作的信号量在信号集semid中的索引。
cmd:指定具体的操作类型,常见的操作有:
SETVAL:设置semnum所代表信号量的值为arg.val。
SETALL:通过arg.val更新所有信号量的值。
IPC_RMID:从内核主存中删除信号量集。
GETVAL:返回semnum所代表信号量的值。
使用信号量创建自定义P/V操作函数库
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int createsem(key_t key){ return semget(key,1,IPC_CREAT|0666); } int getsem(key_t key){ return semget(key,1,0666); } 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) */ }; int initsem(int semid,int initval){ union semun arg; arg.val = initval; return semctl(semid,0,SETVAL,arg); } int P(int semid){ struct sembuf operation[1]; operation[0].sem_num = 0; operation[0].sem_op = -1; return semop(semid,operation,1); } int V(int semid){ struct sembuf operation[1]; operation[0].sem_num = 0; operation[0].sem_op = 1; return semop(semid,operation,1); } int delsem(int semid){ union semun arg; semctl(semid,0,IPC_RMID,arg); } int main(int argc,char * argv[]){ int semid = createsem(0x123456); int seccess = initsem(semid,1); printf("semid:%d ",semid); printf("seccess:%d ",seccess); // int v = V(semid); // printf("v:%d ",v); int p = P(semid); printf("p:%d ",p); int close = delsem(semid); printf("close:%d ",close); return 0; }