【问题标题】:Signal number to name?要命名的信号编号?
【发布时间】:2025-12-09 15:50:01
【问题描述】:

如何从信号编号中快速获取信号名称? 有strsignal(),但我只想要名字,例如SIGUSR1

换句话说,如果我们有像这样的宏 SIGUSR1 -> 12 我们有类似的东西吗 12 -> SIGUSR1?

【问题讨论】:

  • 在我的/usr/include/sys/signal.h 中,所有的信号名称都只是#defined 常量。这意味着在编译器运行时,预处理器已经用常量整数替换了它们——就程序而言,符号名称不存在。你到底想在这里做什么?
  • @CarlNorum,没什么特别的,只是花哨的输出。我认为实际名称可以存储在其他地方以便我可以得到它们。
  • 如果你想要“花哨的输出”,那不正是strsignal 的用途吗?
  • 是的,它很漂亮,但不是我想要的方式:) 只是好奇,根本不是问题。
  • 我的 strsignal 手册页说您可以直接从 sys_signame 获取名称。

标签: c unix


【解决方案1】:

我的strsignal(3) 手册页说您可以直接从sys_signame 数组中获取名称。这是我为测试它而编写的一个简单示例程序:

#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

void upcase(char *s)
{
    while (*s)
    {
        *s = toupper(*s);
        s++;        
    }
}

int main(void)
{    
    for (int sig = 1; sig < NSIG; sig++)
    {
        char *str = strdup(sys_signame[sig]);
        if (!str)
            return -1;

        upcase(str);
        printf("%2d -> SIG%s\n", sig, str);

        free(str);
    }

    return 0;
}

我认为这个程序会产生您正在寻找的输出:

$ ./example 
 1 -> SIGHUP
 2 -> SIGINT
 3 -> SIGQUIT
 4 -> SIGILL
 5 -> SIGTRAP
 6 -> SIGABRT
 7 -> SIGEMT
 8 -> SIGFPE
 9 -> SIGKILL
10 -> SIGBUS
11 -> SIGSEGV
12 -> SIGSYS
13 -> SIGPIPE
14 -> SIGALRM
15 -> SIGTERM
16 -> SIGURG
17 -> SIGSTOP
18 -> SIGTSTP
19 -> SIGCONT
20 -> SIGCHLD
21 -> SIGTTIN
22 -> SIGTTOU
23 -> SIGIO
24 -> SIGXCPU
25 -> SIGXFSZ
26 -> SIGVTALRM
27 -> SIGPROF
28 -> SIGWINCH
29 -> SIGINFO
30 -> SIGUSR1
31 -> SIGUSR2

【讨论】:

  • 似乎只有strsignal 似乎是由POSIX 定义的,像sys_signame 这样的数组声明似乎是扩展。
  • 因此,如果他在 OS X 上,这可能是对 OP 所遇到问题的答案,但不是对问题的答案。
  • ⁺¹,但请注意,数组名称可能已更改 - 它是 sys_siglist
【解决方案2】:

glib 2.32(2020-08-05 发布)引入了函数sigabbrev_np(int)。从那个版本开始,你也不能再使用sys_siglist[]了。

来自man strsignal

sigabbrev_np() 函数返回信号的缩写名称,sig。例如,给定值SIGINT,它返回字符串"INT"
[...]
sigdescr_np() 和 sigdabbrev_np() 最早出现在 glibc 2.32 中。 从 2.32 版开始,glibc 不再导出 sys_siglist 符号。

来自release notes

已添加函数sigabbrev_npsigdescr_npsigabbrev_np 返回缩写的信号名称(例如,"HUP" 代表 SIGHUP)[...] 对于无效的信号编号,两个函数都返回 NULL。

应该使用它们而不是sys_siglistsys_sigabbrev,它们都是线程和异步信号安全的。这些函数是 GNU 扩展。

