linux设置子线程的信号屏蔽器,Linux系统编程与网络编程——信号介绍,未决信号集,信号屏蔽字,捕捉信号(十)...

什么是信号机制

信号(signal)机制是Linux系统中最为古老的进程之间的通信机制。Linux信号也可以称为软中断,是在软件层次上对中断机制的一种模拟。在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程发生了什么。

信号的发生有两个来源:

(1)硬件来源,比如我们按下了键盘或者其他硬件信号触发,硬件异常如除以零运算,内存非法访问。

(2)软件来源,最常见发送信号的系统函数 kill() raise() alarm() 和 setitimer() 等函数以及ctl+c发出SIGINT、ctl+z SIGTSTP、ctl+\ SIGQUIT。

Linux系统中定义了一系列的信号,可以使用 kill -l 命令列出所有的信号。

Linux的信号机制是从Unix继承下来的,早期Unix系统只定义了32种信号,现在Linux支持64种信号。

267f3b05a7f372103456c2a41c5df24a.png

b9eca49a11f5385a55684b1a7c2200af.png

信号集

信号集顾名思义就是信号的集合,信号集的类型为sigset_t,每一种信号用1bit来表示,前面我们提到信号有64种,那么这个sigset_t类型至少占64bit,可以通过sizeof(sigset_t)来查看。

每个进程的PCB进程控制块中都有两个信号集,一个叫作未决信号集,一个叫作信号屏蔽字,信号集的每一位不是0就是1,初始状态下,两个信号集的值都为0。

当有信号传递到该进程的时候,未决信号集的对应位设置为1,其他位不变,这个时候信号只是传递到进程,并未被处理,叫作未决状态。常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。用户只能获取未决信号集值,无法改变其值。

未决信号想要递达程序的信号处理函数(默认、忽略、自定义),还要经过信号屏蔽字的过滤,一旦该信号对应bit为1,则该信号将阻塞,不能传递到信号的处理函数。用户可以设置获取信号屏蔽字的值。

信号集处理函数

5002239e3e49ffa3de61ee665ef3d403.png

sigprocmask,调用函数sigprocmask可以读取或更改进程的信号屏蔽字。

389461a1dd63d95dc6c6faf6c73f36b1.png

如果调用sigprocmask解除了对当前若干个未决信号的阻塞,则在sigprocmask返回前, 至少将其中一个信号递达。

读取当前进程的未决信号集,sigpending:

90825abe8b29542d94cc9edceafc5b7a.png

sigpending获取的是未决信号集,不能获取信号关键字

综合示例

#include

#include

void printsigset(const sigset_t *set)

{

int i;

for (i = 1; i < 32; i++)

if (sigismember(set, i) == 1)

putchar('1');

else

putchar('0');

puts("");

}

int main(void)

{

sigset_t s, p;

sigemptyset(&s);

sigaddset(&s, SIGINT); //将信号集s中的SIGINT置为1

sigprocmask(SIG_BLOCK, &s, NULL); //设置信号屏蔽字

while (1)

{

sigpending(&p); //获取未决信号集

printsigset(&p); //打印未决信号集

sleep(1);

}

return 0;

}

信号传递过程

e964d6dac3895773e289f2d927a75725.png

sigaction函数

#include

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

struct sigaction

{

void (*sa_handler)(int);

void (*sa_sigaction)(int, siginfo_t *, void *);

sigset_t sa_mask;

int sa_flags;

void (*sa_restorer)(void);

}

66d9dba2e88428059df9a1e4c496a5f1.png

sigaction例子

#include

#include

#include

void sig_handle(int sig)

{

puts("recv SIGINT");

sleep(5);

puts("end");

}

int main(int argc, char** argv)

{

struct sigaction act;

act.sa_handler = sig_handle;

act.sa_flags = 0;

sigemptyset(&act.sa_mask);

sigaddset(&act.sa_mask, SIGQUIT); //当进入信号处理函数的时候,屏蔽掉SIGQUIT的递达

sigaction(SIGINT, &act, NULL);

while(1)

sleep(1);

return 0;

}

c4dae61c16f51fa0dc548bf242cb0179.png

进程对信号的响应和处理

进程可以通过三种方式来响应和处理一个信号:

(1)忽略信号:即对信号不做任何处理,但是两个信号不能忽略: SIGKILL 以及 SIGSTOP .

(2)捕捉信号:当信号发生时,执行用户定义的信号处理函数。

(3)执行默认操作: Linux对每种信号都规定了默认操作,man 7 signal查看。

81c5c8aafd36ac6f41aadebaead8d551.png

进程对收到的信号有对应的缺省操作,如果进程要自定义处理某个信号,那么就要在进程中安装该信号。

信号之间不存在相对的优先权。信号在产生时也并不马上送给进程,信号必须等待直到进程再一次被调度运行。

read函数的EINTR错误

#include

#include

#include

#include

#include

void sig_handle(int sig)

{

printf("SIGINT\n");

}

i

nt main(int argc, char** argv)

{

char buf[10];

struct sigaction act;

act.sa_handler = sig_handle;

act.sa_flags = 0;

//act.sa_flags = SA_RESTART; //先试一试sa_flags为0时,然后再试一试SA_RESTART的情况

sigemptyset(&act.sa_mask);

sigaction(SIGINT, &act, NULL);

puts("read stdio:");

int ret = read(STDIN_FILENO, buf, 9);

if(ret == -1)

{

if(errno == EINTR)

perror("read:");

}

else

{

buf[ret] = '\0';

printf("read %d : %s", ret, buf);

}

return 0;

}

以上的运行结果的是,act.sa_flags = 0时一旦发送了一个信号,read将被打断,返回-1,并且设置errno为

EINTR。如果想打断后继续运行read,可以设置一下act.sa_flags = SA_RESTART。或者也可以使用signal函数来处理信号,signal相当于默认设置了SA_RESTART标志。

可重入函数

不含全局变量和静态变量是可重入函数的一个要素, 可重入函数见man 7 signal, 在信号捕捉函数里应使用可重入函数,在信号捕捉函数里禁止调用不可重入函数。

例如:strtok就是一个不可重入函数,因为strtok内部维护了一个内部静态指针,保存上一次切割到的位置,如果信号的捕捉函数中也去调用strtok函数,则会造成切割字符串混乱,应用strtok_r版本,r表示可重入。

#include

#include

#include

#include

static char buf[] = "hello world good book";

void sig_handle(int sig)

{

strtok(NULL, " ");

}

int main(int argc, char** argv)

{

signal(SIGINT, sig_handle);

printf("%s\n", strtok(buf, " "));

printf("%s\n", strtok(NULL, " "));

sleep(5); //可以被信号打断,返回剩余的时间,想想看这个函数应该怎么调用

printf("%s\n", strtok(NULL, " "));

return 0;

}

运行的时候,发现通过Ctrl+c发射信号与没发射信号的结果不一样,可以改用strtok_r函数。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值