Linux signal 那些事儿(4)信号的deliver顺序【转】

本文深入探讨了Linux系统中信号处理的优先级与处理顺序。通过详细的测试案例与代码实现,揭示了不同信号类别的处理机制及其背后的原理。

转自:http://blog.chinaunix.net/uid-24774106-id-4084864.html

上一篇博文提到了,如果同时有多个不同的信号处于挂起状态,kernel如何选择deliver那个信号。
    
    next_signal 负责从挂起信号中选择deliver的signo:当然,有线程显存私有的penging,有线程组共有的pending,对于线程而言,先从自己私有的pending中选,处理完毕私有的才会去处理线程组共有的pending,这个逻辑的代码在:

  1. int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
  2. {
  3.     int signr;

  4.     /* We only dequeue private signals from ourselves, we don't let
  5.      * signalfd steal them
  6.      */
  7.     signr = __dequeue_signal(&tsk->pending, mask, info);  //线程私有的penging优先
  8.     if (!signr) {
  9.         signr = __dequeue_signal(&tsk->signal->shared_pending,
  10.                      mask, info);
  11.        。。。。
  12. }

    换句话说,如果存在挂起队列中,我们用tkill/tgkill发送的信号会先于用kill发送的信号被deliver,这个我们按下不提,我们看在同一个penging队列中如何挑选下个deliver的signal:

  1. int next_signal(struct sigpending *pending, sigset_t *mask)
  2. {
  3.     unsigned long i, *s, *m, x;
  4.     int sig = 0;

  5.     s = pending->signal.sig;
  6.     m = mask->sig;

  7.     /*
  8.      * Handle the first word specially: it contains the
  9.      * synchronous signals that need to be dequeued first.
  10.      */
  11.     x = *s &~ *m;
  12.     if (x) {
  13.         if (x & SYNCHRONOUS_MASK)
  14.             x &= SYNCHRONOUS_MASK;
  15.         sig = ffz(~x) + 1;
  16.         return sig;
  17.     }

  18.     switch (_NSIG_WORDS) {
  19.     default:
  20.         for (i = 1; i < _NSIG_WORDS; ++i) {
  21.             x = *++s &~ *++m;
  22.             if (!x)
  23.                 continue;
  24.             sig = ffz(~x) + i*_NSIG_BPW + 1;
  25.             break;
  26.         }
  27.         break;

  28.     case 2:
  29.         x = s[1] &~ m[1];
  30.         if (!x)
  31.             break;
  32.         sig = ffz(~x) + _NSIG_BPW + 1;
  33.         break;

  34.     case 1:
  35.         /* Nothing to do */
  36.         break;
  37.     }

  38.     return sig;
  39. }

  1. #define SYNCHRONOUS_MASK \
  2.     (sigmask(SIGSEGV| sigmask(SIGBUS| sigmask(SIGILL\
  3.      sigmask(SIGTRAP| sigmask(SIGFPE| sigmask(SIGSYS))

    上一篇博客讲了处于SYNCHRONOUS_MASK里面的信号是优先处理的信号,他们都是一些硬件相关的信号,多是由于异常出错引起。其次是传统信号,[32,64]之间的实时信号,优先级最低。
    换句话说所有信号分成三个等级,{SIGILL(4),SIGTRAP(5),SIGBUS(7),SIGFPE(8),SIGSEGV(11),SIGSYS(31)},这是第一等级,传统信号中排除第一等级的信号,就是第二等级的信号,[34,64]之间的信号属于第三等级。如果同一等级内,存在多个信号,按照小信号优先的顺序去deliver。
    举个例子:  

  1. kill -10 $signal_pid
  2. kill -3 $signal_pid
  3. kill -12 $signal_pid
  4. kill -11 $signal_pid
  5. kill -39 $signal_pid
  6. kill -2 $signal_pid
  7. kill -5 $signal_pid
  8. kill -4 $signal_pid
  9. kill -36 $signal_pid
  10. kill -24 $signal_pid
  11. kill -38 $signal_pid
  12. kill -37 $signal_pid
  13. kill -31 $signal_pid
  14. kill -8 $signal_pid
  15. kill -7 $signal_pid

    我们可以看到,我们向同一进程发送多个信号,加入进程阻塞了所有信号(当然SIGKILL/SIGSTOP 不考虑)。这时候,这些个信号,都会位于挂起信号之中,一旦进程解除阻塞,那么考验deliver顺序的时候到了。
    我们按照kernel的规则,顺序应该是{4,5,7,8,11,31,          2,3,10,12,24,             36,37,38}这么个顺序。
    写个测试程序:  

  1. root@manu-hacks:~/Dropbox/Note/signal# cat signal_delivery_order.c
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <signal.h>
  6. #include <string.h>
  7. #include <errno.h>


  8. static int sig_cnt[NSIG];
  9. static number= 0 ;
  10. int sigorder[128]= {0};

  11. #define MSG "#%d:receiver signal %d\n"

  12. void handler(int signo)
  13. {
  14.     sigorder[number++] = signo;
  15. }

  16. int main(int argc,char* argv[])
  17. {
  18.     int i = 0;
  19.     int k = 0;
  20.     sigset_t blockall_mask ;
  21.     sigset_t pending_mask ;
  22.     sigset_t empty_mask ;
  23.     struct sigaction sa ;
  24.     

  25.     sigfillset(&blockall_mask);
  26. #ifdef USE_SIGACTION
  27.     sa.sa_handler = handler;
  28.     sa.sa_mask = blockall_mask ;
  29.     sa.sa_flags = SA_RESTART;
  30. #endif


  31.     printf("%s:PID is %ld\n",argv[0],getpid());

  32.     
  33.     for(i = 1; i < NSIG; i++)
  34.     {
  35.         if(i == SIGKILL || i == SIGSTOP)
  36.             continue;
  37. #ifdef USE_SIGACTION
  38.         if(sigaction(i,&sa, NULL)!=0)
  39. #else
  40.         if(signal(i,handler)== SIG_ERR)
  41. #endif
  42.         {
  43.             fprintf(stderr,"sigaction for signo(%d) failed (%s)\n",i,strerror(errno));
  44. //            return -1;
  45.         }
  46.     }

  47.     if(argc > 1)
  48.     {
  49.         int sleep_time = atoi(argv[1]);

  50.         if(sigprocmask(SIG_SETMASK,&blockall_mask,NULL) == -1)
  51.         {
  52.             fprintf(stderr,"setprocmask to block all signal failed(%s)\n",strerror(errno));
  53.             return -2;
  54.         }

  55.         printf("I will sleep %d second\n",sleep_time);

  56.         sleep(sleep_time);
  57.         if(sigpending(&pending_mask) == -1)
  58.         {
  59.             fprintf(stderr,"sigpending failed(%s)\n",strerror(errno));
  60.             return -2;
  61.         }

  62.         for(i = 1 ; i < NSIG ; i++)
  63.         {
  64.             if(sigismember(&pending_mask,i))
  65.             printf("signo(%d) :%s\n",i,strsignal(i));
  66.         }

  67.         sigemptyset(&empty_mask);
  68.         if(sigprocmask(SIG_SETMASK,&empty_mask,NULL) == -1)
  69.         {
  70.             fprintf(stderr,"setprocmask to release all signal failed(%s)\n",strerror(errno));
  71.             return -3;
  72.         }

  73.         for( i = 0 ; i < 1000000 ; i++)
  74.         {
  75.             k = random()%1234567;
  76.             
  77.         }
  78.         
  79.     }

  80.     for(i = 0 ; i< number ; i++)
  81.     {
  82.      if(sigorder[i] != 0)
  83.         {
  84.             printf("#%d: signo=%d\n",i,sigorder[i]);
  85.         }
  86.     }

  87.     return 0;

  88. }

    注意这个USE_SIGACTION宏包裹的部分是我后来想到的,在我开始的版本中用的是signal(i,handler)来注册函数。handler函数很有意思,是我精心设计的:  

  1. void handler(int signo)
  2. {
  3.     sigorder[number++] = signo;
  4. }

   按照执行的顺序,我会讲signo的值记录在全局数组中,等到进程退出前,我打印数组的值,就能得到信号deliver的顺序。
    这个进程会阻塞所有信号一段时间,在这段时间内,我会向该进程发送一坨信号,待阻塞解除后,打印数组的值,从而获得deliver的顺序。
    看下测试程序:   

  1. root@manu-hacks:~/code/c/self/signal_deliver# cat test_order.sh 
    #!/bin/bash 


    if [ $1 -eq 0 ]
    then
    ./sigaction_delivery_order  30  &    #正确的程序
    else
    ./signal_delivery_order  30 &        #我最初的程序,信号处理函数执行期间,没有屏蔽其他信号。
    fi
    signal_pid=$!


    sleep 2
    kill -10  $signal_pid
    kill -3  $signal_pid
    kill -12  $signal_pid
    kill -11  $signal_pid
    kill -39  $signal_pid
    kill -2   $signal_pid
    kill -5   $signal_pid
    kill -4   $signal_pid
    kill -36  $signal_pid
    kill -24  $signal_pid
    kill -38  $signal_pid
    kill -37  $signal_pid
    kill -31  $signal_pid
    kill -8   $signal_pid
    kill -7   $signal_pid


    设计很精巧,设计出这个程序后,我很得意,认为验证deliver传递顺序是水到渠成的事情。
    梦想很丰满,无奈现实很骨感,我看了执行结果,那是当头一棒啊:

  1. root@manu-hacks:~/Dropbox/Note/signal#
  2. root@manu-hacks:~/Dropbox/Note/signal# ./test_order.sh 1 
  3. ./signal_delivery_order:PID is 31403
  4. sigaction for signo(32) failed (Invalid argument)
  5. sigaction for signo(33) failed (Invalid argument)
  6. I will sleep 30 second
  7. root@manu-hacks:~/Dropbox/Note/signal# signo(2) :Interrupt
  8. signo(3) :Quit
  9. signo(4) :Illegal instruction
  10. signo(5) :Trace/breakpoint trap
  11. signo(7) :Bus error
  12. signo(8) :Floating point exception
  13. signo(10) :User defined signal 1
  14. signo(11) :Segmentation fault
  15. signo(12) :User defined signal 2
  16. signo(24) :CPU time limit exceeded
  17. signo(31) :Bad system call
  18. signo(36) :Real-time signal 2
  19. signo(37) :Real-time signal 3
  20. signo(38) :Real-time signal 4
  21. signo(39) :Real-time signal 5
  22. #0: signo=39
  23. #1: signo=38
  24. #2: signo=37
  25. #3: signo=36
  26. #4: signo=24
  27. #5: signo=12
  28. #6: signo=10
  29. #7: signo=3
  30. #8: signo=2
  31. #9: signo=31
  32. #10: signo=11
  33. #11: signo=8
  34. #12: signo=7
  35. #13: signo=5
  36. #14: signo=4

    顺序恰恰相反!!!!
   
我最初完全解释不通,我google了类似的topic,我发现,我不是第一个发现这个问题的人,绚丽也尘埃在一篇博客中提到: 

  1. 在网上找到这样一段话:
  2. 信号的优先级:信号实质上是软中断,中断有优先级,信号也有优先级。如果一个进程有多个未决信号,则对于同一个未决的实时信号,内核将按照发送的顺序来递送信号。如果存 在多个未决的实时信号,则值(或者说编号)越小的越先被递送。如果既存在不可靠信号,又存在可靠信号(实时信号),虽然POSIX对这一情况没有明确规 定,但Linux系统和大多数遵循POSIX标准的操作系统一样,将优先递送不可靠信号。

  3. 经过我反反复复地试验,我发现实验结果和上面描述的刚好相反,信号的编号越大越先被递送,一个进程如果处理SIGQUIT(3),SIGINT(2),SIGHUP(1)(通过”kill -l”可以查看信号的编号),那么先后给该进程发送SIGINT,SIGHUP,SIGQUIT,处理的顺序会是SIGQUIT,SIGINT,SIGHUP,不论改变这个三个信号的发送顺序,处理的顺序都是一样的。

    看到了,这位前辈遇到了和我一样的困惑,测试的结果和kernel完全相反。内核不会错,glibc也肯定不会瞎掺合,一定是我的测试程序存在问题:
    今天我坐公交车上,突然意识到问题的所在,我的信号处理函数没有屏蔽信号!!!
    换句话说,4号信号是先被deliver的,但是还没能handler执行,被5号信号中断掉了,5号信号还没开始执行,被7号信号中断掉了,依次类推,所以我们测试的结果和deliver的结果正好相反。
    意识到这一点,我就改进了我的程序,信号执行期间,屏蔽所有信号,这样,就能测试信号deliver的顺序了。对于我的程序而言就是加上-DUSE_SIGACTION选项,让sigaction安装信号时,指明信号处理函数执行期间,屏蔽所有信号。
     在我的64位Ubuntu上执行,结果和kernel代码以及手册上的一样。也就是说,并不是手册描述错了,而是我们的老的测试程序,在signal处理期间,没有屏蔽其他信号导致混乱。那么按照正确的方法测试:

  1. root@manu-hacks:~/code/c/self/signal_deliver# ./test_order.sh 0
  2. ./sigaction_delivery_order:PID is 3652
  3. sigaction for signo(32) failed (Invalid argument)
  4. sigaction for signo(33) failed (Invalid argument)
  5. I will sleep 30 second
  6. root@manu-hacks:~/code/c/self/signal_deliver# signo(2) :Interrupt
  7. signo(3) :Quit
  8. signo(4) :Illegal instruction
  9. signo(5) :Trace/breakpoint trap
  10. signo(7) :Bus error
  11. signo(8) :Floating point exception
  12. signo(10) :User defined signal 1
  13. signo(11) :Segmentation fault
  14. signo(12) :User defined signal 2
  15. signo(24) :CPU time limit exceeded
  16. signo(31) :Bad system call
  17. signo(36) :Real-time signal 2
  18. signo(37) :Real-time signal 3
  19. signo(38) :Real-time signal 4
  20. signo(39) :Real-time signal 5
  21. #0: signo=4
  22. #1: signo=5
  23. #2: signo=7
  24. #3: signo=8
  25. #4: signo=11
  26. #5: signo=31
  27. #6: signo=2
  28. #7: signo=3
  29. #8: signo=10
  30. #9: signo=12
  31. #10: signo=24
  32. #11: signo=36
  33. #12: signo=37
  34. #13: signo=38
  35. #14: signo=39

     和我们预想的完全符合,和内核代码已经手册完全一样。那么这个问题完美解决。

  1. {4,5,7,8,11,31, 2,3,10,12,24, 36,37,38}

     多个挂起信号时,delivery的策略如下:     
    1  {SIGILL(4),SIGTRAP(5),SIGBUS(7),SIGFPE(8),SIGSEGV(11),SIGSYS(31)}第一等级
    2  非实时信号中其他信号是第二等级(SIGKILL SIGSTOP除外)
    3  实时信号是第三等级。
    存在第一等级的信号挂起,那么优先选择第一等级,
     没有第一等级,那么如果存在第二等级的信号,优先选择第二等级内信号。 
    既没有第一等级,又没有第二等级,那么选择第三等级的信号。
    如果同一个等级内都存在多个挂起信号,则小信号优先。

    这只是我们用程序测试的结果,其实systemtap提供了signal_deliver这个event让我们monitor,我们可以直观的看到信号传递的顺序:   

  1. root@manu-hacks:~/code/c/self/signal_deliver# cat signal_deliver.stp
  2. probe kernel.trace("signal_deliver"){
  3.     if(pid() == target())
  4.     {
  5.      printf("signo(%2d) is delivered to PID %8d\n",$sig,pid());
  6.     }
  7. }

     我们可以用test_order.sh 1 ,故意用signal那个给我带来困扰的程序测试,我们会看到,传递的顺序依然是对的,这证明了我前面的推测,4是最先deliver的,只不过是因为没有屏蔽其他信号,被5号信号中断了,5又被7号信号中断了,依次类推,导致了我们看到了相反的执行顺序,给我们带来了困扰。  

  1. root@manu-hacks:~/code/c/self/signal_deliver# ./test_order.sh 1
    ./signal_delivery_order:PID is 4051
    sigaction for signo(32) failed (Invalid argument)
    sigaction for signo(33) failed (Invalid argument)
    I will sleep 30 second
    root@manu-hacks:~/code/c/self/signal_deliver# stap -x 4051 signal_deliver.stp 
    signo(2) :Interrupt
    signo(3) :Quit
    signo(4) :Illegal instruction
    signo(5) :Trace/breakpoint trap
    signo(7) :Bus error
    signo(8) :Floating point exception
    signo(10) :User defined signal 1
    signo(11) :Segmentation fault
    signo(12) :User defined signal 2
    signo(24) :CPU time limit exceeded
    signo(31) :Bad system call
    signo(36) :Real-time signal 2
    signo(37) :Real-time signal 3
    signo(38) :Real-time signal 4
    signo(39) :Real-time signal 5
    #0: signo=39
    #1: signo=38
    #2: signo=37
    #3: signo=36
    #4: signo=24
    #5: signo=12
    #6: signo=10
    #7: signo=3
    #8: signo=2
    #9: signo=31
    #10: signo=11
    #11: signo=8
    #12: signo=7
    #13: signo=5
    #14: signo=4
    signo( 4)  is delivered to PID     4051
    signo( 5)  is delivered to PID     4051
    signo( 7)  is delivered to PID     4051
    signo( 8)  is delivered to PID     4051
    signo(11)  is delivered to PID     4051
    signo(31)  is delivered to PID     4051
    signo( 2)  is delivered to PID     4051
    signo( 3)  is delivered to PID     4051
    signo(10)  is delivered to PID     4051
    signo(12)  is delivered to PID     4051
    signo(24)  is delivered to PID     4051
    signo(36)  is delivered to PID     4051
    signo(37)  is delivered to PID     4051
    signo(38)  is delivered to PID     4051
    signo(39)  is delivered to PID     4051


    ^Croot@manu-hacks:~/code/c/self/signal_deliver# 


     话说systemtap提供了很多signal相关的example脚本,非常好用,如下图,收集signal的发送情况:
    

参考文献
Linux实时信号排队
2  LKML:

    Subject [PATCH -tip v4 2/3] tracepoint: Add signal deliver even
<think>首先,用户的问题是:“我想获取信号以传递 请问get signal to deliver”。这看起来是关于Linux内核中的信号处理,特别是“get_signal_to_deliver”函数。用户可能是在询问如何获取信号以传递给处理函数。 从系统级指令,我需要: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,例如[^1]。 用户提供了多个引用: - [^1]:描述get_signal_to_deliver函数返回值大于0时调用handle_signal函数。 - [^2]:函数的签名:int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka, struct pt_regs *regs, void *cookie)。 - [^3]:详细解释函数的行为,包括它只在CPU返回到用户态时调用,处理挂起信号等。 - [^4]:讨论SIGCONT信号和停止信号的处理。 - [^5]:一个堆栈跟踪,显示get_signal_to_deliver信号处理流程中的位置。 用户的问题核心是“get signal to deliver”,意思是获取信号以传递。在Linux内核中,get_signal_to_deliver函数负责从挂起信号队列中获取信号,以便传递给用户空间的处理函数。 我的回答应该: 1. 解释get_signal_to_deliver函数的作用。 2. 描述其工作流程。 3. 使用中文。 4. 结构清晰,逐步解决问题。 5. 在末尾生成相关问题。 6. 在回答中引用时添加引用标识。 结构化回答: - **引言**:简要解释函数的作用。 - **详细解释**:基于引用,描述函数的行为、参数和返回值。 - **工作流程**:说明它在信号处理中的位置,如从do_signal调用。 - **关键点**:覆盖信号处理、忽略信号、默认操作等。 - **生成相关问题**:在最后添加相关问题。 确保回答真实可靠:基于提供的引用和标准Linux内核知识。 最终,添加相关问题: - 例如:如何在内核中添加自定义信号处理?get_signal_to_deliver函数的优化方法?信号处理中的竞争条件如何避免? 现在,构建回答。</think>### get_signal_to_deliver 函数详解 在 Linux 内核的信号处理机制中,`get_signal_to_deliver` 函数是一个关键的内核函数,负责从当前进程的挂起信号队列中获取信号,并准备将其传递给用户空间的信号处理函数。该函数通常在 CPU 即将返回到用户态时被调用(例如,在中断处理或系统调用返回前),以确保信号能及时被处理[^3]。以下我将逐步解释其工作原理、参数和典型行为,帮助您理解如何“获取信号以传递”。 #### 1. **函数作用与调用时机** - `get_signal_to_deliver` 的主要任务是遍历当前进程的挂起信号队列(包括 `pending` 和 `shared_pending` 队列),并选择一个合适的信号进行处理。如果信号需要传递到用户空间(例如,用户通过 `signal()` 或 `sigaction()` 设置了自定义处理函数),函数会返回该信号的详细信息[^1]。 - 调用时机:该函数仅在 CPU 准备返回到用户态时被激活,由内核检查 `TIF_SIGPENDING` 标志触发。这意味着信号处理不会在内核态中发生,而是延迟到进程即将返回用户空间时执行[^3]。 - 典型调用栈:`do_signal()` → `get_signal_to_deliver()` → `handle_signal()`。其中: - `do_signal()` 负责处理所有未阻塞的挂起信号。 - `handle_signal()` 在信号需要用户态处理时被调用,用于设置信号处理程序的执行环境(如修改硬件上下文)[^3]。 #### 2. **函数参数与返回值** 函数签名如下(基于引用[^2]): ```c int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka, struct pt_regs *regs, void *cookie) ``` - **参数说明**: - `info`:指向 `siginfo_t` 结构的指针,用于存储信号的详细信息(如信号编号、发送者 PID 等)。 - `return_ka`:指向 `struct k_sigaction` 的指针,用于返回信号的处理动作(包括用户定义的函数指针或默认行为)。 - `regs`:指向 `struct pt_regs` 的指针,包含当前进程的寄存器状态,用于在信号处理时保存和恢复上下文。 - `cookie`:一个不透明指针,通常用于内核内部扩展。 - **返回值**: - 如果返回值大于 0,表示成功获取到一个需要传递到用户空间的信号(即用户设置了自定义处理函数),此时内核会调用 `handle_signal()` 来执行后续操作[^1]。 - 如果返回值小于或等于 0,表示没有信号需要传递(例如,信号被忽略、执行默认操作,或队列为空),内核可能直接忽略信号或执行默认行为(如终止进程)[^3]。 #### 3. **工作流程详解** `get_signal_to_deliver` 函数在内部执行以下步骤: 1. **检查挂起信号队列**:反复调用 `dequeue_signal()` 函数,直到 `pending`(进程私有信号)和 `shared_pending`(线程组共享信号)队列为空。队列中的信号按优先级排序(例如,实时信号优先)[^3]。 2. **处理信号类型**: - 如果信号被忽略(例如,通过 `SIG_IGN` 设置),函数会跳过该信号。 - 如果信号需要执行默认操作(如 `SIGKILL` 终止进程),函数直接在内核态处理,不返回用户空间。 - 如果信号有用户态处理函数,函数填充 `info` 和 `return_ka` 参数,并返回正数。 3. **特殊信号处理**:对于停止信号(如 `SIGSTOP`)和继续信号(如 `SIGCONT`),函数会更新线程组状态。例如: - 当接收到 `SIGCONT` 时,如果线程组正在停止(`group_stop_count > 0`),函数会清除 `SIGNAL_STOP_DEQUEUED` 标志,确保停止过程被取消[^4]。 4. **上下文准备**:在返回用户空间前,函数通过 `setup_rt_frame()` 修改进程的硬件上下文(如栈指针),为信号处理函数的执行做好准备[^3]。 #### 4. **关键特性与注意事项** - **性能与竞争条件**:由于信号处理涉及用户态和内核态的切换,`get_signal_to_deliver` 可能成为性能瓶颈。内核通过仅在返回到用户态时调用该函数来减少开销。但竞争条件(如信号在检查队列时被添加)需要小心处理[^3]。 - **错误处理**:如果函数返回负数或 0,内核可能触发进程终止或记录错误(如内存储),这在调试中常见(引用[^5] 展示了函数在崩溃堆栈中的位置)。 - **安全性**:函数确保信号处理不会在内核态执行,防止恶意信号导致系统崩溃。用户自定义处理函数始终在用户空间运行[^1]。 总之,`get_signal_to_deliver` 是 Linux 信号传递的核心机制,它桥接内核和用户空间,确保信号高效、安全地处理。如果您需要修改或扩展信号行为,建议查阅内核源码(如 `kernel/signal.c`)以获取更多细节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值