inux信号(3):信号内核表示 、操作函数,阻塞与未决

信号在内核中的表示

   执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending)。进程可以选择阻塞(Block)某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。

   注意,阻塞和忽略是不同,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。信号在内核中的表示可以看作是这样的:

解释说明:

  1)PCB进程控制块(task_struct)中函数有信号屏蔽状态字(block)和信号未决状态字(pending)还有是否忽略标志;

  2) 信号屏蔽状态字(block), 1代表阻塞、0代表不阻塞;

      信号未决状态字(pending)的1代表未决,0代表信号可以抵达了;

  3)向进程发送SIGINT,内核首先判断信号屏蔽状态字是否阻塞,若阻塞,信号未决状态字(pending)相应位制成1;若阻塞解除,信号未决状态字(pending)相应位制成0;表示信号可以抵达了。

  4)block状态字、pending状态字均64位(bit);

  5)block状态字用户可以读写,pending状态字用户只能读;这是信号设计机制。

信号集操作函数(状态字表示) 

#include <signal.h>
sigemptyset(sigset_t *set)
//初始化由set指定的信号集,信号集里面的所有信号被清空,相当于64为置0;

sigfillset(sigset_t *set)
//调用该函数后,set指向的信号集中将包含linux支持的64种信号,相当于64为都置1;


sigaddset(sigset_t *set, int signum)
//在set指向的信号集中加入signum信号,相当于将给定信号所对应的位置1;

sigdelset(sigset_t *set, int signum)
//在set指向的信号集中删除signum信号,相当于将给定信号所对应的位置0;


sigismember(const sigset_t *set, int signum)
//判定信号signum是否在set指向的信号集中,相当于检查给定信号所对应的位是0还是1。

sigprocmask:读取/更改信号屏蔽状态字(Block)

int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

返回值:若成功则为0,若出错则为-1

  读取:如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。

  更改:如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了how参数的可选值。

sigpending获取信号未决状态字(pending)信息 

#include <signal.h>
int sigpending(sigset_t *set);
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>


#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

void handler(int sig);
void printsigset(sigset_t *set)    //打印信号集
{
    int i;
    for (i=1; i<NSIG; ++i)
    {
        if (sigismember(set, i))
            putchar('1');
        else
            putchar('0');
    }
    printf("\n");
}

int main(int argc, char *argv[])
{
    sigset_t pset;
    sigset_t bset;
    sigemptyset(&bset);            //初始化  清空
    sigaddset(&bset, SIGINT);      //设置SIGINT位为1
    if (signal(SIGINT, handler) == SIG_ERR)
        ERR_EXIT("signal error");
    if (signal(SIGQUIT, handler) == SIG_ERR)
        ERR_EXIT("signal error");

    sigprocmask(SIG_BLOCK, &bset, NULL);//SIGINT阻塞 将信号加入进程阻塞集中
    for (;;)
    {
        sigpending(&pset);           //获取
        printsigset(&pset);          //打印
        sleep(1);
    }
    return 0;
}

void handler(int sig)
{
    if (sig == SIGINT)
        printf("recv a sig=%d\n", sig);    
    else if (sig == SIGQUIT)
    {
        sigset_t uset;
        sigemptyset(&uset);
        sigaddset(&uset, SIGINT);
        sigprocmask(SIG_UNBLOCK, &uset, NULL);     //解除阻塞
    }
}

说明:程序首先将SIGINT信号加入进程阻塞集(屏蔽集)中,一开始并没有发送SIGINT信号,所以进程未决集中没有处于未决态的信号,当我们连续按下ctrl+c时,向进程发送SIGINT信号,由于SIGINT信号处于进程的阻塞集中,所以发送的SIGINT信号不能递达,也是就是处于未决状态,所以当我打印未决集合时发现SIGINT所对应的位为1,现在我们按下ctrl+\,发送SIGQUIT信号,由于此信号并没被进程阻塞,所以SIGQUIT信号直接递达,执行对应的处理函数,在该处理函数中解除进程对SIGINT信号的阻塞,所以之前发送的SIGINT信号递达了,执行对应的处理函数,但由于SIGINT信号是不可靠信号,不支持排队,所以最终只有一个信号递达。  (killall  main  结束程序)

猜你喜欢

转载自blog.csdn.net/Alatebloomer/article/details/81806102