关于linxu信号灯等操作(包括回滚)例子及其说明

本文介绍Unix或Linux系统中如何利用信号量机制实现进程间的P-V操作,包括信号量的创建、初始化、读取及P/V操作的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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不能对同一个信号等变量混合使用,否则系统会发生不可预测的错误,我的经验是,混合使用到一定时间后,信号等变量的值会突然成一个很大的随机数。

代码范例可到 http://download.youkuaiyun.com/source/647035  下载
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值