【Linux】信号的产生、生存与处置惩罚

[复制链接]
发表于 2026-1-26 10:09:46 | 显示全部楼层 |阅读模式
目次

信号
什么是信号
信号的产生
信号的生存与处置惩罚
信号的捕获
信号的捕获流程
signal函数 
 sigaction函数
sigprocmask函数 


信号

什么是信号

信号是Linux提供的一种向指定历程发送特定变乱的方式。
Linux中规定了64种信号,此中1到31号为不可靠信号(大概丢失),32到64号为可靠信号(不大概丢失)。

注:绝大多数的默认操纵都是停止历程。 
信号取值名称作用默认操纵1SIGHUP挂起2SIGINT停止3SIGQUIT退出4SIGILL非法指令5SIGTRAP断点或陷阱指令6SIGABRTabort发出的信号7SIGBUS非法内存访问8SIGFPE浮点非常9SIGKILLkill信号不能被忽略、处置惩罚和壅闭10SIGUSR1用户信号111SIGSEGV无效内存访问12SIGUSR2用户信号213SIGPIPE管道粉碎,没有读端的管道写数据14SIGALRMalarm发出的信号15SIGTERM停止信号16SIGSTKFLT栈溢出17SIGCHLD子历程退出默认忽略18SIGCONT历程继续19SIGSTOP历程制止不能被忽略、处置惩罚和壅闭20SIGTSTP历程制止21SIGTTIN历程制止,配景历程从终端读数据时22SIGTTOU历程制止,配景历程向终端写数据时23SIGURGIO有告急数据到达当进步程默认忽略24SIGXCPU历程的CPU时间片到期25SIGXFSZ文件巨细的超出上限26SIGVTALRM捏造时钟超时27SIGPROFprofile时钟超时28SIGWINCH窗口巨细改变默认忽略29SIGIOIO干系30SIGPWR关机默认忽略31SIGSYS体系调用非常         信号通常履历以下三个阶段,产生、生存、递达(处置惩罚)。处于生存时期的信号处于一个叫未决的状态。可以简朴明白为信号到了,但未处置惩罚的状态。 
        举个例子,你的朋侪让你给他买瓶汽水,但由于你手头上有更加告急的事,以是你岑寂地记下给他买瓶汽水这个信号比及你忙完后才去实验。此时你对信号的处置惩罚是壅闭,信号在被递达前都处于未决状态。固然,除了壅闭你也可以选择忽略,忽略则是信号到了但是你直接无视。
 

信号的产生

信号的产生是异步的,他可以大概使一个正在实验的历程被异步打断,转而行止理一个突发变乱。
信号的产生大抵有以下五种缘故原由,但从始至终信号都是有OS来发送的
   

  • 通过kill下令向指定历程发信号。例:kill -2 8888  (2是信号取值,代表2号信号。8888是一个历程的pid)
  • 键盘也可以产生信号(Ctrl+c  就相当于向当前举行发送2号信号)
  • 体系调用,比方  int kill(pid_t pid,int sig)函数。
  • 软件条件,比方管道读端关闭,写端不绝写,那么OS就会向他发送13号信号SIGPIPE关闭写端。(这就是读端关闭写端就不写了的缘故原由)
  • 非常,出现非常历程制止运行也是OS对其发送了信号。 
  信号的生存与处置惩罚

每个历程都有一个对应的task_struct,而在task_struct中有一个管理信号的结构。这个结构告急分为三部门。pending、block是两张位图,pengding的每个位置都代表对应的信号,0或1代表该信号是否未决(到达但未处置惩罚)。clock的每个位置同样代表对应的信号,0或1代表该信号是否被壅闭。而handle是一个函数指针数组,他的每个位置都对应信号的处置惩罚方法,不修改则为默认,可以通过体系调用举行自界说。

当信号到来时,OS就会遍历pending位图,为0则检察下一个比特位,为1则检察block位图的对应位置,此时block的该比特位为1则不做处置惩罚(被壅闭),为0则将对应位置pending置0再实验对应位置的handle操纵。
信号的捕获

信号的捕获流程


 内核态对比用户态最显着的区别的内核态拥有更大的权利。以是举行自界说的信号处置惩罚时,要从内核态回到用户态,这是对操纵体系的掩护,防止用户界说的函数在内核态举行非法操纵。
