Linux信号实践(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状态字用户只能读;这是信号设计机制。

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

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

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

答案见下;

信号集操作函数(状态字表示)#include <signal.h>int sigemptyset(sigset_t *set);//把信号集清零;(64bit/8=8字节)int sigfillset(sigset_t *set); //把信号集64bit全部置为1int sigaddset(sigset_t *set, int signo); //根据signo,把信号集中的对应位置成1int sigdelset(sigset_t *set, int signo); //根据signo,把信号集中的对应位置成0int 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:

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

#include <signal.h>int sigpending(sigset_t *set);

DESCRIPTION

sigpending()returnsthesetofsignalsthatarependingfordeliverytothecallingthread

(i.e.,thesignalswhichhavebeenraisedwhileblocked).

Themaskofpendingsignalsisreturnedinset.

/**示例1:添加信号SIGINT到信号屏蔽字此时再按下Ctrl+C键, 进程也接收不到SIGINT信号了**/inline void err_exit(std::string message);void sigHandler(int signo);void printSigSet(const sigset_t &sigset);int main(){//虽然此处安装了信号处理函数, 但是该进程还是接收不到SIGINT信号if (signal(SIGINT, sigHandler) == SIG_ERR)err_exit("signal error");//添加信号屏蔽字: 屏蔽SIGINT信号sigset_t addset;sigemptyset(&addset);sigaddset(&addset, SIGINT);if (sigprocmask(SIG_BLOCK, &addset, NULL) == -1)err_exit("sigprocmask error");// 不断打印当前的信号屏蔽字sigset_t sigset;while (true){sigpending(&sigset);printSigSet(sigset);sleep(1);}}inline void err_exit(std::string message){perror(message.c_str());exit(EXIT_FAILURE);}void sigHandler(int signo){cout << "catch a signal, number = " << signo << endl;}void printSigSet(const sigset_t &sigset){for (unsigned i = 1; i < NSIG; ++i){if (sigismember(&sigset, i))putchar(‘1’);elseputchar(‘0’);}putchar(‘\n’);}/** 示例2:在示例1的基础上继续屏蔽SIGINT信号, 但是如果该进程接收到了SIGQUIT信号, 则将对SIGINT信号的屏蔽解除, 需要1.在main函数中再安装一个SIGQUIT捕捉函数2.对sigHandler函数进行改造**/int main(){if (signal(SIGINT, sigHandler) == SIG_ERR)err_exit("signal SIGINT error");if (signal(SIGQUIT, sigHandler) == SIG_ERR)err_exit("signal SIGQUIT error");//添加信号屏蔽字: 屏蔽SIGINT信号sigset_t addset;sigemptyset(&addset);sigaddset(&addset, SIGINT);if (sigprocmask(SIG_BLOCK, &addset, NULL) == -1)err_exit("sigprocmask error");// 不断打印当前的信号屏蔽字sigset_t sigset;while (true){sigpending(&sigset);printSigSet(sigset);sleep(1);}}void sigHandler(int signo){switch (signo){case SIGINT:cout << "catch a signal SIGINT" << endl;break;case SIGQUIT://如果是SIGQUIT信号, 则将SIGINT信号的屏蔽进行解除sigset_t unblockSet;sigemptyset(&unblockSet);sigaddset(&unblockSet, SIGINT);if (sigprocmask(SIG_UNBLOCK, &unblockSet, NULL) == -1)err_exit("sigprocmask unblock error");elsecout << "sigprocmask success" << endl;break;default:cerr << "unknown signal" << endl;break;}}/**连续的按Ctrl+c键盘,虽然发送了多个SIGINT信号,但是因为信号是不稳定的(不支持排队),所以只保留了一个,如下图*/

再怎么风光明媚的自家山川,

Linux信号实践(3)

相关文章:

你感兴趣的文章:

标签云: