news 2026/7/21 10:53:49

Linux task_struct信号处理与sigpending组织

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux task_struct信号处理与sigpending组织

Linux task_struct信号处理与sigpending组织

struct task_struct中信号相关字段分布在多个cache line上,直接决定信号投递路径的缓存行为。task_struct->pending是进程级挂起信号集合,类型为struct sigpending,内部包含一个sigset_t signal(64位系统上为两个unsigned long)和一个struct list_head list链式队列。thread group共享的signal_struct->shared_pending则存放需要广播到整个线程组的挂起信号。

```c
struct sigpending {
struct list_head list;
sigset_t signal;
};

struct task_struct {
/* ... */
struct sigpending pending; /* 进程私有挂起信号 */
/* ... */
struct signal_struct *signal; /* 指向共享的thread group信号描述符 */
/* ... */
unsigned int siginfo_try_wait:1;
unsigned int siginfo_try_wait_seen:1;
/* ... */
};
```

信号投递入口为__send_signal_locked()。该函数首先判断目标tgid是否等于当前pid——即进程自身发信号——若是则直接写入current->pending;否则调用__sigqueue_alloc()分配一个struct sigqueue节点插入目标task->pending.list,同时设置pending.signal中对应的bit。这里存在一个关键竞态:如果目标进程正在执行do_exit()释放signal_struct,__sigqueue_alloc()可能看到的是已置SIGNAL_GROUP_EXIT标志的signal_struct,此时必须放弃投递。

```c
static int __send_signal_locked(int sig, struct kernel_siginfo *info,
struct task_struct *t, bool group, bool force)
{
struct sigpending *pending;
struct sigqueue *q;
int override_rlimit;
int ret = 0;

pending = group ? &t->signal->shared_pending : &t->pending;

if (unlikely(!t->signal)) /* 目标进程正在exit */
return -ESRCH;

/*
* 快速路径:实时信号或非实时信号忽略rlimit检查
* 对于sigqueue溢出场景,重试一次后丢信号
*/
override_rlimit = (sig >= SIGRTMIN) || sig == SIGKILL || sig == SIGSYS;

if (!override_rlimit && !sigismember(&t->signal->rlim[RLIMIT_SIGPENDING].rlim_cur)) {
if (t->signal->nr_queued >= t->signal->rlim[RLIMIT_SIGPENDING].rlim_cur) {
ret = -EAGAIN;
goto out;
}
}

q = __sigqueue_alloc(sig, t, GFP_ATOMIC, override_rlimit, 0);
if (q) {
list_add_tail(&q->list, &pending->list);
sigaddset(&pending->signal, sig);
} else if (!is_si_special(info)) {
/* 分配失败且不是特殊info,丢弃信号 */
ret = -ENOMEM;
goto out;
}

/* 非实时信号相同sig只保留一个sigqueue,丢弃后续重复 */
if (sig < SIGRTMIN) {
struct sigqueue *first = list_first_entry_or_null(&pending->list, typeof(*first), list);
while (first && first->info.si_signo == sig && first != q) {
list_del(&first->list);
__sigqueue_free(first);
first = list_first_entry_or_null(&pending->list, typeof(*first), list);
}
}

complete_signal(sig, t, group);
out:
return ret;
}
```

信号递送路径complete_signal()的关键决策是选择投递目标线程。对于SIGKILL或SIGSTOP这类终止类信号,直接标记整个thread group后唤醒所有TASK_INTERRUPTIBLE状态的成员;对于其他信号,优先选择当前正在运行的线程(通过sigismember(&t->signal->curr_target->pending.signal)),若不可用则遍历thread group找第一个不阻塞该信号的线程。选中的线程通过signal_wake_up()置TIF_SIGPENDING标志,然后视当前cpu是否为目标cpu决定是否需要reschedule IPI。

```c
static void complete_signal(int sig, struct task_struct *p, enum pid_type type)
{
struct signal_struct *signal = p->signal;
struct task_struct *t;

if (unlikely(sig == SIGKILL || sig == SIGSTOP)) {
/* 终止类信号直接广播 */
__for_each_thread(signal, t)
signal_wake_up(t, sig == SIGKILL);
return;
}

/* 找不阻塞该信号的目标线程 */
t = signal->curr_target;
while (!sigismember(&t->blocked, sig)) {
t = next_thread(t);
if (t == signal->curr_target)
break; /* 所有线程都阻塞 */
}

if (t->flags & PF_EXITING)
t = p; /* 目标在exit,退回发信号者 */

signal_wake_up(t, 0);
}
```

另一个关键路径是从user mode返回内核时调用do_signal()。entry_SYSCALL_64后检查thread_info->flags的_TIF_SIGPENDING位,若置位则跳转至do_notify_resume()再进入do_signal()。这里使用get_signal()从pending链表中逐一取出struct ksignal,判断sa_handler是否为SIG_IGN或SIG_DFL,对于SIG_IGN直接丢弃信号不计入signal_struct->nr_queues;对于SIG_DFL则执行默认动作如do_group_exit()。标准信号(1-31)在pending.signal中只保留一个bit,但list上仍可能有多个sigqueue节点——list遍历时必须通过info.si_signo匹配真实的信号编号。

