1.什么是信号量:
信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。
信号量的值为正的时候,说明它空闲。所测试的线程可以锁定而使用它。若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒。
信号量是一种特殊的变量,访问具有原子性。
只允许对它进行两个操作:
1)等待信号量
当信号量值为0时,程序等待;当信号量值大于0时,信号量减1,程序继续运行。
2)发送信号量
将信号量值加1。
讲个比喻:比如有一个房间,门上有一把锁,这个房间同时只允许一个人进去,那么开锁的钥匙,我们就称为信号量,如果同时有很多把钥匙和锁,那么那些钥匙我们叫信号量集,房间叫做临界资源。
2.信号量的api:
semget
原型:
int semget(key_t key,int num_sems,int sem_flags);
第一个参数就是信号量的键值用ftok()获取,第二个是信号量集中信号量的个数,第三个参数就是两个宏:
IPC_CREATE和IPC_EXCL,
IPC_CREATE表示若信号量已存在,返回该信号量标识符。
IPC_EXCL表示若信号量已存在,返回错误。
一般我们选IPC_CREATE
示例:
semget(key,1,IPC_CREAT|0666);//0666权限可读可写
semop
原型:
int semop(int sem_id,struct sembuf *sem_opa,size_t num_sem_ops);
我们在调用p操作和v操作时使用这个函数,p操作就是取钥匙,v操作是放钥匙
第一个参数是semget的返回值,即信号量的ID,第二个是一个结构体 sembuf,用来设定信号量的变化以及初值,第三个是有几个信号量
示例:
void pgetkey(int id)
{
struct sembuf set;
set.sem_num=0;
set.sem_op=-1;
set.sem_flg=SEM_UNDO;
semop(id,&set,1);
}
semclt
原型:
int semctl(int sem_id,int sem_num,int command,[union semun sem_union]);
第一个参数是semget返回值,即信号量ID,第二个是操作第几个信号量,从第0个开始,第三个是控制命令有两个值SETVAL,IPC_RMID,分别表示初始化和删除信号量,第四个参数是联合体需要我们配置
示例:
union semun{
int val;
struct seemid_ds *buf;
unsigned short *array;
struct seminfo *_buf;
};
int main()
{
union semun initsem;
semctl(semid,0,SETVAL,initsem);
initsem.val=0;
return 0;
}
应用场景,在父进程创建子进程后,我们一般要控制其先后,sleep(),wait()命令都可以实现,今天我们用信号量来控制实现先运行子进程,再运行父进程,原理是,一开始没有信号量,父进程拿不到信号量无法执行,只能由子进程执行完毕后获得信号量再执行父进程
示例代码:
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
union semun{
int val;
struct seemid_ds *buf;
unsigned short *array;
struct seminfo *_buf;
};
void pgetkey(int id)
{
struct sembuf set;
set.sem_num=0;
set.sem_op=-1;
set.sem_flg=SEM_UNDO;
semop(id,&set,1);
printf("get key\n");
}
void vputbackkey(int id)
{
struct sembuf set;
set.sem_num=0;
set.sem_op=1;
set.sem_flg=SEM_UNDO;
semop(id,&set,1);
printf("put back the key\n");
}
int main(int argc,char *argv[])
{
key_t key;
int semid;
key=ftok(".",2);
semid=semget(key,1,IPC_CREAT|0666);
union semun initsem;
initsem.val=0;
semctl(semid,0,SETVAL,initsem);
printf("there is no key\n");
int pid=fork();
if(pid>0){
pgetkey(semid);
printf("this is father\n");
vputbackkey(semid);
semctl(semid,0,IPC_RMID);
}
else if(pid==0){
printf("this is child\n");
vputbackkey(semid);
}
else{
printf("fork error\n");
}
return 0;
}
运行结果:
以上就是关于Linux中信号量的介绍,尚有不足之处,请各位大师指正。
salute CLC