Chinaunix首页 | 论坛 | 博客
  • 博客访问: 304418
  • 博文数量: 47
  • 博客积分: 2026
  • 博客等级: 大尉
  • 技术积分: 1620
  • 用 户 组: 普通用户
  • 注册时间: 2007-12-25 09:34
文章分类

全部博文(47)

文章存档

2009年(8)

2008年(39)

我的朋友

分类: LINUX

2008-11-20 11:46:09

unix或linux实现P,V操作,主要是通过信号等操作来实现。
信号灯的创建和获取通过调用semget函数来实现 例如mysem_id=semget(SEM_KEY,SEMSIZE,IPC_CREAT|0666)),其中SEM_KEY为欲创建或调用的信号灯的键值,可以自己定义  比如#define  SEM_KEY 7589    ,SEMSIZE 为信号等的数量,semget实际是得到一个信号灯数组,这个数组的长度可以自己定义 #define SEMSIZE 5  这样就得到了一个信号灯数量为5的信号灯数组。创建或取得信号等数组后,可以对各个信号灯进行赋值。一下为赋值函数
/**
 * s_id 第s_id个信号灯
 * ini_value  初始值
 */
void init_mysemvalue(int s_id,int ini_value)
{
    key_t    key;
    int i;
    struct sembuf    op;
    union semun        arg;
//    op.sem_num    =    s_id;
    op.sem_op    =    1;
    op.sem_flg    =    0;
//    key=ftok(PATHNAME,'O');
    if(mysem_id == -1){
        if((mysem_id=semget(SEM_KEY,SEMSIZE,IPC_CREAT|0666))==-1){
                perror("semget err!\n");
                exit(-1);
        }
    }
    /*arg.array = (unsigned short *) calloc(SEMSIZE,sizeof(unsigned short));
    if(arg.array == NULL){
        printf("No enough memory to calloc!\n");
        exit(-1);
    }
    arg.array[s_id]    =    ini_value;*/
    arg.val = ini_value;
    if(semctl(mysem_id,s_id,SETVAL,arg) == -1){
        perror("semctl err:");
        exit(-1);
    }
}
其中 union semun 的定义为
union semun{
        int     val;
        struct semid_ds *buf;
        unsigned short *array;
};
取得指定的信号等的当前值
int  getmysemvalue(int s_id)
{
    int i_value;
    if(mysem_id == -1){/*如果没有创建或者获取到信号灯的句柄,要先获取句柄*/
        if((mysem_id=semget(SEM_KEY,SEMSIZE,IPC_CREAT|0666))==-1){
                perror("semget err!\n");
                exit(-1);
        }
    }
    i_value = semctl(mysem_id,s_id,GETVAL);
    if(i_value == -1){
        perror("semctl err:");
        exit(-1);
    }
    return i_value;
}

mywait用于对信号等进可回滚的P操作,可回滚的意思是,当对信号灯进行P操作的进程因为某些意外或者有意识的退出(死亡)后,信号等将自动恢复到操作前的状态,例如某个进程因为做了P操作阻塞了,在阻塞过程中被人人为的kill掉,该进程死后,系统讲自动的对该信号量做一次V操作以保持平衡,这样的好处是在进行互斥操作的多进程程序不会因为某个进程只做P操作而未做V操作就退出后,发生死锁行为。
void mywait(int s_id)
{
    struct sembuf    op;
    int    i_result;
    op.sem_num    =    s_id;
    op.sem_op    =    -1;
    op.sem_flg    =    SEM_UNDO;
    if(mysem_id == -1)
        init_mysem(0);
    while((i_result = semop(mysem_id,&op,1)) == -1 && errno == EINTR) usleep(5000);
    if(i_result==-1){
                        printf("mywait sid = %d errno = %d   %s\n",s_id,errno,strerror(errno));
            if(errno == ERANGE){
                                printf("semval = %d\n",getmysemvalue(s_id));
                        }
            perror("semop err!\n");
            exit(-1);
    }
}

mysignal对信号灯进行可回滚的V操作,回滚的好处和刚才的mywait一样
void mysignal(int s_id)
{
    struct sembuf    op;
    int i_result;
    op.sem_num    =    s_id;
    op.sem_op    =    1;
    op.sem_flg    =    SEM_UNDO;
    if(mysem_id == -1)
        init_mysem(0);
    while((i_result = semop(mysem_id,&op,1)) == -1 && errno == EINTR) usleep(5000);
    if(i_result==-1){
                        printf("mysignal s_id = %d  errno = %d   %s\n",s_id,errno,strerror(errno));
            if(errno == ERANGE){
                printf("semval = %d\n",getmysemvalue(s_id));
            }
            perror("semop err!\n");
            exit(-1);
    }
}



myuwait 是对信号灯进行不可回滚的P操作,使用起来要各位小心。否则容易发生死锁
void myuwait(int s_id)
{
    struct sembuf    op;
    int i_result;
    op.sem_num    =    s_id;
    op.sem_op    =    -1;
    op.sem_flg    =    0;
    if(mysem_id == -1)
        init_mysem(0);
    while((i_result = semop(mysem_id,&op,1)) == -1 && errno == EINTR) usleep(5000);
    if(i_result==-1){
                printf("myuwait s_id = %d  errno = %d   %s\n",s_id,errno,strerror(errno));
            if(errno == ERANGE){
                                printf("semval = %d\n",getmysemvalue(s_id));
                        }
            perror("semop err!\n");
            exit(-1);
    }
}
myusignal是对信号灯进行不可回滚的V操作
void myusignal(int s_id)
{
    struct sembuf    op;
    int i_result;
    op.sem_num    =    s_id;
    op.sem_op    =    1;
    op.sem_flg    =    0;
    if(mysem_id == -1)
        init_mysem(0);
    while((i_result = semop(mysem_id,&op,1)) == -1 && errno == EINTR) usleep(5000);
    if(i_result==-1){
                printf("myusignal s_id = %d  errno = %d   %s\n",s_id,errno,strerror(errno));
            if(errno == ERANGE){
                                printf("semval = %d\n",getmysemvalue(s_id));
                        }
            perror("semop err!\n");
            exit(-1);
    }
}



需要注意的是多某个信号灯变量做可回滚的P操作,就必须使用可回滚的V操作,就是说如果对某个信号灯用mywait操作,就必须使用mysignal做V操作,而不能使用myusignal,并且其他地方要对该信号灯变量做P,V操作,都必须使用mywait  和  mysignal ,mywait  和 myuwait不能对同一个信号等变量混合使用,否则系统会发生不可预测的错误,我的经验是,混合使用到一定时间后,信号等变量的值会突然成一个很大的随机数。

代码范例可到 下载
阅读(1828) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~