```c
void get_signal(struct ksignal *ksig)
{
struct task_struct *tsk = current;
struct signal_struct *signal = tsk->signal;
int sig;

for (;;) {
sig = dequeue_signal(tsk, &tsk->blocked, &ksig->info);
if (!sig)
break; /* 所有信号处理完毕 */

if (sigismember(&tsk->real_blocked, sig))
continue; /* 被real_blocked二次屏蔽 */

ksig->sig = sig;

/* ka缓存了handler和flags */
if (ksig->ka->sa.sa_handler == SIG_IGN) {
continue; /* 直接吞掉 */
}
if (ksig->ka->sa.sa_handler == SIG_DFL) {
switch (sig) {
case SIGCONT: case SIGCHLD: case SIGWINCH: case SIGURG:
continue; /* 默认忽略 */
case SIGTSTP: case SIGTTIN: case SIGTTOU:
do_signal_stop(sig);
goto relock;
case SIGSTOP:
do_signal_stop(sig);
goto relock;
case SIGKILL:
do_group_exit(SIGKILL);
default:
break;
}
}
return; /* 返回用户信号处理函数 */
}

/* 所有信号已耗尽,清理TIF_SIGPENDING */
clear_tsk_thread_flag(tsk, TIF_SIGPENDING);
}
```

dequeue_signal()的实现需要同时扫描task->pending和signal->shared_pending两个队列,并且必须保持一致性——先取shared_pending再取私有pending,中间不能存在抢锁窗口。内部的collect_signal()负责从list头摘取匹配sig的sigqueue节点,如果list为空则清除对应的pending.signal bit。实时信号(>=SIGRTMIN)保证sigqueue的数量不压缩,32个实时信号每个都可以独立排队;非实时信号在投递时就做了重复合并,所以dequeue时一个信号至多对应一个sigqueue。

结构布局上的一个陷阱:struct task_struct的pending字段和blocked字段分散在不同的cache line上,signal_wake_up()写task->pending时不会invalid目标cpu上blocked字段的cache line。但get_signal()读blocked时若与complete_signal()并发,由于二者均持有siglock,写者通过siglock的release语义保证了读者能看到完整的pending.signal集合。然而在siglock未持有的检查路径如signal_pending()中,只读取TIF_SIGPENDING位而不访问sigpending本体,这是安全的——因为TIF_SIGPENDING的set/clear永远在siglock保护下进行。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/21 10:48:07

职场摸鱼党必备!财运到股票期货盯盘助手V3深度测评

职场摸鱼党必备&#xff01;财运到股票期货盯盘助手V5深度测评 对于职场中的投资爱好者而言&#xff0c;一边专注工作一边紧盯股市期货行情&#xff0c;往往是件两难的事。既要避免被同事或领导发现&#xff0c;又不想错过关键行情节点&#xff0c;一款隐形且功能强大的盯盘工具…

作者头像 李华
网站建设 2026/7/21 10:47:16

Unreal Engine核心架构:Actor与Component的设计哲学与实战应用

1. 项目概述&#xff1a;从“积木”到“机器人”的认知跃迁 刚接触Unreal Engine&#xff08;虚幻引擎&#xff09;时&#xff0c;很多朋友会被“游戏对象”和“组件”这两个概念绕晕。这太正常了&#xff0c;因为教科书式的定义往往过于抽象。今天&#xff0c;我想从一个更贴近…

作者头像 李华
网站建设 2026/7/21 10:47:03

m4s-converter:5秒解锁B站缓存视频,打造你的个人数字图书馆

m4s-converter&#xff1a;5秒解锁B站缓存视频&#xff0c;打造你的个人数字图书馆 【免费下载链接】m4s-converter 一个跨平台小工具&#xff0c;将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 你是否曾经为…

作者头像 李华
网站建设 2026/7/21 10:46:34

OpenSSL 4.0核心技术解析:ECH与后量子密码实践

1. OpenSSL 4.0技术革新全景解读 OpenSSL项目在2026年4月正式发布了具有里程碑意义的4.0版本&#xff0c;这个被全球开发者期待已久的更新带来了三大核心升级&#xff1a;增强型隐私保护机制、后量子密码学支持以及遗留技术清理。作为互联网安全通信的基础设施&#xff0c;Open…

作者头像 李华
网站建设 2026/7/21 10:46:25

深入解析TMS320F2802x PIE中断机制:从架构到实战配置

1. 项目概述 在嵌入式系统&#xff0c;尤其是像TI C2000系列这样面向实时控制应用的DSP中&#xff0c;中断机制的设计直接决定了系统的响应速度和可靠性。很多刚接触TMS320F2802x这类芯片的朋友&#xff0c;面对手册里密密麻麻的寄存器表格和中断向量表&#xff0c;常常感到无从…

作者头像 李华
网站建设 2026/7/21 10:45:20

Linux内核进程管理子系统有什么第九十三回 —— 进程调度(20)

接前一篇文章:Linux内核进程管理子系统有什么第九十二回 —— 进程调度(19) 本文内容参考: Linux内核进程管理专题报告_linux rseq-CSDN博客 《趣谈Linux操作系统 核心原理篇:第三部分 进程管理》—— 刘超 《图解Linux内核 基于6.x》 —— 姜亚华 机械工业出版社 (68…

作者头像 李华