40-System V——信号量的细节问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_35733751/article/details/82919091

1. 多个进程操作信号量

假设此时有多个进程在请求信号量发生阻塞时,且每个进程请求信号量减去的值都是一样的,那么到底哪个进程能继续执行是不确定的,这取决于调度算法。如果在阻塞的进程中,每个进程请求信号量减去的值都是不一样的,那么则会按照先满足条件的先执行的顺序进行。

例如此时有A和B两个进程阻塞,当前信号量的值为0,A进程请求将信号量值减去2,B进程请求将信号量减去1,当C进程请求将信号量加1时,此时信号量的值只有1,所以A进程并不满足要求,只有B进程满足条件,那么B进程会优先解除阻塞并执行,在某些情况下可能导致A进程一直无法满足条件并永远阻塞等待。

具体可参考semop函数的调用行为:39-System V——信号量第六小节。

 

2. 关于SEM_UNDO选项

如果一个进程在申请完信号量资源之后就终止了,不管是正常终止还是意外终止,默认情况下,信号量的值是不会发生改变的,这将会引发一个问题,如果当前还有进程阻塞,正等待释放信号量资源时,而之前的进程在退出时且没有正常释放信号量资源,那么当前的进程将会永远阻塞在这。

为了解决这个问题,可以在semop函数指定SEM_UNDO选项,然后内核会在进程终止后(无论是正常终止还是非正常终止),自动回收信号量资源。

 

sem1进程

#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

void print_sem(int semid)
{
        //打印信号量
        int ret;
        unsigned short array[1] = {0};
        ret = semctl(semid , 0 , GETALL , array);
        if(ret < 0){
                perror("semtcl error:");
        }
        printf("sem1 = %d\n" , array[0]);
}

int main(void)
{
        int ret;
        int semid = 0;
        key_t key = 0x00112233;

        //创建信号集,1个信号
        semid = semget(key , 1 , IPC_CREAT | IPC_EXCL | 0664);
        if(semid < 0){
                perror("semget error:");
        }

        //设置信号量的值为1
        int val = 1;
        //通过val设置信号量
        ret = semctl(semid , 0 , SETVAL , val);
        if(ret < 0){
                perror("semtcl error:");
        }

        //表示请求信号量1个资源,信号量值将减去1
        struct sembuf buf1 = {0 , -1 , 0};
        semop(semid , &buf1 , 1);
        puts("sem1 is get sem");
        sleep(8);

        //进程1退出,此时进程1并没有正常释放信号量资源
        return 0;
}

sem2进程

#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

void print_sem(int semid)
{
        //打印信号量
        int ret;
        unsigned short array[1] = {0};
        ret = semctl(semid , 0 , GETALL , array);
        if(ret < 0){
                perror("semtcl error:");
        }
        printf("sem1 = %d\n" , array[0]);
}

int main(void)
{
        int ret;
        int semid = 0;
        key_t key = 0x00112233;

        //获取信号量
        semid = semget(key , 0 , 0);
        if(semid < 0){
                perror("semget error:");
        }
        print_sem(semid);
        //表示请求信号量1个资源,信号量值将减去1
        struct sembuf buf1 = {0 , -1 , 0};
        //如果不满足条件,sem2则会一直阻塞在此
        puts("sem2 is wait for sem");
        semop(semid , &buf1 , 1);

        puts("sem2 is get sem");

        //释放信号量,释放1个资源,信号量的值将加1
        struct sembuf buf2 = {0 , 1 , 0};
        semop(semid , &buf2 , 1);
        return 0;
}

程序执行结果:

由于sem1在退出时没有释放信号量资源,导致sem2进程一直阻塞。

当sem1指定SEM_UNDO选项后,程序执行结果如下:

struct sembuf buf1 = {0 , -1 , SEM_UNDO};
semop(semid , &buf1 , 1);

 sem1在指定SEM_UNDO选项后,sem1退出时并没有释放信号量资源,此时将会由内核自动释放信号量资源,并且sem2申请信号量资源成功,解除阻塞状态。

猜你喜欢

转载自blog.csdn.net/qq_35733751/article/details/82919091
今日推荐