(P19)信号:信号在内核中的表示、信号阻塞与未决、信号集操作函数、sigprocmask

文章目录

    • 1.信号在内核中的表示
    • 2.信号阻塞与未决
    • 3.信号集操作函数
    • 4.sigprocmask

1.信号在内核中的表示

  • 当进程捕捉到信号后,就直接递交给信号处理函数来进行处理,这就是递达状态

  • 执行信号的处理动作称之为信号递达Delivery,信号从产生到递达之间的状态,称为信号未决Pending(前提:进程必须先阻塞某一个信号,信号发生的时候,进程不立刻去执行信号处理函数,而是处于阻塞的状态,这样的状态称之为未决状态
    进程可以选择阻塞Block某个信号;
    被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作;
    注意:阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作

2.信号阻塞与未决

  • 信号从产生到递达的过程(信号在内核中的表示可以看作是:)
    (1)信号屏蔽字,未决状态都是64bit的
    (2)假设SIGINT信号发送给进程,该信号是否会递达(是否会执行信号所对应的处理函数呢?)取决于block的位是1还是0,为1时,表示信号到的时候,进程会阻塞,一旦阻塞,pending对应的位就会置1,表示该信号处于未决状态,直到解除了阻塞,即block由1置为0,解除了阻塞信号就会被递达,pending对应的位也由1置为0,接着执行对应的处理函数,当前的处理函数:SIG_IGN忽略
    (P19)信号:信号在内核中的表示、信号阻塞与未决、信号集操作函数、sigprocmask_第1张图片

3.信号集操作函数

  • 64bit的信号屏蔽字由sigset_t表示
    只是改变变量,不会改变进程中的信号屏蔽字
    man sigismember
#include 

int sigemptyset(sigset_t *set);64bit清零

int sigfillset(sigset_t *set);64bit都变为1

int sigaddset(sigset_t *set, int signum);
信号对应的位变为1

int sigdelset(sigset_t *set, int signum);
信号对应的位变为0

int sigismember(const sigset_t *set, int signum);
检测信号对应的位是0还是1

4.sigprocmask

  • 改变或者获取进程中的信号屏蔽字
#include 
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
 参数:
how:改变方式:如何去改变进程中的信号屏蔽字
set:准备的信号集,将信号集按照号所指定的方式改变到进程当中
oldset:返回进程中在改变之前的屏蔽字的状态

返回值:若成功则为0,若出错则为-1
  • (1)如果oldset是非空指针,则读取进程的当前信号屏蔽字通过oldset参数传出;

  • (2)如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改。

  • (3)如果oldset和set都是非空指针,则先将原来的信号屏蔽字备份到oldset里,然后根据set和how参数更改信号更改信号屏蔽字。

  • 假设当前的信号屏蔽字为mask,how参数的可选值如下:
    改变方式:3种
    (P19)信号:信号在内核中的表示、信号阻塞与未决、信号集操作函数、sigprocmask_第2张图片

  • eg:P19signal.c

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 


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


void printsigset(sigset_t *set)
{
    int i;
    for (i = 1; i < NSIG; ++i)//NSIG表示信号的最大值:为64
    {
        if (sigismember(set, i))//检测对应的信号是否在集合中,若在输出1,不在输出0
            putchar('1');
        else
            putchar('0');
    }
    printf("\n");
}

void 
int main(int argc, char *argv[])
{
    sigset_t pset;

    if (signal(SIGINT, handle) == SIG_ERR)
        ERR_EXIT("signal error");
    for(;;)
    {
        //man sigpending
        //sigpending:获取进程中未决的信号集保存到pset
        sigpending(&pset);
        printsigset(&pset);
        sleep(1);
    }
    return 0;
}

void handle(int sig)
{
    printf("recv a sig=%d\n", sig);
}

  • 测试:
    按下ctrl c,因为信号被捕捉了,它直接就递达了,也就是说直接调用了信号处理函数,这样不会经过未决状态,所以也没有1的出现
    (P19)信号:信号在内核中的表示、信号阻塞与未决、信号集操作函数、sigprocmask_第3张图片

  • eg:P19signalstatus.c

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 


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


void printsigset(sigset_t *set)
{
    int i;
    for (i = 1; i < NSIG; ++i)//NSIG表示信号的最大值:为64
    {
        if (sigismember(set, i))//检测对应的信号是否在集合中,若在输出1,不在输出0
            putchar('1');
        else
            putchar('0');
    }
    printf("\n");
}

void 
int main(int argc, char *argv[])
{
    sigset_t pset;
    sigset_t bset;
    
    sigemptyset(&bset);
    //增加一个阻塞的信号
    sigaddset(&bset, SIGINT);

    if (signal(SIGINT, handle) == SIG_ERR)
        ERR_EXIT("signal error");

    //改变进程中的信号屏蔽字
    sigprocmask(SIG_BLOCK, &bset, NULL);
    for(;;)
    {
        //man sigpending
        //sigpending:获取进程中未决的信号集保存到pset
        sigpending(&pset);
        printsigset(&pset);
        sleep(1);
    }
    return 0;
}

void handle(int sig)
{
    printf("recv a sig=%d\n", sig);
}

  • 测试:

按下ctrl c,不会递达了,因为进程中的信号屏蔽字已被sigprocmask函数更改,SIG_BLOCK表示将SIGINT对应的位,置1了,所以即使信号到了,也把信号阻塞住了,处于未决状态
(P19)信号:信号在内核中的表示、信号阻塞与未决、信号集操作函数、sigprocmask_第4张图片

  • eg:P19signalstatus1.c
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 


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


void printsigset(sigset_t *set)
{
    int i;
    for (i = 1; i < NSIG; ++i)//NSIG表示信号的最大值:为64
    {
        if (sigismember(set, i))//检测对应的信号是否在集合中,若在输出1,不在输出0
            putchar('1');
        else
            putchar('0');
    }
    printf("\n");
}

void 
int main(int argc, char *argv[])
{
    sigset_t pset;
    sigset_t bset;
    
    sigemptyset(&bset);
    //增加一个阻塞的信号
    sigaddset(&bset, SIGINT);

    if (signal(SIGINT, handle) == SIG_ERR)
        ERR_EXIT("signal error");
        
    if (signal(SIGQUIT, handle) == SIG_ERR)
        ERR_EXIT("signal error");
    //改变进程中的信号屏蔽字
    sigprocmask(SIG_BLOCK, &bset, NULL);
    for(;;)
    {
        //man sigpending
        //sigpending:获取进程中未决的信号集保存到pset
        sigpending(&pset);
        printsigset(&pset);
        sleep(1);
    }
    return 0;
}

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

  • 测试:
    当按下ctrl \后,解除阻塞,处于未决状态的信号就会被递达,就算多次按下ctrl c,只会有一个未决的状态,因为他是不可靠信号,不支持排队,产生这么多的2号SIGINT信号,只会被保留一个
    (P19)信号:信号在内核中的表示、信号阻塞与未决、信号集操作函数、sigprocmask_第5张图片

  • Makefile

.PHONY:clean all
CC=gcc
CFLAGS=-Wall -g
BIN=01sigstatus
all:$(BIN)
%.o:%.c
	$(CC) $(CFLAGS) -c $< -o $@
clean:
	rm -f *.o $(BIN)

你可能感兴趣的:(Linux高性能编程)