Linux的信号量机制
#include<sys/sem.h>
int semctl(int sem_id , int sem_num , int command , ...);
int semget(key_t key , int num_set , int sem_flags);
int semop(int sem_id struct sembuf *sem_ops , size_t num_sem_ops);
头文件sys/sem.h通常依赖于另外两个有文件sys/types.h和sys/ipc.h。一般情况下,他们会被sys/sem.h自动包含,因此不需要为它们明确添加相应的#include语句。
参数key的作用很像一个文件名,它代表程序可能要使用的某个资源,如果多个程序使用相同的key值,它将负责协调工作。
1.semget函数
semget函数的作用是创建一个新信号量或取得一个已有信号量的键:
int semget(key_t key , int num_set , int sem_flags);
第一个参数是整数值,不相关的进程可以通过它访问同一个信号量。只有semget函数才直接使用信号量键,所有其他的信号量函数都是使用有semget函数返回的信号量标识符。
num_sems指定需要的信号量的数目,它几乎总是取值为1。
sem_flags参数是一组标志,它与open函数的标志非常相识。
2.semop函数
semop函数用于改变信号量的值,它的定义如下所示:
int semop(int sem_id , struct sembuf *sem_ops , size_t num_sem_ops);
第一个参数sem_id是由semget返回的信号量标识符。第二个参数sem_ops是指向一个结构数组的指针,每个数组元素至少包含一下几个成员:
struct sembuf
{
short sem_num;
short sem_op;
short sem_flg;
}
第一个成员sem_num是信号量编写,除非你需要使用一组信号量,否则它的取值一般是0。
sem_op成员的值是信号量在一次操作中需要改变的数值。通常只会用到两个值,一个是-1,也就是P操作,它等待信号量变为可用。另外一个是V操作,它是+1,它发送信号表示信号量现在可用。
最后一个成员sem_flg通常被设置为SEM_UNDO。它将是的操作系统跟踪当前进程对这个信号量的修改情况。如果这个进程在没有释放该信号量的情况下终止,操作系统将自动释放该进程持有的信号量。
3.semctl函数
int semctl(int sem_id , int sem_num , int command , ...);
第一个参数sem_id是由semget返回的信号量标识符。sem_num参数是信号量编写,当需要用到成组的信号量时,就要用到这个参数,它一般取值为0,表示这是第一个唯一的一个信号量。command参数是将要采取的动作。如果还有第四个参数,它将会是一个union semun结构,它至少包含一下几个成员:
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
}
semctl函数中的command参数可以设置许多不同的值,但只有下面介绍的两个值最常用。
SETVAL:用来把信号量初始化为一个已知的值。这个用过union semun中的val成员设置。
IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。
/* After the #includes, the function prototypes and the global variable, we come to the
main function. There the semaphore is created with a call to semget, which returns the
semaphore ID. If the program is the first to be called (i.e. it's called with a parameter
and argc > 1), a call is made to set_semvalue to initialize the semaphore and op_char is
set to X. */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/sem.h>
#include "semun.h"
static int set_semvalue(void);
static void del_semvalue(void);
static int semaphore_p(void);
static int semaphore_v(void);
static int sem_id;
int main(int argc, char *argv[])
{
int i;
int pause_time;
char op_char = 'O';
srand((unsigned int)getpid());
sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
if (argc > 1) {
if (!set_semvalue()) {
fprintf(stderr, "Failed to initialize semaphore\n");
exit(EXIT_FAILURE);
}
op_char = 'X';
sleep(2);
}
/* Then we have a loop which enters and leaves the critical section ten times.
There, we first make a call to semaphore_p which sets the semaphore to wait, as
this program is about to enter the critical section. */
for(i = 0; i < 10; i++) {
if (!semaphore_p()) exit(EXIT_FAILURE);
printf("%c", op_char);fflush(stdout);
pause_time = rand() % 3;
sleep(pause_time);
printf("%c", op_char);fflush(stdout);
/* After the critical section, we call semaphore_v, setting the semaphore available,
before going through the for loop again after a random wait. After the loop, the call
to del_semvalue is made to clean up the code. */
if (!semaphore_v()) exit(EXIT_FAILURE);
pause_time = rand() % 2;
sleep(pause_time);
}
printf("\n%d - finished\n", getpid());
if (argc > 1) {
sleep(10);
del_semvalue();
}
exit(EXIT_SUCCESS);
}
/* The function set_semvalue initializes the semaphore using the SETVAL command in a
semctl call. We need to do this before we can use the semaphore. */
static int set_semvalue(void)
{
union semun sem_union;
sem_union.val = 1;
if (semctl(sem_id, 0, SETVAL, sem_union) == -1) return(0);
return(1);
}
/* The del_semvalue function has almost the same form, except the call to semctl uses
the command IPC_RMID to remove the semaphore's ID. */
static void del_semvalue(void)
{
union semun sem_union;
if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
fprintf(stderr, "Failed to delete semaphore\n");
}
/* semaphore_p changes the semaphore by -1 (waiting). */
static int semaphore_p(void)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1; /* P() */
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1) {
fprintf(stderr, "semaphore_p failed\n");
return(0);
}
return(1);
}
/* semaphore_v is similar except for setting the sem_op part of the sembuf structure to 1,
so that the semaphore becomes available. */
static int semaphore_v(void)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1; /* V() */
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1) {
fprintf(stderr, "semaphore_v failed\n");
return(0);
}
return(1);
}