欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 维修 > Linux进程信号

Linux进程信号

2025/4/17 17:13:39 来源:https://blog.csdn.net/2302_81171591/article/details/147000812  浏览:    关键词:Linux进程信号
  • 你怎么能识别信号呢?识别信号是内置的,进程识别信号,是内核程序员写的内置特性。
  • 信号产生之后,你知道怎么处理吗?知道。如果信号没有产生,你知道怎么处理信号吗?知道。所以,信号的处理方法,在信号产生之前,已经准备好了。
  • 处理信号,立即处理吗?我可能正在做优先级更高的事情,不会立即处理?什么时候?合适的时候。
  • 信号到来 | 信号保存 | 信号处理
  • 怎么进行信号处理啊?a.默认 b.忽略 c.自定义, 后续都叫做信号捕捉。
// sig.cc
#include <iostream>
#include <unistd.h>
int main()
{
while(true){
std::cout << "I am a process, I am waiting signal!" << std::endl;
sleep(1);
}
}

编写了一个死程序,按理说会无限循环下去,但是为什么用户ctrl + c会终止运行呢?

  1. 用户输入命令,在Shell下启动一个前台进程
  2. 用户按下 Ctrl+C ,这个键盘输入产生一个硬件中断,被OS获取,解释成信号,发送给目标前台进程
  3. 前台进程因为收到信号,进而引起进程退出

进程信号

 信号的本质就是宏,而1~31个信号分别用比特位的01代表示

 

而其实, Ctrl+C 的本质是向前台进程发送 SIGINT 即 2 号信号,我们证明⼀下,这里需要引入一
个系统调用函数(signal)

signal

NAMEsignal - ANSI C signal handling
SYNOPSIS#include <signal.h>typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);
参数说明:
signum:信号编号[后⾯解释,只需要知道是数字即可]
handler:函数指针,表⽰更改信号的处理动作,当收到对应的信号,就回调执⾏handler⽅法

开始测试

编写一个死循环,同时接收到2号信号时不执行默认的函数,转而使用自定义函数,预期结果:打印信号而不是杀死进程