signal函数 

作用:捕获一个指定信号,设定该信号的操纵方法。
   

  • #include <signal.h>
    typedef void (*sighandler_t)(int);
    sighandler_t signal(int signum, sighandler_t handler);
  • 第一个参数signum代表信号的取值。
  • 第二个参数有三种,第一种是返回值为void,参数为int的自界说函数指针;第二种是  SIG_IGN  代表忽略一个信号;SIG_DFL代表默认处置惩罚。
  1. #include <iostream>
  2. #include <unistd.h>
  3. #include <signal.h>
  4. void sigcb(int sig)
  5. {
  6.     std::cout << "get a sig : " << sig << std::endl;
  7. }
  8. int main()
  9. {
  10.     signal(2, sigcb);
  11.     while (true)
  12.     {
  13.         std::cout << "process is running , pid : " << getpid() << std::endl;
  14.         sleep(1);
  15.     }
  16.     return 0;
  17. }
复制代码
向历程3348发送2号信号,历程收到信号后实验了sigcb函数。 

 sigaction函数

作用:捕获一个指定信号,设定该信号的操纵方法。
   

  • #include <signal.h>                                                                                                            int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
  • 第一个参数signum代表信号的取值。
  • 第二个参数设定新的结构体(结构体中包罗handle函数指针,与signal中的划一)
  • 第三个参数输出原来的结构体
  1. #include <iostream>
  2. #include <unistd.h>
  3. #include <signal.h>
  4. void sigcb(int sig)
  5. {
  6.     std::cout << "get a sig : " << sig << std::endl;
  7. }
  8. /*struct sigaction {
  9.     void (*sa_handler)(int);
  10.     void (*sa_sigaction)(int, siginfo_t *, void *);
  11.     sigset_t sa_mask;
  12.     int sa_flags;
  13.     void (*sa_restorer)(void);
  14. }
  15. */
  16. int main()
  17. {
  18.     struct sigaction act;
  19.     act.sa_handler = sigcb;
  20.     sigaction(2, &act, nullptr);
  21.     while (true)
  22.     {
  23.         std::cout << "process is running , pid : " << getpid() << std::endl;
  24.         sleep(1);
  25.     }
  26.     return 0;
  27. }
复制代码
sigprocmask函数 

作用: sigprocmask函数用于查抄或修改当进步程的信号屏蔽字(signal mask)
   

  • #include <signal.h>                                                                                                          int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
  • 第一个参数,操纵标志,决定怎样修改信号屏蔽字。有三个选项:                 SIG_BLOCK:把 set 指向的信号会合的信号添加到当前信号屏蔽字中。         SIG_UNBLOCK:从当前信号屏蔽字中移除 set 指向的信号会合的信号。     SIG_SETMASK:用 set 指向的信号集更换当前信号屏蔽字。
  • const sigset_t *set:指向要修改的新信号集的指针。
  • sigset_t *oldset:如果不为 NULL,则存储之前的信号屏蔽字。
  1. #include <iostream>
  2. #include <unistd.h>
  3. #include <signal.h>
  4. void PrintfPending(sigset_t &pending)
  5. {
  6.     std::cout << "cur process pid : " << getpid() << "\n";
  7.     std::cout << "pending signo :";
  8.     for (int signo = 31; signo >= 1; signo--)
  9.     {
  10.         if (sigismember(&pending, signo))
  11.         {
  12.             std::cout << 1;
  13.         }
  14.         else
  15.         {
  16.             std::cout << 0;
  17.         }
  18.     }
  19.     std::cout << "\n";
  20. }
  21. int main()
  22. {
  23.     signal(2, handler);
  24.     sigset_t block_set, old_set;
  25.     sigemptyset(&block_set);
  26.     sigemptyset(&old_set);
  27.     sigaddset(&block_set, 2);
  28.     sigprocmask(SIG_BLOCK, &block_set, &old_set);
  29.     while (true)
  30.     {
  31.         sigset_t pending;
  32.         sigpending(&pending);
  33.         PrintfPending(pending);
  34.         sleep(2);
  35.     }
  36.     return 0;
  37. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表