信号量
信号量用于实现对共享资源的同步访问。
信号量工作原理:假设有两个进程proc1与proc2,这两个进程会在他们执行的某一时刻排他的访问一个数据库。我们定义一个单一的二值信号量,sv,初始值为1并且可以为两个进程所访问。两个进程然后需要执行同样的处理来访问临界区代码。
这两个进程共享sv信号量变量。一旦一个进程已经执行P(sv)操作,这个进程就可以获得信号量并且进入临界区。第二个进程就会被阻止进行临界区,因为当他尝试执行P(sv)时,他就会等待,直到第一个进程离开临界区并且执行V(sv)操作来释放信号量。
所需要的过程如下:
semaphore sv = 1;
loop forever {
P(sv);
critical code section;
V(sv);
noncritical code section;
}
信号量的操作较复杂,定义几个函数来实现, 使主程序结构清晰。提高可读性。
1.初始化函数:set_semvalue
在semctl调用中使用SETVAL命令来初始化信号量。
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;
}
2.semaphore_p函数将信号量减1(获取信号量)
static int semaphore_p(void)
{
struct sembuf sem_b;
sem_b.sem_num = 0; //信号量集中该信号量的编号,只有一个,故为0
sem_b.sem_op = -1; // P操作,获取信号量
sem_b.sem_flag = SEM_UNDO; //系统自动释放残余信号量
if(semop(sem_id, &sem_b, 1) == -1) //最后参数1:规定操作的数量
{
fprintf(stderr, "semaphore_p failed\n");
return 0;
}
return 1;
}
3. semaphore_v函数将sembuf结构的sem_op部分设置为1,从而信号量变得可用。
static int semaphore_v(void)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flag = SEM_UNDO;
if(semop(sem_id, &sem_b, 1) == -1)
{
fprintf(stderr, "semaphore_v failed\n");
return 0;
}
return 1;
}
4. del_semvalue函数几乎具有相同的格式,所不同的是semctl调用使用IPC_RMID命令来移除信号量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");
}
代码:
#include <sys/types.h> #include <sys/msg.h> #include <sys/shm.h> #include <sys/sem.h> #include <unistd.h> #include<errno.h> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<sys/ipc.h> #include<signal.h>
static int init_semvalue(int sem_id); static void del_semvalue(int sem_id); static int semaphore_p(int sem_id); static int semaphore_v(int sem_id); union semun { int val; struct semid_ds *buf; unsigned short *array; }; // This union does not exist in kernel 2.6, define it
main(void) { int sem_id; int child;
if((sem_id = semget((key_t)1234, 1, IPC_CREAT|0770)) < 0) //get the semaphore
{ printf("semget error:%s\n", strerror(errno)); exit(1); } init_semvalue(sem_id); if((child = fork()) < 0) { printf("fork error:%s\n", strerror(errno)); exit(1); } if(child > 0) { semaphore_p(sem_id); printf("father: getpid=%d\n", getpid()); sleep(3); printf("father:child value=%d\n", child); semaphore_v(sem_id); del_semvalue(sem_id); } else { semaphore_p(sem_id); printf("child:getpid=%d\n", getpid()); sleep(2); printf("child :child=%d\n",child); semaphore_v(sem_id); // del_semvalue(sem_id);
} }
static int init_semvalue(int sem_id) { union semun sem_union; sem_union.val = 1; if(semctl(sem_id, 0, SETVAL, sem_union) == -1) { printf("semctl error\n"); exit(1); } return 0; }
static int semaphore_v(int sem_id) { struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = 1; sem_b.sem_flg = SEM_UNDO; if(semop(sem_id, &sem_b, 1) == -1) { printf("operation_v error:%s\n", strerror(errno)); exit(1); } return 0; }
static int semaphore_p(int sem_id) { struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = -1; sem_b.sem_flg = SEM_UNDO; if(semop(sem_id, &sem_b, 1) < 0) { printf("operation_p error:%s\n", strerror(errno)); exit(1); } return 0; } static void del_semvalue(int sem_id) { union semun sem_union; if(semctl(sem_id, 0, IPC_RMID, sem_union) < 0) { printf("semctl error:%s\n", strerror(errno)); exit(1); } }
|
易错点:
1. if((sem_id = semget((key_t)1234, 1, IPC_CREAT|0770)) < 0) //get the semaphore
IPC_CREAT标记会使得如果需要的时候创建一个信号量。
2.union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
}; // This union does not exist in kernel 2.6, define it
该结构在2.6内核中背删除,要添加
3. init_semvalue(sem_id);
最初用的是init_semaphore,编译出现错误:对 ‘init_semaphore’ 的静态声明出现在非静态声明之后, 原因可能是:init_semaphore是个内部函数,换个函数名即可。
4. 在后执行的进程中删除信号量,否则出错!
阅读(3315) | 评论(1) | 转发(0) |