当前位置: 首页 > news >正文

Linux内核进程管理子系统有什么第二十六回 —— 进程主结构详解(22)

接前一篇文章:Linux内核进程管理子系统有什么第二十五回 —— 进程主结构详解(21)

本文内容参考:

Linux内核进程管理专题报告_linux rseq-CSDN博客

《趣谈Linux操作系统 核心原理篇:第三部分 进程管理》—— 刘超

《图解Linux内核 基于6.x》 —— 姜亚华 机械工业出版社

特此致谢!

进程管理核心结构 —— task_struct

3. 信号处理相关成员

上一回继续讲解task_struct结构中信号处理相关的成员,包括:

	/* Signal handlers: */struct signal_struct		*signal;struct sighand_struct __rcu		*sighand;sigset_t			blocked;sigset_t			real_blocked;/* Restored if set_restore_sigmask() was used: */sigset_t			saved_sigmask;struct sigpending		pending;unsigned long			sas_ss_sp;size_t				sas_ss_size;unsigned int			sas_ss_flags;

上一回沿以下路径深入跟进:

do_send_sig_info函数   --->

    send_signal_locked函数   --->

        __send_signal_locked函数   --->

            complete_signal函数

讲到了__send_signal_locked函数的最后一步 —— complete_signal函数。

(6)调用complete_signal函数查找一个处理信号的进程(线程)

上一回解析了complete_signal函数的第1步,本回继续解析后续内容。为了便于理解和回顾,再次贴出complete_signal函数源码,在同文件(kernal/signal.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;/** Now find a thread we can wake up to take the signal off the queue.** If the main thread wants the signal, it gets first crack.* Probably the least surprising to the average bear.*/if (wants_signal(sig, p))t = p;else if ((type == PIDTYPE_PID) || thread_group_empty(p))/** There is just one thread and it does not need to be woken.* It will dequeue unblocked signals before it runs again.*/return;else {/** Otherwise try to find a suitable thread.*/t = signal->curr_target;while (!wants_signal(sig, t)) {t = next_thread(t);if (t == signal->curr_target)/** No thread needs to be woken.* Any eligible threads will see* the signal in the queue soon.*/return;}signal->curr_target = t;}/** Found a killable thread.  If the signal will be fatal,* then start taking the whole group down immediately.*/if (sig_fatal(p, sig) &&(signal->core_state || !(signal->flags & SIGNAL_GROUP_EXIT)) &&!sigismember(&t->real_blocked, sig) &&(sig == SIGKILL || !p->ptrace)) {/** This signal will be fatal to the whole group.*/if (!sig_kernel_coredump(sig)) {/** Start a group exit and wake everybody up.* This way we don't have other threads* running and doing things after a slower* thread has the fatal signal pending.*/signal->flags = SIGNAL_GROUP_EXIT;signal->group_exit_code = sig;signal->group_stop_count = 0;t = p;do {task_clear_jobctl_pending(t, JOBCTL_PENDING_MASK);sigaddset(&t->pending.signal, SIGKILL);signal_wake_up(t, 1);} while_each_thread(p, t);return;}}/** The signal is already in the shared-pending queue.* Tell the chosen thread to wake up and dequeue it.*/signal_wake_up(t, sig == SIGKILL);return;
}

complete_signal函数分为以下步骤:

1)查找一个可以处理信号的进程

2)发现了一个可杀死的线程。如果信号是致命的,那么立即开始关闭整个线程组

代码片段如下:

	/** Found a killable thread.  If the signal will be fatal,* then start taking the whole group down immediately.*/if (sig_fatal(p, sig) &&(signal->core_state || !(signal->flags & SIGNAL_GROUP_EXIT)) &&!sigismember(&t->real_blocked, sig) &&(sig == SIGKILL || !p->ptrace)) {/** This signal will be fatal to the whole group.*/if (!sig_kernel_coredump(sig)) {/** Start a group exit and wake everybody up.* This way we don't have other threads* running and doing things after a slower* thread has the fatal signal pending.*/signal->flags = SIGNAL_GROUP_EXIT;signal->group_exit_code = sig;signal->group_stop_count = 0;t = p;do {task_clear_jobctl_pending(t, JOBCTL_PENDING_MASK);sigaddset(&t->pending.signal, SIGKILL);signal_wake_up(t, 1);} while_each_thread(p, t);return;}}

如果信号属于sig_fatal,并且满足&&后边的一系列条件,则在信号不属于sig_kernel_coredump的情况下,发送SIGKILL到线程组中的每一个线程,整个组退出。

哪些信号属于sig_fatal即致命信号呢?1)信号不属于sig_kernel_ignore;2)信号不属于sig_kernel_stop;3)信号的处理方式是SIG_DFL。也就是说,默认行为是terminate,且用户未改变处理方式的信号。

这里顺带提一下:kill不能发送信号到指定线程,但pthread_kill可以。它是通过tgkill系统调用实现的,和kill不同的是,它调用do_send_sig_info时传递的type参数为PIDTYPE_PID。

3)唤醒步骤1)中找到的可以处理信号的进程或线程

代码片段如下:

	/** The signal is already in the shared-pending queue.* Tell the chosen thread to wake up and dequeue it.*/signal_wake_up(t, sig == SIGKILL);

signal_wake_up函数在include/linux/sched/signal.h中,代码如下:

static inline void signal_wake_up(struct task_struct *t, bool fatal)
{unsigned int state = 0;if (fatal && !(t->jobctl & JOBCTL_PTRACE_FROZEN)) {t->jobctl &= ~(JOBCTL_STOPPED | JOBCTL_TRACED);state = TASK_WAKEKILL | __TASK_TRACED;}signal_wake_up_state(t, state);
}

对于signal_wake_up函数的解析,请看下回。

http://www.lryc.cn/news/619027.html

相关文章:

  • 基于51单片机RFID智能门禁系统红外人流量计数统计
  • 【K8s】K8s控制器——Deamonset、Statefulset、Job与CronJob
  • 下一代防火墙部署
  • 树结构无感更新及地图大批量点位上图Ui卡顿优化
  • C#对接Ollama,调用大模型禁用思考模式
  • JMeter并发测试与多进程测试
  • pcl 按比例去除点云的噪点
  • 编程模型设计空间的决策思路
  • QT第四讲-QString和QT数据类型之间转换
  • 当多模态大语言模型遇上视觉难题!AI视觉探索之旅
  • NLP基础
  • CASS11计算斜面面积
  • sqli-libs通关教程(41-50)
  • 【leetcode】45. 跳跃游戏2
  • cuda排序算法--双调排序(Bitonic_Sort)
  • __base__属性
  • 【动态规划】leecode 198的打家劫舍2:dp集合有两种写法对比
  • 关系型数据库中,如果某一列的选项只有几种(比如性别、状态等低基数枚举值),添加索引的效果如何?
  • day26-IO(2)
  • 学习笔记《区块链技术与应用》ETH 第二天 状态树
  • 数据分析—双十一
  • B.10.02.3-分布式一致性:电商业务场景下的理论与工程实践
  • IDEA插件开发实践
  • 从阶段演进到智能跃迁:企业合同管理的发展与趋势
  • SynAdapt:通过合成连续思维链实现大语言模型的自适应推理
  • @Rancher简介部署使用 - Docker Compose
  • Spring MVC 处理请求的完整流程详解
  • 【Unity】Spine重新播放动画时会闪烁上次动画的残影
  • 秋招笔记-8.12
  • Tauri Qt孰优孰劣