本质
计数器
作用
保护共享资源
- 互斥
- 同步
信号量用法
- 定义一个唯一key(ftok)
- 构造一个信号量(semget)
- 初始化信号量(semctl SETVA)
- 对信号量进行P/V操作(semop)
- 删除信号量(semctl RMID)
semget函数
功能:获取信号量的ID
函数原型:
int semget(key_t key,int nsems,int semflg)
参数:
- key:信号量键值
- nsems:信号量数量
- semflg:
- IPC_CREATE:信号量不存在则创建
- mode:信号量的权限
返回值:
成功:信号量ID
失败:-1
semctl函数
功能:获取或设置信号量的相关属性
函数原型:
int semctl(int semid,int semnum,int cmd,union semun arg)
参数:
-
semid:信号量ID
-
semnum:信号量编号
-
cmd:
- IPC_STAT:获取信号量的属性信息
- IPC_SET:设置信号量的属性
- IPC_RMID:删除信号量
- IPC_SETVAL:设置信号量的值
-
arg:
union semun
{
int val;
struct semid_ds *buf;
}
返回值:
成功:由cmd类型决定
失败:-1
semop函数
函数原型:
int semop(int semid,struct sembuf *sops,size_t nsops)
参数:
-
semid:信号量ID
-
sops:信号量操作结构体数组
struct sembuf
{
short sem_num; //信号量编号
short sem_op;//信号量P/V操作
short sem_flg; //信号量行为,SEM_UNDO
}
- nsops:信号量数量
返回值:
成功:0
失败:-1
实现代码
sem.h
#ifndef _SEM_H_
#define _SEM_H_
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
};
extern int init_sem(int sem_id, int init_value);
extern int del_sem(int sem_id);
extern int sem_p(int sem_id);
extern int sem_v(int sem_id);
#endif
sem.c
#include <sys/sem.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include "sem.h"
/* 信号量初始化(赋值)函数*/
int init_sem(int sem_id, int init_value)
{
union semun sem_union;
sem_union.val = init_value; /* init_value 为初始值 */
if (semctl(sem_id, 0, SETVAL, sem_union) == -1)
{
perror("Initialize semaphore");
return -1;
}
return 0;
}
/* 从系统中删除信号量的函数 */
int del_sem(int sem_id)
{
union semun sem_union;
if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
{
perror("Delete semaphore");
return -1;
}
}
/* P 操作函数 */
int sem_p(int sem_id)
{
struct sembuf sops;
sops.sem_num = 0; /* 单个信号量的编号应该为 0 */
sops.sem_op = -1; /* 表示 P 操作 */
sops.sem_flg = SEM_UNDO; /* 若进程退出,系统将还原信号量*/
if (semop(sem_id, &sops, 1) == -1)
{
perror("P operation");
return -1;
}
return 0;
}
/* V 操作函数*/
int sem_v(int sem_id)
{
struct sembuf sops;
sops.sem_num = 0; /* 单个信号量的编号应该为 0 */
sops.sem_op = 1; /* 表示 V 操作 */
sops.sem_flg = SEM_UNDO; /* 若进程退出,系统将还原信号量*/
if (semop(sem_id, &sops, 1) == -1)
{
perror("V operation");
return -1;
}
return 0;
}
main.c
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include "sem.h"
#define DELAY_TIME 3 /* 为了突出演示效果,等待几秒钟, */
int main(void)
{
pid_t result;
int sem_id;
sem_id = semget((key_t)6666, 1, 0666 | IPC_CREAT); /* 创建一个信号量*/
init_sem(sem_id, 0);
/*调用 fork()函数*/
result = fork();
if(result == -1)
{
perror("Fork\n");
}
else if (result == 0) /*返回值为 0 代表子进程*/
{
printf("Child process will wait for some seconds...\n");
sleep(DELAY_TIME);
printf("The returned value is %d in the child process(PID = %d)\n",result, getpid());
//到支路进行v+操作,也叫信号量释放,这就保证了子进程执行在父进程前面,也就是同步。
sem_v(sem_id);
}
else /*返回值大于 0 代表父进程*/
{
//信号为o不能进行p-操作,代码执行到这会阻塞,直到子进程释放信号量。
sem_p(sem_id);
printf("The returned value is %d in the father process(PID = %d)\n",result, getpid());
sem_v(sem_id);
del_sem(sem_id);
}
exit(0);
}