Linux信号阻塞与未达

信号在内核中的表示

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

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


说明:

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

    2)信号屏蔽状态字(block),1代表阻塞、0代表不阻塞;信号未决状态字(pending)的1代表未决,0代表信号可以抵达了;

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

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

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

 

   思考1:状态字都64bit,编程时,如何表示状态字那?

   思考2:block状态字信息如何获取或者操作那?哪些api?

   思考3:pending状态字信息如何获取或者操作那?哪些api?

     答案见下;

 

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

sigpending获取信号未决状态字(pending)信息
#include <signal.h>
int sigemptyset(sigset_t *set);	//把信号集清零;(64bit/8=8字节)
int sigfillset(sigset_t *set); 	//把信号集64bit全部置为1
int sigaddset(sigset_t *set, int signo); 	//根据signo,把信号集中的对应位置成1
int sigdelset(sigset_t *set, int signo); 	//根据signo,把信号集中的对应位置成0
int sigismember(const sigset_t *set, int signo);	//判断signo是否在信号集中

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参数的可选值。

How:

 
NAME
       sigpending - examine pending signals
SYNOPSIS
       #include <signal.h>
       int sigpending(sigset_t *set);
DESCRIPTION
       sigpending()  returns the set of signals that are pending for delivery to the calling thread (i.e., the signals which have been raised while blocked).  The mask of pending signals is returned in set.

信号阻塞未达实践


void printSignalPending(sigset_t *printSigSet)
{
    for (unsigned i = 1; i < NSIG; ++i)
    {
        if (sigismember(printSigSet,i))
        {
            putchar(‘1‘);
        }
        else
        {
            putchar(‘0‘);
        }
    }
    putchar(‘\n‘);
}

void onSignalAction(int signalNumber)
{
    switch(signalNumber)
    {
    case SIGINT:
        cout << "SIGINT = " << signalNumber << endl;
        cout << "I have return .... ^^ ^^ ^^ ^^" << endl;
        break;
    case SIGQUIT:
        cout << "SIGQUIT = " << signalNumber << endl;

        //对SIGINT信号解除阻塞
        cout << "Unblock SIGINT..." << endl;
        sigset_t unblockSigset;
        sigemptyset(&unblockSigset);
        sigaddset(&unblockSigset,SIGINT);
        sigprocmask(SIG_UNBLOCK,&unblockSigset,NULL);
        break;
    default:
        cout << "Other Signal ..." << endl;
        break;
    }
}

int main()
{
    if (signal(SIGINT,onSignalAction)== SIG_ERR)
    {
        perror("signal error");
        return -1;
    }
    if (signal(SIGQUIT,onSignalAction) == SIG_ERR)
    {
        perror("signal error");
        return -1;
    }

    sigset_t setSigSet,printSigSet;
    sigemptyset(&setSigSet);
    sigaddset(&setSigSet,SIGINT);   //添加SIGINT到信号屏蔽字
    //sigaddset(&setSigSet,SIGQUIT);  //添加SIGQUIT到信号屏蔽字

	//更改进程的信号屏蔽字 这里用来阻塞Ctrl+c[SIGINT]信号
	//SIGINT信号被设置成阻塞,即使用户按下Ctrl+c键盘,也不会抵达
    sigprocmask(SIG_BLOCK,&setSigSet,NULL);

    while (true)
    {
        //获取并打印未决字信息
        sigpending(&printSigSet);
        printSignalPending(&printSigSet);

        sleep(2);
    }

    return 0;
}

/**
    连续的按Ctrl+c键盘,虽然发送了多个SIGINT信号,
    但是因为信号是不稳定的(不支持排队),所以只保留了一个,如下图
*/



-杀死改进程,需要另启一终端,执行

killall main


-Makefile文件

CC = g++ 
CPPFLAGS = -Wall -g

BIN = main
SOURCES = $(wildcard *.cpp)
OBJECTS = $(SOURCES:.cpp=.o)

.PHONY: clean all 

all: $(BIN)

$(BIN): $(OBJECTS)

%.o: %.cpp
    $(CC) $(CPPFLAGS) -o $@ -c $<

clean:
    -rm -rf $(BIN) $(OBJECTS)


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。