信号量
? 信号量是一个计数器,可以用来控制多个线程对共享资源的访问.它不是用于交换大批数据,而用于多线程之间的同步.它常作为一种锁机制,防止某进程在访问资源时其它进程也访问该资源.因此,主要作为进程间以及同一个进程内不同线程之间的同步手段[1].
? 简单的可以认为信号量就是一个数字, semctl
函数用来直接控制信号量信息. semop
函数可以增大或者减小这个数字. 如果减小后的数字小于0, 那么线程或进程阻塞直到减小后的数字大于等于0.
? 信号量与互斥锁的不同: 1. 只有持有锁的线程才能释放锁, 而信号量的值的修改可以在任何不同的线程中进行; 2. 互斥锁最多只能被一个线程持有, 效果与值为1信号量(好像又叫做二元信号量吧)一样. 而信号量的值可以被多个线程减小.
相关接口
使用这些接口之前需要先引入这些头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
-
semget
: 创建一组新信号量或取得一组已有信号量
//key : 通过ftok()获取
//nsems : 这一组信号量中有多少个信号量
//semflg : 一组标记, IPC_CREATE | 0644
//返回值: 成功返回信号量集的id. 失败返回-1
int semget(key_t key, int nsems, int semflg);
-
semctl
: 函数用来直接控制信号量信息
//semid : 信号量集的id
//semnum : 信号量集中的第几个信号量, 从0开始
//cmd : 对信号量进行的操作
//是传第四个参数有cmd决定的
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) */
};
从共用体semun
的定义就可看出哪些cmd需要传递 第四个参数(注释大写的内同) . 用的比较多的是SETVAL
用来设置信号量的值.
-
semop
: 改变信号量的值
//semid: 信号量集的id
//sops: 结构体数组
//nsops: 数组长度
int semop(int semid, struct sembuf *sops, size_t nsops);
struct sembuf{
unsigned short sem_num; /* semaphore number */ //信号量集中的第几个信号量
short sem_op; /* semaphore operation */ //信号量的值改变量, 整数时信号量的值增大, 负数时减小
short sem_flg; /* operation flags *///SEM_UNDO: 进程结束后自动恢复该进程对信号量修改
};
简单的代码实例
//创建有n个信号量的信号量集, 并返回信号量集的id
int creat_sem(const char *path, int proj_id, int nsems){
int sem_id;
key_t key = ftok(path, proj_id);
if ((sem_id = semget(key, nsems, IPC_CREAT | 0644)) < 0) {
return -1;
}
return sem_id;
}
//为sem_id的信号量集中每个信号量设置值
int init_sem(int sem_id, int nsems, int value){
for (int i = 0; i < nsems; ++i) {
union semun arg;
arg.val = value;
int ret = semctl(sem_id,i,SETVAL, arg);
if (ret < 0) {
return -1;
}
}
return 0;
}
//对信号量集中第sem_num+1个信号量的值减一
int P(int sem_id, int sem_num){
struct sembuf sbuf;
sbuf.sem_num = sem_num;
sbuf.sem_op = -1;
sbuf.sem_flg = SEM_UNDO;
if (semop(sem_id, &sbuf, 1) < 0) {
return -1;
}
return 0;
}
//对信号量集中第sem_num+1个信号量的值加一
int V(int sem_id, int sem_num){
struct sembuf sbuf;
sbuf.sem_num = sem_num;
sbuf.sem_op = 1;
sbuf.sem_flg = SEM_UNDO;
if (semop(sem_id, &sbuf, 1) < 0) {
return -1;
}
return 0;
}
参考文章:
[1]https://www.linuxprobe.com/linux-process-method.html?ivk_sa=1024320u