一、信号量相关函数
1. 创建信号量集
本次实验使用信号量机制来实现进程的同步与互斥,因此首先需要创建信号量集。
使用函数semget可以创建信号量集,它的原型如下:
int semget(key_t key, int nsems, int semflg)
其中,key是创建信号量集的键,nsems是信号量集中信号量量的数量;
semflg是指定的选项及其权限位,包含IPC_CREAT(创建新的信号量集)、IPC_EXCEL(如果信号量集已经存在,则返回错误)等。
这个函数创建的是一个信号量集,其中包含多个信号量,可以通过函数semop来访问信号量集中的某个信号量,或者使用semctl函数来对信号量进行操作,一般创建数据集后都要首先使用semctl对每个信号量设置初始值。semop和semctl函数的介绍在下面。
2. 获取信号量集
一个进程创建号信号量集后,另一个进程想要访问这个信号量集,可以使用semget函数传入KEY值来获取该信号量集的id,该函数原型如下:
int semid = semget(KEY, 0, 0);
上述代码只为获取一个已经存在的信号量集,不需要nsems和semflg,全部为0即可。获取成功会返回信号量集id,如果获取失败的话会返回-1 。
3. 等待、通知信号量集
使用semop函数可以访问一个信号量集,进行获取(P操作)和释放(V操作),其原型如下:
int semop(int semid,struct sembuf *sops,unsigned nsops)
semid是使用semget函数获取到的信号量集的id;
sops是一个sembuf类型的结构,用于描述信号量的操作:等待、通知等,其定义如下:
struct sembuf{
short sem_num; // 要访问的信号量在信号量集中的索引
short sem_op; // 对信号量的操作,为负数是P操作,正数是V操作
short sem_flg; // 操作标志,可以是0或IPC_NOWAIT(非阻塞方式)
}
nsops是指定信号量集中操作的信号量个数。
4. 控制信号量集
要对整个信号量集进行操作可以使用semctl函数,其原型如下:
int semctl(int semid, int semnum, int cmd, union semun arg)
semid是由semget函数返回的信号量集id
semnum是信号量在信号量集中的索引
cmd是控制命令,用于对信号量执行指定的操作,命令包括:
IPC_STAT:获取信号量集合的属性信息,将结果写入指定的结构体中。
IPC_SET:设置信号量集合的属性信息,使用指定的结构体中的值进行设置。
GETVAL:获取指定信号量的值。
SETVAL:设置指定信号量的值。
GETALL: 获取信号量集合中所有信号量的值
SETALL: 设置所有信号量的值
GETPID:获取最后一个执行 semop() 操作的进程 ID。
GETNCNT:获取等待该信号量值增加的进程数。
GETZCNT:获取等待该信号量值变为 0 的进程数。
IPC_RMID:删除信号量集合。
二、简单进程同步
现在使用上面给出的函数编写几个程序实现进程同步。
信号量机制实现进程同步需要使用一些简单的信号量集操作,包括创建信号量集,P操作,V操作,删除信号量集。
1. 创建信号量集
createSem.cpp
#include
#include
#include
#include
using namespace std;
#define KEY 2002
int main(){
int semid = semget(KEY, 1, IPC_CREAT);
semctl(semid, 0, SETVAL, 0);
}
上面代码使用semid函数创建一个只包含一个信号量的信号量集,随后使用semctl将该信号量的值初始化为0 。
2. P操作
p1.cpp
#include
#include
#include
#include
#define KEY 2002
using namespace std;
int main(){
int semid = semget(KEY, 0, 0);
struct sembuf *sops = new sembuf;
sops->sem_num = 0;
sops->sem_op = -1;
sops->sem_flg = 0;
if(semop(semid, sops, 1) == -1){
char err[] = "semop";
perror(err);
exit(1);
}
cout sem_op = 1;
sops->sem_flg = 0;
if(semop(semid, sops, 1) == -1){
char err[] = "semop";
perror(err);
exit(1);
}
return 0;
}
上面代码与p1基本时一样的,只是修改了sem_op,改为1(V操作,信号量加1),执行该程序后,对应信号量会加一,原本阻塞的程序就能结束等待,继续执行下去。
4. 删除信号量集
deletSem.cpp
信号量集使用完毕后需要删除信号量集,使用semctl函数实现,控制命令选择IPC_RMID。
#include
#include
#include
#include
using namespace std;
#define KEY 2002
int main(){
int semid = semget(KEY, 0, 0);
semctl(semid, 0, IPC_RMID, 0);