semget函数
创建信号量集
int semget( key_t key, int nsems, int flag);
//such as:
semId = semget(key,1,IPC_CREAT|0666);
if(semId == -1){
perror("semget");
return -1;
}
参数 nsems
是信号量集里面的信号量的个数
参数 flag
是信号量的操作类型以及操纵权限
semop函数
函数semop用以操作一个信号量集,
通常用在PV操作中
int semop( int semid, struct sembuf semoparray[], size_t nops );
/* struct sembuf sops[2];
int semid;
sops[0].sem_num = 0;
sops[0].sem_op = 0;
sops[0].sem_flg = 0;
sops[1].sem_num = 0;
sops[1].sem_op = 1;
sops[1].sem_flg = 0;*/
//such as:
// 二进制信号量(binary semaphore):只允许信号量取0或1值
// 信号量集只有一个信号量
void pKey(int semid)
{
struct sembuf sops;
sops.sem_num = 0;
sops.sem_op = -1;
sops.sem_flg = SEM_UNDO;
semop(semid,&sops,1);
}
void vKey(int semid)
{
struct sembuf sops;
sops.sem_num = 0;
sops.sem_op = 1;
sops.sem_flg = SEM_UNDO;
semop(semid,&sops,1);
}
第一个参数 semid
semget函数的返回值
第二个参数 semoparray[]
是一个struct sembuf结构类型的数组指针
struct sembuf{
unsigned short sem_num;
short sem_op;
short sem_flg;
}
sem_num是相对应的信号量集中的某一个资源,所以其值是一个从0到相应的信号量集的资源总数(ipc_perm.sem_nsems)之间的整数。
sem_op指明所要执行的操作,sem_op的值是一个整数。
sem_flg说明函数semop的行为。
第三个参数 nops
参数nops标明了参数semoparray所指向数组中的元素个数
注意:sem_op 详细解释,其实,看也看不懂
sem_op 正数
释放相应的资源数,将sem_op的值加到信号量的值上
sem_op 0
进程阻塞直到信号量的相应值为0,当信号量已经为0,函数立即返回。如果信号量的值不为0,则依据sem_flg的IPC_NOWAIT位决定函数动作。sem_flg指定IPC_NOWAIT,则semop函数出错返回EAGAIN。sem_flg没有指定IPC_NOWAIT,则将该信号量的semncnt值加1,然后进程挂起直到下述情况发生。信号量值为0,将信号量的semzcnt的值减1,函数semop成功返回;此信号量被删除(只有超级用户或创建用户进程拥有此权限),函数smeop出错返回EIDRM;进程捕捉到信号,并从信号处理函数返回,在此情况将此信号量的semncnt值减1,函数semop出错返回EINTR
sem_op 负数
请求sem_op的绝对值的资源。如果相应的资源数可以满足请求,则将该信号量的值减去sem_op的绝对值,函数成功返回。当相应的资源数不能满足请求时,这个操作与sem_flg有关。sem_flg指定IPC_NOWAIT,则semop函数出错返回EAGAIN。sem_flg没有指定IPC_NOWAIT,则将该信号量的semncnt值加1,然后进程挂起直到下述情况发生:当相应的资源数可以满足请求,该信号的值减去sem_op的绝对值。成功返回;此信号量被删除(只有超级用户或创建用户进程拥有此权限),函数smeop出错返回EIDRM:进程捕捉到信号,并从信号处理函数返回,在此情况将此信号量的semncnt值减1,函数semop出错返回EINTR
semctl函数 执行在信号量集上的控制操作
int semctl(int semid,int semnum,int cmd, /*union semun arg*/);
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) */
};
//需要注意的是,对于semun联合体,最好自己定义,否则GCC编译器可能会报“semun大小未知”
//such as:
semctl(semId,0,SETVAL,initsem);
第一个参数是信号量集IPC标识符。
第二个参数是操作信号在信号集中的编号,第一个信号的编号是0。
第三个参数cmd
·IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
·IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
·IPC_RMID将信号量集从内存中删除。
·GETALL用于读取信号量集中的所有信号量的值。
·GETNCNT返回正在等待资源的进程数目。
·GETPID返回最后一个执行semop操作的进程的PID。
·GETVAL返回信号量集中的一个单个的信号量的值。
·GETZCNT返回正在等待完全空闲的资源的进程数目。
·SETALL设置信号量集中的所有的信号量的值。
·SETVAL设置信号量集中的一个单独的信号量的值。
下面有个简单的例子来使用这些API
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <unistd.h>
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) */
};
void pKey(int semid)
{
struct sembuf sops;
sops.sem_num = 0;
sops.sem_op = -1;
sops.sem_flg = SEM_UNDO;
semop(semid,&sops,1);
}
void vKey(int semid)
{
struct sembuf sops;
sops.sem_num = 0;
sops.sem_op = 1;
sops.sem_flg = SEM_UNDO;
semop(semid,&sops,1);
}
int main(int argc,char**argv)
{
key_t key;
int pid;
int semId;
key = ftok(".",1);
semId = semget(key,1,IPC_CREAT|0666);
union semun initsem;
initsem.val = 0;
semctl(semId,0,SETVAL,initsem);
pid = fork();
if(pid>0){
sleep(3);
printf("this is in father\n");
vKey(semId);
}else if(pid == 0){
pKey(semId);
printf("this is in chird\n");
vKey(semId);
semctl(semId,0,IPC_RMID);
}else{
perror("fork error:");
return -1;
}
return 0;
}