前言
本文为笔者学习笔记,若有不妥之处,欢迎斧正。
一、信号量概述
信号量(semaphore)与之前介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
它是一个特殊变量,只允许对它进行等待和发送信号这两种操作
P(信号量变量):等待。如果sv大于0,减小sv。如果sv为0,挂起这个进程的执行。
V(信号量变量sv):发送信号。如果有进程被挂起等待sv,使其恢复执行。如果没有进行被挂起等待sv,增加sv。
信号量通信机制:Linux系统中的信号量接口经过了精心设计,提供了比通常所需更多的机制。所有的Linux信号量函数都是针对成组的通用信号量进行操作,而不只是针对一个二进制信号量。但是在绝大多数情况下,使用一个单个信号量就足够了。
信号量的特点:
- 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
- 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
- 每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
- 支持信号量组。
二、信号量相关API详解
1.创建或者获取一个信号量
函数原型:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
函数作用:创建或者获取一个信号量。
参数说明:
- key:键值(ftok的返回值)
- nsems:创建或获取信号量的个数
- semflg:配置信号量的权限(通常我们使用IPC_CREAT|0666)
返回值:成功返回信号量ID,失败返回-1;
2.改变信号量的值
函数原型:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
struct sembuf{
short sem_num; //除非使用一组信号量,否则它为0
short sem_op; //信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
//一个是+1,即V(发送信号)操作。
short sem_flg; //通常为SEM_UNDO,使操作系统跟踪信号,
//并在进程没有释放该信号量而终止时,操作系统释放信号量
};
函数作用:改变信号量的值。
参数说明:
- semid:信号量ID。
- sops:信号量数组。
- 指定信号量数组中的个数
返回值:成功返回0,失败返回-1。
3.控制信号量
函数原型:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
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) */
};
函数作用:控制信号量,可以用来初始化信号量。
参数说明:
- semid:信号量ID。
- semnum:第几个信号量。
- cmd:使用系统提供的宏定义(初始化使用SETVAL)
- 该函数比较特殊可有三个参数也可由四个参数,第四个参数为一个联合体。
返回值:成功返回非0值,失败返回-1;
三、代码演示
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
union semu
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *_buf;
};
void pGetKey(int semid)
{
struct sembuf sops;
sops.sem_num = 0;
sops.sem_op = -1;
sops.sem_flg = SEM_UNDO;
semop(semid,&sops,1);
printf("get key\n");
}
void vPutBack(int semid)
{
struct sembuf sops;
sops.sem_num = 0;
sops.sem_op = 1;
sops.sem_flg = SEM_UNDO;
semop(semid,&sops,1);
printf("put back\n");
}
int main(void)
{
key_t key;
union semu tem;
pid_t pid;
tem.val = 0;
key = ftok(".",3);
if(key == -1)
{
perror("fok failed:");
exit(-1);
}
int semid = semget(key,1,IPC_CREAT|0666);
if(semid == -1)
{
perror("semget failed:");
exit(-1);
}
int err = semctl(semid,0,SETVAL,tem);
if(err == -1)
{
perror("semctl failed:");
exit(-1);
}
if((pid = fork()) == -1)
{
perror("fork failed:");
exit(-1);
}
if(pid > 0)
{
pGetKey(semid);
printf("this is father,pid=%d\n",getpid());
vPutBack(semid);
semctl(semid,0,IPC_RMID);
}
else if(pid == 0)
{
printf("thus is son,pid=%d\n",getpid());
vPutBack(semid);
}
return 0;
}
运行结果如下:
thus is son,pid=18589
put back
get key
this is father,pid=18588
put back