#include<iostream>
#include<unistd.h>
#include<signal.h>
void handlerSig(int sig)
{
std::cout<<getpid()<<"获得了一个信号:"<<sig<<std::endl;
}
int main()
{ //进程收到2号信号时转而调用自定义函数handlerSig并将sig传递给自定义函数signal(SIGINT,handlerSig);int cnt=0;while(true){std::cout<<getpid()<<": "<<"hello word"<<cnt++<<std::endl;sleep(1);}}

此时ctrl + C杀不掉只能使用kill -9 

这里进程为什么不退出?signal调用将信号编号交给默认的函数处理,而2号对于默认的函数是杀掉进程,现在转而执行自定义函数
这个例子能说明哪些问题?信号处理,不是自己处理,而是调用别的函数
 

注意

  • 要注意的是,signal函数仅仅是设置了特定信号的捕捉作为处理方式,并不是直接调用处理动作。如果后续特定信号没有产生,设置的捕捉函数永远也不会被调用!!
  • Ctrl-C 产生的信号只能发给前台进程。一个命令后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。
  • Shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接到像 Ctrl-C这种控制键产生的信号。
  • 前台进程在运行过程中用户随时可能按下 Ctrl-C 而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都有可能收到 SIGINT 信号而终止,所以信号相对于进程的控制流程来说是异步(Asynchronous)的。
  • 关于进程间关系,我们在⽹络部分会专门来讲,现在就了解即可。
  • 可以渗透 & 和 nohup

信号处理

处理信号有三种方式:

1.默认处理的动做

#include<iostream>
#include<unistd.h>
#include<signal.h>
void handlerSig(int sig)
{
std::cout<<getpid()<<"获得了一个信号:"<<sig<<std::endl;
}
int main()
{ //进程收到2号信号时转而调用自定义函数handlerSig并将sig传递给自定义函数signal(SIGINT/*2*/, SIG_DFL); // 设置忽略信号的宏int cnt=0;while(true){std::cout<<getpid()<<": "<<"hello word"<<cnt++<<std::endl;sleep(1);}}

2.自定义处理动作

如之前代码

提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为自
定义捕捉(Catch)一个信号。

3.忽略处理

#include<iostream>
#include<unistd.h>
#include<signal.h>
void handlerSig(int sig)
{
std::cout<<getpid()<<"获得了一个信号:"<<sig<<std::endl;
}
int main()
{ //进程收到2号信号时转而调用自定义函数handlerSig并将sig传递给自定义函数signal(SIGINT/*2*/, SIG_IGN); // 设置忽略信号的宏int cnt=0;while(true){std::cout<<getpid()<<": "<<"hello word"<<cnt++<<std::endl;sleep(1);}}

 上面的所有内容,我们都没有做非常多的解释,主要是先用起来,然后渗透部分概念和共识,下面我们从理论和实操两个层面,来进⾏对信号的详细学习、论证和理解。为了保证条理,我们采用如下思路来进行阐述:

通过终端按键产生信号 

  • Ctrl+C (SIGINT) 已经验证过,这里不再重复
  • Ctrl+\(SIGQUIT)可以发送终止信号并生成core dump文件,用于事后调试(后面详谈)

 给所有比特位都注册信号

#include<iostream>
#include<unistd.h>
#include<signal.h>
void handlerSig(int sig)
{
std::cout<<getpid()<<"获得了一个信号:"<<sig<<std::endl;
}
int main()
{ for(int i=1;i<=31;i++)signal(i, handlerSig); //给1~31号信号都注册自定义函数int cnt=0;while(true){std::cout<<getpid()<<": "<<"hello word"<<cnt++<<std::endl;sleep(1);}}

 产生信号的方式:

1.调用系统命令kill向进程发信号

2.通过键盘

3.调用系统调用函数

kill

kill 命令是调用 kill 函数实现的。 kill 函数可以给一个指定的进程发送指定的信号

NAME
       kill - send signal to a process
SYNOPSIS
       #include <sys/types.h>
       #include <signal.h>
       int kill(pid_t pid, int sig);
RETURN VALUE
   On success (at least one signal was sent), zero is returned. On error,
   -1 is returned, and errno is set appropriately.

#include<iostream>
#include<sys/types.h>
#include<signal.h>//  ./mykill  signumber pid
int main( int argc ,char* argv[])
{
if(argc !=3)
{std::cout<<"./mykill  signumber pid"<<std::endl;return 1;
}
int signum =std::stoi(argv[1]);
pid_t target =std::stoi(argv[2]);
int n=kill(target,signum);
if(n==0)
{std::cout<<"发送"<<signum<<"到"<<target<<"成功"<<std::endl;
}
return 0;
}

 在kill这个系统调用中,可以发送进程信号给指定进程

 raise


raise 函数可以给当前进程发送指定的信号(自己给自己发信号)。

NAMEraise - send a signal to the caller
SYNOPSIS#include <signal.h>int raise(int sig);
RETURN VALUEraise() returns 0 on success, and nonzero for failure.
#include<iostream>
#include<unistd.h>
#include<signal.h>
void handlerSig(int sig)
{
std::cout<<getpid()<<"获得了一个信号:"<<sig<<std::endl;
}
int main()
{ for(int i=1;i<=31;i++)signal(i, handlerSig); //给1~31号信号都注册自定义函数for(int i=1;i<=31;i++){sleep(1);raise(i);}}

 到9就停止是因为9号信号是强制执行的,系统调用对他无效,即便是调用自定义函数也仍然会杀死进程,比2号进程还猛

 abort


abort 函数使当前进程接收到信号而异常终止。

NAMEabort - cause abnormal process termination
SYNOPSIS#include <stdlib.h>void abort(void);
RETURN VALUEThe abort() function never returns.// 就像exit函数⼀样,abort函数总是会成功的,所以没有返回值。

#include<iostream>
#include<unistd.h>
#include<signal.h>
void handlerSig(int sig)
{
std::cout<<getpid()<<"获得了一个信号:"<<sig<<std::endl;
}
int main()
{ for(int i=1;i<=31;i++)signal(i, handlerSig); //给1~31号信号都注册自定义函数int cnt=0;while(true){std::cout<<getpid()<<": "<<"hello word"<<cnt++<<std::endl;abort();sleep(1);}}

 发送固定的6号(SIGABRT)信号给自己,要求进程必须处理,目的就是终止进程

由软件条件产生信号

SIGPIPE 是一种由软件条件产生的信号,在“管道”中已经介绍过了。本节主要介绍 alarm 函数
和 SIGALRM 信号。

NAMEalarm - set an alarm clock for delivery of a signal
SYNOPSIS#include <unistd.h>unsigned int alarm(unsigned int seconds);
RETURN VALUEalarm() returns the number of seconds remaining until any previouslyscheduled alarm was due to be delivered, or zero if there was no previ‐ously scheduled alarm.
  • 调用 alarm 函数可以设定一个闹钟,也就是告诉内核在 seconds 秒之后给当前进程发
    SIGALRM 信号,该信号的默认处理动作是终止当前进程。
  • 这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数。打个比方,某人要小睡一觉,设定闹钟为30分钟之后响,20分钟后被人吵醒了,还想多睡一会儿,于是重新设定闹钟为15分钟之后响,“以前设定的闹钟时间还余下的时间”就是10分钟。如果seconds值为0,表示取消以前设定的闹钟,函数的返回值仍然是以前设定的闹钟时间还余下的秒数。 

基本alarm验证-体会IO效率问题

程序的作用是1秒钟之内不停地数数,1秒钟到了就被SIGALRM信号终止。必要的时候,对SIGALRM信号进行捕捉

 1.IO多
// IO 多
#include <iostream>
#include <unistd.h>
#include <signal.h>
int main()
{
int count = 0;
alarm(1);
while(true)
{
std::cout << "count : "
<< count << std::endl;
count++;
}
return 0;
}

2.IO少

// IO 少
#include <iostream>
#include <unistd.h>
#include <signal.h>
int count = 0;
void handler(int signumber)
{
std::cout << "count : " <<
count << std::endl;
exit(0);
}
int main()
{
signal(SIGALRM, handler);
alarm(1);
while (true)
{
count++;
}
return 0;
}

 明显下面的count计数更快,上面是每加一次IO一次,下面是加完在IO,IO频繁得读写文件更慢

总结:

  • 闹钟会响一次,默认终止进程
  • 有IO效率低 

一次性闹钟

不断打点,1s后收到SIGALRM信号后打出该信号然后停留一秒,然后无限制打点

// 一次性闹钟
#include <iostream>
#include <unistd.h>
#include <signal.h>
int count = 0;
void handler(int signumber)
{std::cout << "获得了一个信号:" << SIGALRM << std::endl;sleep(1);
}
int main()
{signal(SIGALRM, handler);alarm(1);while (true){std::cout << "." << std::endl;}return 0;
}

 重复闹钟

在自定义函数内继续发闹钟

// 一次性闹钟
#include <iostream>
#include <unistd.h>
#include <signal.h>
int count = 0;
void handler(int signumber)
{std::cout << "获得了一个信号:" << signumber << "pid:" << getpid() << std::endl;alarm(1);
}
int main()
{signal(SIGALRM, handler);alarm(1);while (true){std::cout << "." << "pid:" << getpid() << std::endl;sleep(1);}return 0;
}

pause

等待一个信号出错时返回-1,没有信号时暂停,信号捕捉时返回时才会返回

NAMEpause - wait for signal
SYNOPSIS#include <unistd.h>int pause(void);
DESCRIPTIONpause() causes the calling process (or thread) to sleep until a signalis delivered that either terminates the process or causes the invoca‐tion of a signal-catching function.
RETURN VALUEpause() returns only when a signal was caught and the signal-catchingfunction returned. In this case, pause() returns -1, and errno is setto EINTR. 

 进程处于暂停状态,一收到信号,转而实现各种功能,每隔1s收到一个信号,周期性打印


#include <iostream>
#include <unistd.h>
#include <signal.h>
#include<functional>
#include<vector>
using func_t=std::function<void()>;
std::vector<func_t>funcs;
///
void sche()
{std::cout<<"我是进程调度"<<std::endl;}
void memmanger()
{std::cout<<"我是周期性内存管理"<<std::endl;}
void Fflush()
{std::cout<<"我是刷新程序"<<std::endl;}
///
void handler(int signumber)
{for(auto f:funcs){f();}int n= alarm(1);}
int main()
{   funcs.push_back(sche);funcs.push_back(memmanger);funcs.push_back(Fflush);signal(SIGALRM, handler);alarm(1);while (true){  pause();}return 0;
}

 进程处于暂停状态,由外部驱动发送信号从而实现特定功能,这也是操作系统

子进程退出core dump(ctrl +\)

  • 首先解释什么是Core Dump。当一个进程要异常终止时,可以选择把进程的用户空间内存数据全部保存到磁盘上,文件名通常是core,这叫做Core Dump。
  • 进程异常终止通常是因为有Bug,比如非法内存访问导致段错误,事后可以用调试器检查core文件以查清错误原因,这叫做 Post-mortem Debug (事后调试)。
  • 一个进程允许 产生多大的 core 文件取决于进程的 Resource Limit (这个信息保存 在PCB中)。默认是不允许产生 core 文件的, 因为 core 文件中可能包含用户密码等敏感信息,不安全。
  • 在开发调试阶段可以用 ulimit 命令改变这个限制,允许产生core 文件。 首先用 ulimit 命令改变 Shell 进程的 Resource Limit ,如允许 core 文件最大为 1024K: $ ulimit -c 1024

 

 SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump,但一般情况下防止产生过多的core文件是被禁用的,现在我们来验证一下。

 

 

重新设置

 

为什么会核心转储?开启core dump,直接运行崩溃,gdb,core-file core,直接帮助我们定位到出错行---事后调试

#include <iostream>
#include <unistd.h>
#include <signal.h>int main()
{printf("111");int a=10;a/=0;return 0;
}

 

 core和term的区别:

 waitpid函数中有一个status,第7个比特位表示的是core dump

 


#include <iostream>
#include <unistd.h>
#include <signal.h>
#include<sys/wait.h>
int main()
{pid_t id=fork();if(id==0){printf("111");  int a=10;a/=0;exit(1);}int status=0;waitpid(id,&status,0);
printf("signal:%d,exit code:%d,core dump:%d",(status&0x7F),(status>>8)&0xFF,(status>>7)&0x1);return 0;
}

 

 

 

如何理解软件条件


在操作系统中,信号的软件条件指的是由软件内部状态或特定软件操作触发的信号产生机制。这些条件包括但不限于定时器超时(如alarm函数设定的时间到达)、软件异常(如向已关闭的管道写数据产生的SIGPIPE信号)等。当这些软件条件满足时,操作系统会向相关进程发送相应的信号,以通知进程进行相应的处理。简而言之,软件条件是因操作系统内部或外部软件操作而触发的信号产生。

如何简单快速理解系统闹钟

系统闹钟,其实本质是OS必须自身具有定时功能,并能让用户设置这种定时功能,才可能实现闹钟这样的技术。
现代Linux是提供了定时功能的,定时器也要被管理:先描述,在组织。内核中的定时器数据结构是:

struct timer_list {struct list_head entry;unsigned long expires;void (*function)(unsigned long);unsigned long data;struct tvec_t_base_s *base;
};

我们不在这部分进行深究,为了理解它,我们可以看到:定时器超时时间expires和处理方法
function。
操作系统管理定时器,采用的是时间轮的做法,但是我们为了简单理解,可以把它在组织成为"堆结构"。 

 4.异常

1.除零错误

程序跑起来变成进程了,进程因为语法错误或使用错误导致崩掉了从而发送异常信号

#include<iostream>
#include<unistd.h>
#include<signal.h>
void handlerSig(int sig)
{
std::cout<<getpid()<<"获得了一个信号:"<<sig<<std::endl;
}
int main()
{ for(int i=1;i<=31;i++)signal(i, handlerSig); //给1~31号信号都注册自定义函数int cnt=1;//除零错误cnt/=0;}

kill -l查表,8号是SIGFPE浮点数错误

 cpu里有一个状态寄存器,在跑程序执行运算时出错了会修改标志寄存器,操作系统是软硬件资源的管理者,通过全局指针current就能找到对于的进程从而发送信号

2.野指针访问 

#include<iostream>
#include<unistd.h>
#include<signal.h>
void handlerSig(int sig)
{
std::cout<<getpid()<<"获得了一个信号:"<<sig<<std::endl;
}
int main()
{ for(int i=1;i<=31;i++)signal(i, handlerSig); //给1~31号信号都注册自定义函数int *p =nullptr;
*p=1;//野指针}

 对野指针的访问,11号信号是SIGSEGV段错误

 访问零号地址,操作系统拿到的是虚拟地址,cr3寄存器可通过页表的地址找到页表,MMU将cr3的页表地址和要访问的0号地址做虚拟的页表转化,但是页表下在零号地址没有对应的映射关系,转化失败,硬件报错,cpu的寄存器无法把100写到0号地址处,发送11号信号,终止进程

信号全部都是由操作系统发送的,程序犯错了,操着系统识别到了,根据犯错类型发送信号回该进程执行对于的函数

保存信号

 信号其他相关常见概念

  • 实际执行信号的处理动作称为信号递达(Delivery)
  • 信号从产生到递达之间的状态,称为信号未决(Pending)。
  • 进程可以选择阻塞 (Block )某个信号。
  • 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
  • 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

在内核中的表示
信号在内核中的表示示意图:

  •  每个信号都有两个标志位分别表示阻塞(block)未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。
  • SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
  • SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX.1允许系统递送该信号一次或多次。Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。本章不讨论实时信号。
  1. block标志位共32个比特位二进制01表示对应的1-31号信号是否阻塞
  2. pending标志位工32个比特位对应的01表示是否收到信号
  3. handler是一个函数指针数组,存放着对应的系统调用,1-31号对应的默认执行
  4. task_struct保存着对应的三张表
  5. 对应的信息横着看,如第一行:没有阻塞1号信号且没有收到1号信号,对应的默认执SIG_DFL

上述我们讲的1.键盘2.系统调用3.系统命令4.硬件异常5.软件条件,都是OS操作,本质就是修改位图

 

// 内核结构 2.6.18
struct task_struct {.../* signal handlers */struct sighand_struct *sighand;sigset_t blockedstruct sigpending pending;...}
struct sighand_struct {
atomic_t
count;
struct k_sigaction action[_NSIG]; // #define _NSIG
64
spinlock_t
siglock;
};
struct __new_sigaction {
__sighandler_t sa_handler;
unsigned long
sa_flags;
void
(*sa_restorer)(void);
/* Not used by Linux/SPARC */
__new_sigset_t sa_mask;
};
struct k_sigaction {
struct __new_sigaction sa;
void
__user *ka_restorer;
};
/* Type of a signal handler. */
typedef void (*__sighandler_t)(int);
struct sigpending {
struct list_head list;
sigset_t signal;
};

 sigset_t

从上图来看,每个信号只有⼀个bit的未决标志, 非0即1, 不记录该信号产生了多少次,阻塞标志也是这样表示的。因此, 未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集, 这个类型可以表示每个信号的“有效”或“无效”状态, 在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞, 而在未决信号集中“有 效”和“无效”的含义是该信号是否处于未决状态。阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。
 

信号集操作函数


sigset_t类型对于每种信号用一个bit表示“有效”或“无效”状态, 至于这个类型内部如何存储这些bit则依赖于系统实现, 从使用者的角度是不必关心的, 使用者只能调用以下函数来操作sigset_ t变量,而不应该对它的内部数据做任何解释, 比如用printf直接打印sigset_t变量是没有意义的。

#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);
  • 函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含任何有效信号。
  • 函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表示该信号集的有效信号包括系 统支持的所有信号。
  • 注意,在使用sigset_ t类型的变量之前,⼀定要调用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号。

这四个函数都是成功返回0,出错返回-1。sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种信号,若包含则返回1,不包含则返回0,出错返回-1。

 实操

屏蔽该信号


#include <iostream>
#include <unistd.h>
#include <signal.h>void Print(sigset_t &pending)
{printf("我是一个进程(%d),pending:",getpid());
for(int signo=31;signo>=1;signo--)
{if(sigismember(&pending,signo)){
std::cout<<"1";}else{std::cout<<"0";}
}
std::cout<<std::endl;
}
int main()
{sigset_t block,oblock;sigemptyset(&block);sigemptyset(&oblock);sigaddset(&block,SIGINT);//屏蔽2号信号//设回black表里int n=sigprocmask(SIG_SETMASK,&block,&oblock);(void)n;while(true)
{//获取pending信号集合sigset_t pending;int m=sigpending(&pending);Print(pending);sleep(1);
}}

 

倘若我要是给所有信号都屏蔽了呢?


#include <iostream>
#include <unistd.h>
#include <signal.h>void Print(sigset_t &pending)
{printf("我是一个进程(%d),pending:",getpid());
for(int signo=31;signo>=1;signo--)
{if(sigismember(&pending,signo)){
std::cout<<"1";}else{std::cout<<"0";}
}
std::cout<<std::endl;
}
int main()
{sigset_t block,oblock;sigemptyset(&block);sigemptyset(&oblock);for(int i=1;i<=31;i++){sigaddset(&block,i);}//设回black表里int n=sigprocmask(SIG_SETMASK,&block,&oblock);(void)n;while(true)
{//获取pending信号集合sigset_t pending;int m=sigpending(&pending);Print(pending);sleep(1);
}}

 9号信号不可被捕捉,不可被阻塞!此外还有若干信号也是如此

 先屏蔽在解除,可以看到pending位图从0变1,然后解除完后2号信号立即抵达执行对应方法杀掉进程,看不到从1变0的结果


#include <iostream>
#include <unistd.h>
#include <signal.h>void Print(sigset_t &pending)
{printf("我是一个进程(%d),pending:", getpid());for (int signo = 31; signo >= 1; signo--){if (sigismember(&pending, signo)){std::cout << "1";}else{std::cout << "0";}}std::cout << std::endl;
}
int main()
{sigset_t block, oblock;sigemptyset(&block);sigemptyset(&oblock);sigaddset(&block, 2);int cnt = 0;// 设回black表里int n = sigprocmask(SIG_SETMASK, &block, &oblock);(void)n;while (true){// 获取pending信号集合sigset_t pending;int m = sigpending(&pending);Print(pending);sleep(1);cnt++;if (cnt == 10){// 恢复sigprocmask(SIG_SETMASK, &oblock, nullptr);std::cout << "解除对2号信号的屏蔽" << std::endl;}}
}

 借助自定义捕捉,可以看到由0变1在变0的过程


#include <iostream>
#include <unistd.h>
#include <signal.h>void Print(sigset_t &pending)
{printf("我是一个进程(%d),pending:", getpid());for (int signo = 31; signo >= 1; signo--){if (sigismember(&pending, signo)){std::cout << "1";}else{std::cout << "0";}}std::cout << std::endl;
}
void handler(int sig)
{
std::cout<<"递达"<<sig<<"信号"<<std::endl;
}
int main()
{signal(SIGINT,handler);sigset_t block, oblock;sigemptyset(&block);sigemptyset(&oblock);sigaddset(&block, 2);int cnt = 0;// 设回black表里int n = sigprocmask(SIG_SETMASK, &block, &oblock);(void)n;while (true){// 获取pending信号集合sigset_t pending;int m = sigpending(&pending);Print(pending);if (cnt == 10){// 恢复sigprocmask(SIG_SETMASK, &oblock, nullptr);std::cout << "解除对2号信号的屏蔽" << std::endl;}sleep(1);cnt++;}
}

sigprocmask


调用函数 sigprocmask 可以读取或更改进程的信号屏蔽字(阻塞信号集)。

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
返回值:若成功则为0,若出错则为-1

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

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

sigpending

#include <signal.h>
int sigpending(sigset_t *set);
读取当前进程的未决信号集,通过set参数传出。
调⽤成功则返回0,出错则返回-1

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词