信号量相当于钥匙,进程A拿钥匙进入临界资源(多个进程都可以进入),B就没有钥匙进去了,只能A出来,B才有钥匙拿进去。
信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
1、特点
- 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
- 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
- 每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
- 支持信号量组。
原子操作:就是在执行某一操作时不被打断。
linux原子操作问题来源于中断、进程的抢占以及多核smp系统中程序的并发执行。
2、原型
最简单的信号量是只能取 0 和 1 的变量,这也是信号量最常见的一种形式,叫做二值信号量(Binary Semaphore)。而可以取多个正整数的信号量被称为通用信号量。
Linux 下的信号量函数都是在通用的信号量数组上进行操作,而不是在一个单一的二值信号量上进行操作。
1 #include <sys/sem.h>
// 创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1
2 int semget(key_t key, int num_sems, int sem_flags); 创建信号量
参数1 :key
参数2: 信号量集当中的信号量个数 (1)
参数3: (创建/获取) IPC_CREAT|0666
返回值:集合id
// 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
3 int semop(int semid, struct sembuf semoparray[], size_t numops);
参数1:semid
参数2: 次参数的数组可以在结构体进行
参数3: SEM_UNDO (默认)t
void getkey(int id){
struct sembuf set;
set.sem_num =0; /* Operate on semaphore 0 */
set.sem_op =-1; / * Wait for value to equal 0 */
set.sem_flg = SEM_UNDO;
semop(id,&set,1);
printf("get the key\n");
}
void backkey(int id){
struct sembuf set;
set.sem_num =0; /* Operate on semaphore 0 */
set.sem_op =1; /* Wait for value to equal 0 */
set.sem_flg = SEM_UNDO;
semop(id,&set,1);
printf("back the key\n");
}
// 控制信号量的相关信息
4 int semctl(int semid, int sem_num, int cmd, ...); 初始化信号量
参数1:返回值信号量集合id
参数2:操作第几个信号量
参数3:(设置信号量的值)SETVAL
参数4: 一个结构体 (结构体的第一个数据提前初始化)
先实现一段代码
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 getkey(int id){
struct sembuf set;
set.sem_num =0; /* Operate on semaphore 0 */
set.sem_op =-1; /* Wait for value to equal 0 */
set.sem_flg = SEM_UNDO;
semop(id,&set,1);
printf("get the key\n");
}
void backkey(int id){
struct sembuf set;
set.sem_num =0; /* Operate on semaphore 0 */
set.sem_op =-1; /* Wait for value to equal 0 */
set.sem_flg = SEM_UNDO;
semop(id,&set,1);
printf("get the key\n");
}
void backkey(int id){
struct sembuf set;
set.sem_num =0; /* Operate on semaphore 0 */
set.sem_op =1; /* Wait for value to equal 0 */
set.sem_flg = SEM_UNDO;
semop(id,&set,1);
printf("back the key\n");
}
int main(){
union semun data;
key_t key;
key=ftok(".",'1');
int semid =semget(key,1,IPC_CREAT|0666); //创建
data.val=0; //这个值关乎信息量钥匙的有与没,拿与放
semctl(semid, 0, SETVAL,data.val); //初始化 信息量
pid_t pid=fork();
if(pid>0){
getkey(semid); //获取钥匙
printf("the is father\n");
backkey(semid); //退出钥匙
}
else if (pid==0){
// getkey(semid);
printf("the is chill\n");
backkey(semid);
}
else{
printf(" open pid error\n");
}
return 0;
}
运行
CLC@Embed_Learn:~/ccc$ ./sig
the is chill
back the key
get the key
the is father
back the key
CLC@Embed_Learn:~/ccc$ vi demo17.c
上面的例子如果不加信号量,则子进程会先执行完毕。这里加了信号量让子进程等待父进程执行完以后再执行。
更详细的代码参考