内核维护的计数器,用于管理多进程之间共享资源。
例如:有个变量n表示资源的数量,当有个进程想要独占一个资源时,n的值要减1(可能减多个),如果n的值等于0(不够减),则进程阻塞,直到n的值可以减会被再次唤醒,当资源使用完毕后n要加1(可能加多个)。
用到的函数:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
Int semget(key_t key, int nsems, int semflg);
功能:创建/获取信号量
key: IPC键值,由ftok函数生成
nsems:信号量的数量
semflg:
0 | 获取信号量 |
IPC_CREAT | 创建信号量(存在则获取,不存在则创建) |
IPC_EXCL | 如果已经存在则创建失败 |
返回值:信号量标识
int semop(int semid, struct sembuf *sops, unsigned nsops);
功能:操作信号量(对信号量进行加减操作)
semid:信号量标识,semget的返回值
sops:结构体数组
struct sembuf
{
unsigned short sem_num; 信号量的下标 /* semaphore number */
short sem_op; 操作 /* semaphore operation */
short sem_flg; 标记: /* operation flags */
sem_flg:
IPC_NOWAIT :信号量不够减的时候不阻塞
SEM_UNDO :当进程结束时,信号量的值自动归还
}
nsops:数组的长度
Int semtimedop(int semid, struct sembuf *sops, unsigned nsops, struct timespec *timeout);
功能:带时间限制的操作信号量
struct timespec
{
__time_t tv_sec; 秒
long int tv_nsec; 纳秒
}
Int semctl(int semid, int semnum, int cmd, ...);
功能:初始化信号量的值/删除信号量/获取、设置信号量的属性
cmd:
IPC_STAT:获取信号量的属性
IPC_SET:设置信号量的属性
struct ipc_perm
{
key_t __key; /* Key supplied to semget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions */
unsigned short __seq; /* Sequence number */
};
IPC_RMID :删除信号量
IPC_INFO :获取信号量的信息
struct seminfo
{
int semmap; /* Number of entries in semaphore map; unused within kernel */
int semmni; /* Maximum number of semaphore sets */
int semmns; /* Maximum number of semaphores in all semaphore sets */
int semmnu; /* System-wide maximum number of undo structures; unused within kernel */
int semmsl; /* Maximum number of semaphores in a set */
int semopm; /* Maximum number of operations for semop(2) */
int semume; /* Maximum number of undo entries perprocess; unused within kernel */
int semusz; /* Size of struct sem_undo */
int semvmx; /* Maximum semaphore value */
int semaem; /* Max. value that can be recorded forsemaphore adjustment (SEM_UNDO) */
};
SEM_INFO :设置信号量的信息
GETALL :获取所有信号量的值
GETNCNT :获取信号量的数量
GETVAL :获取某个信号量的值
SETALL :设置所有信号量的值
SETVAL :设置某个信号量的值
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) */
};
***编程模型***
进程A | 进程B |
创建信号量 semget | 获取信号量 |
初始化型号量的值 semctl | ... |
加减信号量 semop | 加减信号量 |
删除信号量 semctl | ... |
注意:信号量是用来计数的,一定要与资源对应。
实现代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/wait.h>
int main()
{
//1.创建信号量
int semid = semget(ftok(".",888),1,IPC_CREAT|0644);
if(0 > semid)
{
perror("semget");
return -1;
}
//2. 初始化信号量
if(semctl(semid,0,SETVAL,5))
{
perror("semctl");
return -1;
}
printf("我有5个信号\n");
//3. 创建进程,子进程使用信号量
for(int i = 0 ; i < 10 ; i++)
{
if(0 == fork())
{
//4.使用信号量
struct sembuf buf = {0,-1,0};
semop(semid,&buf,1);
printf("进程:%u,获得1个信号,还剩%d个信号\n",getpid(),semctl(semid,0,GETVAL));
printf("-------------------------------------------\n");
sleep(2);
//5. 归还信号量
buf.sem_op = 1;
semop(semid,&buf,1);
printf("进程:%u,归还1个信号,还剩%d个信号\n",getpid(),semctl(semid,0,GETVAL));
exit(0);
}
}
//删除信号量
while(-1 != wait(NULL))
{
printf("回收子进程\n");
}
semctl(semid,0,IPC_RMID);
return 0;
}