Chinaunix首页 | 论坛 | 博客
  • 博客访问: 640686
  • 博文数量: 128
  • 博客积分: 4385
  • 博客等级: 上校
  • 技术积分: 1546
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-22 14:05
文章分类

全部博文(128)

文章存档

2012年(2)

2011年(51)

2010年(75)

分类: LINUX

2010-08-26 20:58:06

信号量
   信号量用于实现对共享资源的同步访问。
信号量工作原理:假设有两个进程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. 在后执行的进程中删除信号量,否则出错!

阅读(3261) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-08-29 15:57:05

Download More than 1000 free IT eBooks: http://free-ebooks.appspot.com