一、semget系统调用
semget系统调用创建一个新的信号量集,或者获取一个已经存在的信号量集。
key参数是一个键值,用来标识一个全局唯一的信号量集,就像文件名全局唯一的标识一个文件一样。要通过信号量通信的进程需要使用相同的键值来创建或获取该信号量。
num_sems参数指定要创建或获取的信号量集中信号量的数目。如果是创建信号量,则该值必须指定;如果是获取已存在的信号量,则可以设置为0
sem_flags参数指定一组标志。它低端的9个比特是该信号量的权限,其格式和含义都与系统调用open的mode参数相同。
我们可以和IPC_CREAT标志做按位“或”运算,此时即使信号量存大,semget也不会报错。
我们还可以用IPC_CREAT和IPC_EXCL标志来确保创建一组新的、唯一的信号量集,此时若信号量存在,semget返回错误并设置errno为EEXIST。
semget调用成功时返回一个正整数,它是信号量集的标识符;失败时返回-1,并设置errno
二、semop系统调用
semop系统调用改变信号量的值,即执行 P 和 V 操作。
sem_num是信号量集中信号量的编号,像数组一样,从0开始。
sem_op指定操作类型,可选值为正整数、0、负整数。每种类型的操作行为又受到sem_flg的影响。
sem_flg的可选值是IPC_NOWAIT、SEM_UNDO。IPC_NOWAIT指无论信号量操作是否成功,semop调用都将立即返回,类似于非阻塞。SEM_UNDO指当进程退出时取消正在进行的semop操作。
num_sem_ops参数指定要执行的操作个数,即sem_ops数组中无素的个数。semop对数组中的每个成员按数组顺序依次执行,该过程是原子操作。
semop成功时返回0,失败时返回-1并设置errno
三、semctl系统调用
semctl系统调用允许调用者对信号量进行直接控制。
sem_id参数是由semget调用返回的信号量集标识符,用以指定被操作的信号量集。
sem_num参数指定被操作的信号量在信号量集中的编号。
command参数指定要执行的命令。
有的命令需要第4个参数。第4个参数由用户自己定义,但sys/sem.h中给出了推荐格式:
具体参数请自行man。
sem_ctl成功时的返回值取决于command参数,失败时返回-1并设置errno
四、特殊键值 IPC_PRIVATE
semget的调用可以给其key参数传递一个特殊的键值IPC_PRIVATE,其值为0,这样无论该信号量是否存在,semget都将创建一个新的信号量。
使用该值创建的信号量并非像它的名字声称的那样是进程私有的。man手册上说,使用此名字有些误导,是历史原因,应该称为IPC_NEW。
五、代码:使用IPC_PRIVATE创建信号量
semget系统调用创建一个新的信号量集,或者获取一个已经存在的信号量集。
#include <sys/sem.h>
int semget(key_t key, int num_sems, int sem_flags);
key参数是一个键值,用来标识一个全局唯一的信号量集,就像文件名全局唯一的标识一个文件一样。要通过信号量通信的进程需要使用相同的键值来创建或获取该信号量。
num_sems参数指定要创建或获取的信号量集中信号量的数目。如果是创建信号量,则该值必须指定;如果是获取已存在的信号量,则可以设置为0
sem_flags参数指定一组标志。它低端的9个比特是该信号量的权限,其格式和含义都与系统调用open的mode参数相同。
我们可以和IPC_CREAT标志做按位“或”运算,此时即使信号量存大,semget也不会报错。
我们还可以用IPC_CREAT和IPC_EXCL标志来确保创建一组新的、唯一的信号量集,此时若信号量存在,semget返回错误并设置errno为EEXIST。
semget调用成功时返回一个正整数,它是信号量集的标识符;失败时返回-1,并设置errno
二、semop系统调用
semop系统调用改变信号量的值,即执行 P 和 V 操作。
#include <sys/sem.h>
int semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops);
sem_id参数是由semget调用返回的信号量集标识符,用以指定被操作的目标信号量集。
sem_ops参数指向一个sembuf结构体的数组,定义如下:
struct sembuf
{
unsigned short int sem_num;
short int sem_op;
short int sem_flg;
};
sem_num是信号量集中信号量的编号,像数组一样,从0开始。
sem_op指定操作类型,可选值为正整数、0、负整数。每种类型的操作行为又受到sem_flg的影响。
sem_flg的可选值是IPC_NOWAIT、SEM_UNDO。IPC_NOWAIT指无论信号量操作是否成功,semop调用都将立即返回,类似于非阻塞。SEM_UNDO指当进程退出时取消正在进行的semop操作。
num_sem_ops参数指定要执行的操作个数,即sem_ops数组中无素的个数。semop对数组中的每个成员按数组顺序依次执行,该过程是原子操作。
semop成功时返回0,失败时返回-1并设置errno
三、semctl系统调用
semctl系统调用允许调用者对信号量进行直接控制。
#include <sys/sem.h>
int semctl(int sem_id, int sem_num, int command, ...);
sem_id参数是由semget调用返回的信号量集标识符,用以指定被操作的信号量集。
sem_num参数指定被操作的信号量在信号量集中的编号。
command参数指定要执行的命令。
有的命令需要第4个参数。第4个参数由用户自己定义,但sys/sem.h中给出了推荐格式:
union semun
{
int val; //用于SETVAL命令
struct semid_ds *buf; //用于IPC_STAT和IPC_SET命令
unsigned short *array; //用于GETALL和SETALL命令
struct seminfo *__buf; //用于IPC_INFO命令
};
具体参数请自行man。
sem_ctl成功时的返回值取决于command参数,失败时返回-1并设置errno
四、特殊键值 IPC_PRIVATE
semget的调用可以给其key参数传递一个特殊的键值IPC_PRIVATE,其值为0,这样无论该信号量是否存在,semget都将创建一个新的信号量。
使用该值创建的信号量并非像它的名字声称的那样是进程私有的。man手册上说,使用此名字有些误导,是历史原因,应该称为IPC_NEW。
五、代码:使用IPC_PRIVATE创建信号量
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
union semun
{
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
//op为-1时执行P操作, op为1时执行V操作
void pv(int sem_id, int op)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = op;
sem_b.sem_flg = SEM_UNDO;
semop(sem_id, &sem_b, 1);
}
int main(int argc, char *argv[])
{
int sem_id = semget(IPC_PRIVATE, 1, 0666);
union semun sem_un;
sem_un.val = 1;
semctl(sem_id, 0, SETVAL, sem_un);
pid_t id = fork();
if (id < 0) {
fprintf(stderr, "fork failed.\n");
return 1;
}
else if (id == 0) {
printf("child try to get binary sem\n");
pv(sem_id, -1);
printf("child get the sem and would release it after 5 seconds\n");
sleep(5);
pv(sem_id, 1);
exit(0);
}
else{
printf("parent try to get binary sem\n");
pv(sem_id, -1);
printf("parent get the sem and would release it after 3 seconds\n");
sleep(3);
pv(sem_id, 1);
}
waitpid(id, NULL, 0);
//删除信号量
semctl(sem_id, 0, IPC_RMID, sem_un);
return 0;
}
参考:《linux高性能服务器编程》