我们来深入学习 rt_sigqueueinfo 系统调用,在 Linux 中,进程间通信(IPC)有多种方式,信号(Signal)是其中一种轻量级的通知机制。通常,我们使用 kill() 来发送一个简单的信号,或者使用 sigqueue() 来发送一个信号并附带一小段数据(一个整数或指针)。
1. 函数介绍 在 Linux 中,进程间通信(IPC)有多种方式,信号(Signal)是其中一种轻量级的通知机制。通常,我们使用 kill() 来发送一个简单的信号,或者使用 sigqueue() 来发送一个信号并附带一小段数据(一个整数或指针)。
rt_sigqueueinfo 是一个更底层、更强大但也更危险的系统调用。它允许你向另一个进程发送一个信号,并且可以完全自定义随信号一起传递的 siginfo_t 结构体中的所有信息。这包括发送者的 PID、信号产生的原因代码(si_code)、以及附带的数据等。
为什么危险?因为它强大的自定义能力意味着发送者可以伪造信号的来源和原因。例如,一个普通用户进程可以伪造自己是内核(si_code 为 SI_KERNEL)或其他进程发送的信号。因此,这个系统调用通常受到严格的权限检查,普通应用程序一般不应该直接使用它。
什么时候会用到?主要是在实现更高级别的信号发送函数(如 sigqueue)时,由 C 库(glibc)内部调用,或者在一些非常特殊的、需要精确控制信号信息的系统级编程中。
对于 Linux 编程小白:你更可能使用 sigqueue() 函数,它更安全、更易用,并且能满足绝大多数需要发送带数据信号的场景。
2. 函数原型 1 2 3 4 5 6 7 8 #include <sys/syscall.h> #include <unistd.h> #include <signal.h> long syscall (SYS_rt_sigqueueinfo, pid_t pid, int sig, siginfo_t *uinfo) ;
用户空间更常用、更安全的替代函数:
1 2 3 4 #include <signal.h> int sigqueue (pid_t pid, int sig, const union sigval value) ;
3. 功能 向指定进程 ID (pid) 发送指定信号 (sig),并允许发送者完全指定 siginfo_t 结构体 (uinfo) 中包含的详细信息。
4. 参数 pid:
sig:
uinfo:
5. 返回值
失败: 返回 -1,并设置全局变量 errno 来指示具体的错误原因:
EAGAIN: (对于实时信号) 已达到接收者排队信号的最大数量限制 (RLIMIT_SIGPENDING)。
EPERM: 调用者没有权限发送信号给目标进程(例如,普通用户不能向 root 进程发送任意伪造的信号)。
EINVAL: sig 是无效的信号号,或者 uinfo 中的 si_code 是无效的或不允许由用户设置的。
ESRCH: 找不到 pid 指定的进程或进程组。
6. 相似函数或关联函数
siginfo_t: 包含信号详细信息的结构体。主要字段包括:
si_signo: 信号编号(内核会设置)。
si_errno: 如果非零,表示伴随信号的错误代码(内核会设置)。
si_code: 信号产生的原因代码(例如 SI_USER, SI_QUEUE, SI_TIMER 等)。这是 rt_sigqueueinfo 允许用户自定义的关键字段,但也因此危险。
si_pid: 发送信号的进程 ID(通常由内核设置,但 rt_sigqueueinfo 可能允许伪造)。
si_uid: 发送信号的用户 ID(通常由内核设置)。
si_value: 伴随信号的用户数据(union sigval,包含 sival_int 或 sival_ptr)。
… 还有其他针对特定信号类型的字段。
7. 示例代码 由于直接使用 rt_sigqueueinfo 需要特殊权限且容易误用,下面的示例将演示如何通过 syscall 调用它,并说明其风险。同时,也会提供一个使用标准 sigqueue 的对比示例。
警告:直接使用 rt_sigqueueinfo 可能会因为权限问题而失败,尤其是在没有 root 权限的情况下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <string.h> #include <sys/syscall.h> #include <errno.h> #include <sys/types.h> void signal_handler (int sig, siginfo_t *info, void *context) { printf ("Received signal %d\n" , sig); printf (" si_signo: %d\n" , info->si_signo); printf (" si_code: %d" , info->si_code); switch (info->si_code) { case SI_USER: printf (" (SI_USER: kill, sigsend or raise)\n" ); break ; case SI_QUEUE: printf (" (SI_QUEUE: sigqueue)\n" ); break ; case SI_KERNEL: printf (" (SI_KERNEL: sent by kernel)\n" ); break ; case SI_TKILL: printf (" (SI_TKILL: tkill or tgkill)\n" ); break ; default : printf (" (Other code)\n" ); break ; } printf (" si_pid: %d\n" , info->si_pid); printf (" si_uid: %d\n" , info->si_uid); if (info->si_code == SI_QUEUE) { printf (" si_value.sival_int: %d\n" , info->si_value.sival_int); printf (" si_value.sival_ptr: %p\n" , info->si_value.sival_ptr); } }int main () { pid_t my_pid = getpid (); struct sigaction sa; siginfo_t si_to_send; union sigval data_to_send = {.sival_int = 999 }; printf ("My PID is: %d\n" , my_pid); memset (&sa, 0 , sizeof (sa)); sa.sa_sigaction = signal_handler; sigemptyset (&sa.sa_mask); sa.sa_flags = SA_SIGINFO; if (sigaction (SIGUSR1, &sa, NULL ) == -1 ) { perror ("sigaction" ); exit (EXIT_FAILURE); } printf ("\n--- Using sigqueue (Recommended) ---\n" ); printf ("Sending SIGUSR1 with data %d using sigqueue()...\n" , data_to_send.sival_int); if (sigqueue (my_pid, SIGUSR1, data_to_send) == -1 ) { perror ("sigqueue" ); } sleep (1 ); printf ("\n--- Using rt_sigqueueinfo (Not Recommended) ---\n" ); memset (&si_to_send, 0 , sizeof (si_to_send)); si_to_send.si_signo = SIGUSR1; si_to_send.si_code = SI_QUEUE; si_to_send.si_pid = my_pid; si_to_send.si_uid = getuid (); si_to_send.si_value = data_to_send; printf ("Attempting to send SIGUSR1 with forged info using rt_sigqueueinfo()...\n" ); printf ("(This will likely fail with EPERM unless run with special privileges)\n" ); long result = syscall (SYS_rt_sigqueueinfo, my_pid, SIGUSR1, &si_to_send); if (result == -1 ) { perror ("rt_sigqueueinfo" ); printf ("Error: rt_sigqueueinfo failed. This is expected for unprivileged processes.\n" ); printf ("Errno: %d\n" , errno); if (errno == EPERM) { printf ("Reason: EPERM - Operation not permitted (insufficient privileges to forge signal info).\n" ); } } else { printf ("rt_sigqueueinfo succeeded (unexpected for unprivileged user).\n" ); } sleep (1 ); printf ("\n--- Forging signal as from Kernel (Highly Not Recommended) ---\n" ); si_to_send.si_code = SI_KERNEL; printf ("Attempting to forge signal as sent by the KERNEL...\n" ); result = syscall (SYS_rt_sigqueueinfo, my_pid, SIGUSR1, &si_to_send); if (result == -1 ) { perror ("rt_sigqueueinfo (forged as kernel)" ); printf ("Error: Forging kernel signal failed (as expected).\n" ); } else { printf ("rt_sigqueueinfo (forged as kernel) succeeded (highly unexpected!).\n" ); } sleep (1 ); printf ("\nProgram finished.\n" ); return 0 ; }
使用 sigqueue 的简单对比示例 (推荐方式):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> void signal_handler (int sig, siginfo_t *info, void *context) { if (info->si_code == SI_QUEUE) { printf ("Received signal %d with value: %d\n" , sig, info->si_value.sival_int); } else { printf ("Received signal %d (not from sigqueue)\n" , sig); } }int main () { pid_t pid; struct sigaction sa; union sigval value1 = {.sival_int = 100 }; union sigval value2 = {.sival_int = 200 }; memset (&sa, 0 , sizeof (sa)); sa.sa_sigaction = signal_handler; sigemptyset (&sa.sa_mask); sa.sa_flags = SA_SIGINFO; if (sigaction (SIGUSR1, &sa, NULL ) == -1 ) { perror ("sigaction" ); exit (EXIT_FAILURE); } pid = fork(); if (pid == -1 ) { perror ("fork" ); exit (EXIT_FAILURE); } if (pid == 0 ) { sleep (1 ); printf ("Child: Sending SIGUSR1 with value %d\n" , value1. sival_int); if (sigqueue (getppid (), SIGUSR1, value1) == -1 ) { perror ("sigqueue 1" ); } sleep (1 ); printf ("Child: Sending SIGUSR1 with value %d\n" , value2. sival_int); if (sigqueue (getppid (), SIGUSR1, value2) == -1 ) { perror ("sigqueue 2" ); } exit (EXIT_SUCCESS); } else { printf ("Parent: Waiting for signals...\n" ); sleep (5 ); wait (NULL ); printf ("Parent: Finished.\n" ); } return 0 ; }
编译和运行:
1 2 3 4 5 6 7 8 9 10 gcc -o rt_sigqueueinfo_example rt_sigqueueinfo_example.c gcc -o sigqueue_example sigqueue_example.c ./rt_sigqueueinfo_example ./sigqueue_example
总结:对于 Linux 编程新手,请优先学习和使用 sigqueue()。rt_sigqueueinfo 是一个底层工具,功能强大但使用不当有安全风险,通常由系统库内部使用。
https://www.calcguide.tech/2025/08/24/rt-sigqueueinfo系统调用及示例/
rt_sigqueueinfo系统调用及示例-CSDN博客
相关阅读