【讨论】:

    【解决方案3】:

    在 Ubuntu 16.04 和 MIPS 上测试了下面的代码,它工作正常。

    #include <signal.h>
    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>
    
    extern const char * const sys_siglist[];
    
    void upcase(char *s)
    {
        while (*s)
        {
            *s = toupper(*s);
            s++;
        }
    }
    
    int main(void)
    {
        int sig;
        /*
           NSIG returns number of signals available in a system
           and it may vary according to platforms;Found on Ubuntu-16.04 it return 65
           where as in MIPS it is 31; Found in both the platforms it leads to core dump
           after signal 31 so limiting scanning of signal till 31 instead of using NSIG
        */
        for (sig = 1; sig < 32; sig++)
        {
            char *str = strdup(sys_siglist[sig]);
            if (!str)
                return -1;
    
            upcase(str);
            printf("%2d -> SIG%s\n", sig, str);
    
            free(str);
        }
    
        return 0;
    }
    

    上述代码在 Ubuntu-16.04(Intel x86_64 GNU/Linux)上的输出:

     1 -> SIGHANGUP
     2 -> SIGINTERRUPT
     3 -> SIGQUIT
     4 -> SIGILLEGAL INSTRUCTION
     5 -> SIGTRACE/BREAKPOINT TRAP
     6 -> SIGABORTED
     7 -> SIGBUS ERROR
     8 -> SIGFLOATING POINT EXCEPTION
     9 -> SIGKILLED
    10 -> SIGUSER DEFINED SIGNAL 1
    11 -> SIGSEGMENTATION FAULT
    12 -> SIGUSER DEFINED SIGNAL 2
    13 -> SIGBROKEN PIPE
    14 -> SIGALARM CLOCK
    15 -> SIGTERMINATED
    16 -> SIGSTACK FAULT
    17 -> SIGCHILD EXITED
    18 -> SIGCONTINUED
    19 -> SIGSTOPPED (SIGNAL)
    20 -> SIGSTOPPED
    21 -> SIGSTOPPED (TTY INPUT)
    22 -> SIGSTOPPED (TTY OUTPUT)
    23 -> SIGURGENT I/O CONDITION
    24 -> SIGCPU TIME LIMIT EXCEEDED
    25 -> SIGFILE SIZE LIMIT EXCEEDED
    26 -> SIGVIRTUAL TIMER EXPIRED
    27 -> SIGPROFILING TIMER EXPIRED
    28 -> SIGWINDOW CHANGED
    29 -> SIGI/O POSSIBLE
    30 -> SIGPOWER FAILURE
    31 -> SIGBAD SYSTEM CALL
    

    以上代码在busybox(MIPS、Cavium)上的输出:

     1 -> SIGHANGUP
     2 -> SIGINTERRUPT
     3 -> SIGQUIT
     4 -> SIGILLEGAL INSTRUCTION
     5 -> SIGTRACE/BREAKPOINT TRAP
     6 -> SIGABORTED
     7 -> SIGEMT TRAP
     8 -> SIGFLOATING POINT EXCEPTION
     9 -> SIGKILLED
    10 -> SIGBUS ERROR
    11 -> SIGSEGMENTATION FAULT
    12 -> SIGBAD SYSTEM CALL
    13 -> SIGBROKEN PIPE
    14 -> SIGALARM CLOCK
    15 -> SIGTERMINATED
    16 -> SIGUSER DEFINED SIGNAL 1
    17 -> SIGUSER DEFINED SIGNAL 2
    18 -> SIGCHILD EXITED
    19 -> SIGPOWER FAILURE
    20 -> SIGWINDOW CHANGED
    21 -> SIGURGENT I/O CONDITION
    22 -> SIGI/O POSSIBLE
    23 -> SIGSTOPPED (SIGNAL)
    24 -> SIGSTOPPED
    25 -> SIGCONTINUED
    26 -> SIGSTOPPED (TTY INPUT)
    27 -> SIGSTOPPED (TTY OUTPUT)
    28 -> SIGVIRTUAL TIMER EXPIRED
    29 -> SIGPROFILING TIMER EXPIRED
    30 -> SIGCPU TIME LIMIT EXCEEDED
    31 -> SIGFILE SIZE LIMIT EXCEEDED
    

    【讨论】:

    • 但没有 SIGHANGUP 或 SIGINTERRUPT,它们是 SIGHUP 和 SIGINT。带空格的更明显是错误的。这里唯一的就是 SIGQUIT...
    【解决方案4】:

    正如Jens Gustedt 多年前在 cmets 中指出的那样,sys_signamesys_siglist 不可移植。

    由于此问题被标记为[unix],因此您可以最方便地使用#ifdef 来映射特定于您的环境的名称和数值。比如:

    //
    //  const char * signame(int s)
    //
    //    return the name of the given signal number as a string,
    //    or NULL if the number is unrecognized.
    //
    #define _POSIX_C_SOURCE 200809L
    #include <signal.h>
    
    #define SIGNAMEANDNUM(s)  { #s, s }
    
    static struct {
      const char *name,
      int value,
    } known_signals[] = {
      SIGNAMEANDNUM(SIGABRT),   // get the POSIX signals
      SIGNAMEANDNUM(SIGALRM),
      SIGNAMEANDNUM(SIGBUS),
      SIGNAMEANDNUM(SIGCHLD),
      /* ... */
      SIGNAMEANDNUM(SIGXFSZ),
    #ifdef SIGUNUSUAL           // get nonstandard signals 
      SIGNAMEANDNUM(SIGUNUSUAL),
    #endif
      /* ... */
    };
    
    const char *
    signame(int s) {
      const char *name = NULL;
    
      for (int i = 0; i < sizeof(known_signals)/sizeof(*known_signals); i++) {
        if (s == known_signals[i].value) {
          name = known_signals[i].name;
          break;
        }
      }
    
      return name;
    }
    

    当然,这需要您对平台的一些先验知识。

    【讨论】:

      【解决方案5】:

      可能,你可以声明一个全局数组,像这样

      char *signame[]={"INVALID", "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE", "SIGKILL", "SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGSTKFLT", "SIGCHLD", "SIGCONT", "SIGSTOP", "SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGURG", "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGPOLL", "SIGPWR", "SIGSYS", NULL};
      

      并可以使用它在信号处理程序中打印信号名称,例如

      void sig_handler(int signum){
           printf("Received signal : %s\n", signame[signum]);
      }
      

      【讨论】:

      • 这是个坏主意,因为大多数信号编号都依赖于实现。 SIGKILL 始终为 9,但并非所有信号都有这样的固定数字。
      • 但最好在信号处理程序中使用,因为 strsignal 不是信号安全的