我们来深入学习 rt_tgsigqueueinfo 系统调用,在 Linux 系统中,信号(Signal)是进程间通信的一种方式。kill() 系统调用可以向一个进程发送信号,而 sigqueue() 则可以发送信号并附带一些数据(union sigval)。rt_tgsigqueueinfo 是一个更底层、更具体、但也更复杂的系统调用。
1. 函数介绍
在 Linux 系统中,信号(Signal)是进程间通信的一种方式。kill() 系统调用可以向一个进程发送信号,而 sigqueue() 则可以发送信号并附带一些数据(union sigval)。
data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">rt_tgsigqueueinfo 是一个更底层、更具体、但也更复杂的系统调用。它的名字可以拆解为:
rt_: 表示这是一个与实时信号相关的系统调用。
tg: 表示 Thread Group(线程组)。在 Linux 中,一个进程(Process)可以包含多个线程(Threads),这些线程共享同一个 PID,但每个线程有自己的唯一 Thread ID (TID)。所有共享同一个 PID 的线程构成了一个线程组。
sigqueueinfo: 表示它像 sigqueue 一样发送信号和数据,但允许发送者提供更详细的信号信息(通过 siginfo_t 结构体)。
所以,rt_tgsigqueueinfo 的作用是:向指定进程(线程组)中的指定线程(由 TID 指定)发送一个信号,并允许发送者完全指定该信号的 siginfo_t 信息。
与 sigqueue 的区别:
sigqueue(pid_t pid, …): 向进程 pid 发送信号。内核会选择该进程(线程组)中的某个线程来接收信号。
rt_tgsigqueueinfo(pid_t tgid, pid_t tid, …): tgid 是线程组 ID(通常就是进程的 PID),tid 是线程组内具体线程的 ID(TID)。它允许你指定哪个线程应该接收这个信号。
为什么复杂/危险?与 rt_sigqueueinfo 类似,它允许发送者完全伪造 siginfo_t 中的信息(如发送者 PID、si_code 等),这可能带来安全风险。因此,它的使用受到严格的权限检查。
对于 Linux 编程小白:你通常不需要直接使用 rt_tgsigqueueinfo。在需要向特定线程发送信号时,更常见的做法是在线程内部使用 pthread_kill()(POSIX 线程库函数)。rt_tgsigqueueinfo 主要供系统级程序或 C 库实现使用。
2. 函数原型
1 | // 这是底层系统调用,用户空间标准 C 库通常不直接提供包装函数 |
用户空间更常用的相关函数:
1 | #include <signal.h> |
3. 功能
向指定线程组 ID (tgid) 和线程 ID (tid) 的线程发送指定信号 (sig),并允许发送者完全指定 siginfo_t 结构体 (uinfo) 中包含的详细信息。
4. 参数
tgid:
pid_t 类型。
目标线程所属的线程组 ID (Thread Group ID)。对于单线程进程,这通常就是它的 PID。对于多线程程序,所有线程共享同一个 tgid(即主线程的 PID)。
tid:
pid_t 类型。
目标线程在内核中的唯一 ID (Thread ID)。在用户空间的 pthread_create 返回的 pthread_t 和内核的 tid 之间需要转换(可以使用 /proc/self/task/ 目录或特定的系统调用来获取)。
sig:
int 类型。
要发送的信号编号,例如 SIGUSR1, SIGRTMIN 等。注意,不能发送 SIGKILL 和 SIGSTOP。
uinfo:
siginfo_t * 类型。
一个指向 siginfo_t 结构体的指针。这个结构体包含了你想随信号一起传递给目标线程的所有详细信息。调用者需要自己填充这个结构体的各个字段。
5. 返回值
- 成功: 返回 0。
失败: 返回 -1,并设置全局变量 errno 来指示具体的错误原因:
EAGAIN: (对于实时信号) 已达到接收者排队信号的最大数量限制 (RLIMIT_SIGPENDING)。
EPERM: 调用者没有权限发送信号给目标线程(例如,权限不足,或试图伪造某些 si_code)。
EINVAL: sig 是无效的信号号,tgid 或 tid 无效,或者 uinfo 中的 si_code 是无效的或不允许由用户设置的。
ESRCH: 找不到 tgid 指定的线程组或 tid 指定的线程。
6. 相似函数或关联函数
sigqueue: 向进程(线程组)发送信号和数据,由内核选择线程接收。
pthread_kill: POSIX 标准函数,用于向指定的 pthread_t 线程发送信号,更易于使用。
tgkill: 一个更安全的系统调用,用于向指定线程组和线程发送信号(但不带 siginfo_t 数据),通常由 pthread_kill 内部调用。
kill: 向进程发送信号。
siginfo_t: 包含信号详细信息的结构体。
getpid: 获取当前进程的 PID (也是线程组 ID tgid)。
gettid: 获取当前线程的内核 TID。注意:这不是标准 C 库函数,需要通过 syscall(SYS_gettid) 调用。
pthread_self: 获取当前线程的 POSIX 线程 ID (pthread_t)。
7. 示例代码
由于直接使用 rt_tgsigqueueinfo 需要获取内核线程 ID (tid) 并且容易因权限或错误的 siginfo_t 而失败,下面的示例将演示如何获取 tid 并尝试调用 rt_tgsigqueueinfo,同时提供一个使用推荐的 pthread_kill 的对比示例。
警告:直接使用 rt_tgsigqueueinfo 可能会因为权限问题或 siginfo_t 构造不当而失败。
1 | #define _GNU_SOURCE // 启用 GNU 扩展,获取 gettid |
使用 pthread_kill 的推荐示例:
1 | #define _GNU_SOURCE |
编译和运行:
1 | # 假设代码保存在 rt_tgsigqueueinfo_example.c 和 pthread_kill_example.c 中 |
总结:对于 Linux 编程新手,处理多线程信号时,请优先学习和使用 pthread_kill()。rt_tgsigqueueinfo 是一个底层、强大的工具,但使用不当会有安全风险且复杂,通常由系统库内部或高级系统编程使用。
https://www.calcguide.tech/2025/08/24/rt-tgsigqueueinfo系统调用及示例/
rt_tgsigqueueinfo系统调用及示例-CSDN博客