Linux系统信号量控制
实现代码
#include <bits/stdc++.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/wait.h>
using namespace std;
const int TIMES = 5;
const int DELAY = 5;
union Semaphore {
int value;
semid_ds *buffer;
unsigned short *array;
seminfo *infomation;
};
static int semaphoreID = 0;
static bool initializeSemaphoreValue() {
Semaphore semaphoreUnion;
semaphoreUnion.value = 1;
return semctl(semaphoreID, 0, SETVAL, semaphoreUnion) == -1 ? false : true;
}
static bool semaphoreP() {
sembuf semaphoreBuffer;
semaphoreBuffer.sem_num = 0;
semaphoreBuffer.sem_op = -1;
semaphoreBuffer.sem_flg = SEM_UNDO;
return semop(semaphoreID, &semaphoreBuffer, 1) == -1 ? false : true;
}
static bool semaphoreV() {
sembuf semaphoreBuffer;
semaphoreBuffer.sem_num = 0;
semaphoreBuffer.sem_op = 1;
semaphoreBuffer.sem_flg = SEM_UNDO;
return semop(semaphoreID, &semaphoreBuffer, 1) == -1 ? false : true;
}
static void deleteSemaphoreSet() {
Semaphore semaphoreUnion;
if (semctl(semaphoreID, 0, IPC_RMID, semaphoreUnion) == -1) {
fprintf(stderr, "Failed to delete semaphore\n");
}
}
int main(int argc, char const *argv[])
{
if ((semaphoreID = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT)) == -1) {
fprintf(stderr, "Create semaphore set failed!\n");
abort();
}
if (!initializeSemaphoreValue()) {
fprintf(stderr, "Initialize semaphore failed!\n");
abort();
}
pid_t pid = fork();
string message;
switch (pid) {
case -1:
perror("Fork fail!\n");
abort();
case 0:
message = "Child";
break;
default:
message = "Parent";
break;
}
srand((unsigned)getpid());
for (int i = 0; i < TIMES; i++) {
if (!semaphoreP()) {
printf("Semaphore P failed!\n");
abort();
}
cout << message <<endl;
fflush(stdout);
sleep(rand() % DELAY);
if (!semaphoreV()) {
printf("Semaphore V failed!\n");
abort();
}
}
if (pid > 0) {
wait(NULL);
deleteSemaphoreSet();
}
printf("PID %d finished!\n", getpid());
return 0;
}
相关函数
semctl()
- 函数原型
int semctl(int semid,int semnum,int cmd, /*union semun arg*/);
- 参数
系统调用semctl()的第一个参数是信号量集IPC标识符。第二个参数是操作信号在信号集中的编号,第一个信号的编号是0
根据参数cmd选择功能
参数arg代表一个semun的实例。semun是在linux/sem.h中定义的:
/arg for semctl systemcalls./
union semun{
int val;/value for SETVAL/
struct semid_ds *buf;/buffer for IPC_STAT&IPC_SET/
ushort *array;/array for GETALL&SETALL/
struct seminfo *__buf;/buffer for IPC_INFO/
void *__pad;
val当执行SETVAL命令时使用。buf在IPC_STAT/IPC_SET命令中使用。代表了内核中使用的信号量的数据结构。array在使用GETALL/SETALL命令时使用的指针。
- 返回值
若成功,则为一个正数,否则为-1
- 功能
参数cmd中可以使用的命令如下:
·IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
·IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
·IPC_RMID将信号量集从内存中删除。
·GETALL用于读取信号量集中的所有信号量的值。
·GETNCNT返回正在等待资源的进程数目。
·GETPID返回最后一个执行semop操作的进程的PID。
·GETVAL返回信号量集中的一个单个的信号量的值。
·GETZCNT返回正在等待完全空闲的资源的进程数目。
·SETALL设置信号量集中的所有的信号量的值。
·SETVAL设置信号量集中的一个单独的信号量的值。
semop()
- 函数原型
int semopi(nt semid,struct sembuf *sops,size_t nsops);
- 参数
semid:信号集的识别码,可通过semget获取。
sops:指向存储信号操作结构的数组指针,信号操作结构的原型如下
struct sembuf
{
unsigned short sem_num; /* semaphore number /
short sem_op; / semaphore operation /
short sem_flg; / operation flags */
};
这三个字段的意义分别为:
sem_num:操作信号在信号集中的编号,第一个信号的编号是0。
sem_op:如果其值为正数,该值会加到现有的信号内含值中。通常用于释放所控资源的使用权;如果sem_op的值为负数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值。通常用于获取资源的使用权;如果sem_op的值为0,如果没有设置IPC_NOWAIT,则调用该操作的进程或者线程将暂时睡眠,直到信号量的值为0;否则,进程或者线程不会睡眠,函数返回错误EAGAIN。
sem_flg:信号操作标志,可能的选择有两种
IPC_NOWAIT //对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误信息。
SEM_UNDO //程序结束时(不论正常或不正常),保证信号值会被重设为semop()调用前的值。这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。
nsops:信号操作结构的数量,恒大于或等于1。
timeout:当semtimedop()调用致使进程进入睡眠时,睡眠时间不能超过本参数指定的值。如果睡眠超时,semtimedop()将失败返回,并设定错误值为EAGAIN。如果本参数的值为NULL,semtimedop()将永远睡眠等待。
- 功能
操作一个或一组信号
semget()
- 函数原型
int semget(key_t key,int nsems,int semflg);
- 参数
key:所创建或打开信号量集的键值。
nsems:创建的信号量集中的信号量的个数,该参数只在创建信号量集时有效。
semflg:调用函数的操作类型,也可用于设置信号量集的访问权限,两者通过or表示
- 返回值
若成功,则返回信号量集的IPC标识符,否则返回-1
- 功能
建立信号量集
输出结果
Parent
Child
Parent
Child
Parent
Child
Parent
Child
Parent
Child
PID 15250 finished!
PID 15249 finished!
注意
若注释掉main函数中的semaphoreV()函数和semaphoreP()函数,则会不能保证输出结果的正确,由于父进程和子进程相互竞争输出结果,导致输出序列的混乱,信号量机制保证父进程和子进程交替进行,得到想要的结果
Parent
Child
Parent
Parent
Parent
Parent
Child
Child
Child
Child
PID 19265 finished!
PID 19264 finished!
鸣谢
最后
- 由于博主水平有限,不免有疏漏之处,欢迎读者随时批评指正,以免造成不必要的误解!