linux内核进程管理(1)——创建,退出
linux源码阅读——进程管理(1)
- 1. 进程的基本介绍
- 1.1 linux中进程和线程的区别
- 1.2 task_struct中的基本内容
- 1.3 命名空间ns(namespace)
- 命名空间结构图
- Linux 中的命名空间类型
- 1.4 进程标识符
- 2. 创建一个进程的流程
- 2.1 CLONE宏
- 2.2 创建进程系统调用
- 1. do_fork流程——v6.9-kernel_clone
- 2. do_fork中copy_process流程
- (1). 标志冲突判断
- (2). dup_task_struct——分配task空间
- (3). 检查用户的进程数量限制
- (4). copy_creds复制或共享证书
- (5). 检查线程数量限制
- (6). sched_fork——设置调度器相关参数
- (7). copy_xxx——根据CLONE_FLAG复制或共享资源
- 3. do_fork中的wake_up_new_task流程
- 3. 进程的退出
- 3.1 退出概念简介
- 3.2 exit_group线程组退出
- 1. exit_group简介
- 2. exit_group具体流程
- 3.3 kill
注 : 图片来自《Linux内核深度解析 基于ARM64架构的Linux 4.x内核》(余华兵)
1. 进程的基本介绍
1.1 linux中进程和线程的区别
在课本教学中我们习惯将进程和线程看作两种差别很大的东西,但是在实际的linux系统中,无论是进程还是线程都由所谓PCB(process control block) 也就是我们的 task_struct表示。
进程的虚拟地址空间分为用户虚拟地址空间和内核虚拟地址空间,所有进程共享内核虚拟地址空间,每个进程有独立的用户虚拟地址空间。
- 先说结论
- 进程——独立拥有用户虚拟地址空间
- 用户线程——共享用户虚拟地址空间
- 内核线程——没有用户虚拟地址空间
那么大家就要问了,什么是用户虚拟地址空间(mm_struct),这个在讲调度的时候介绍
1.2 task_struct中的基本内容
成员 | 说明 |
---|---|
volatile long state; | 进程的状态,使用volatile关键字,确保读取都能得到最新的值 |
void *stack; | 指向内核栈, 进程在内核态下的运行“装备”,确保进程在进入内核模式时能够正确恢复上下文并安全执行内核代码 |
pid_t pid; | 全局的进程号,同一PID命名空间下,pid唯一 |
pid_t tgid; | 全局的线程组标识符,在一个多线程进程中,多个线程会有不同的 pid,但它们的 tgid 会相同 |
struct pid_link pids[PIDTYPE_MAX]; | 进程号,进程组标识符和会话标识符 |
struct task_struct __rcu *real_parent; | real_parent指向真实的父进程, rcu(read-copy-update)并发编程机制 |
struct task_struct __rcu *parent; | parent指向父进程:如果进程被另一个进程(通常是调试器)使用系统调用ptrace跟踪,那么父进程是跟踪进程,否则和real_parent相同 |
struct task_struct *group_leader; | 指向线程组的组长 |
const struct cred __rcu *real_cred; | real_cred指向主体和真实客体证书, |
const struct cred __rcu *cred; | cred指向有效客体证书。通常情况下,cred和real_cred指向相同的证书,但是cred可以被临时改变 |
char comm[TASK_COMM_LEN]; | 进程名称 |
int prio, static_prio, normal_prio; unsigned int rt_priority; unsigned int policy; | 调度策略和优先级,dl,rt,cfs |
cpumask_t cpus_allowed | 允许进程在哪些处理器上运行,处理器亲和性 |
struct mm_struct *mm,*active_mm; | 指向内存描述符进程:mm和active_mm指向同一个内存描述符内核线程:mm是空指针,当内核线程运行时,active_mm指向从进程借用的内存描述符 |
struct fs_struct *fs; | 文件系统信息,主要是进程的根目录和当前工作目录 |
struct files_struct *files; | 打开文件表 |
struct nsproxy *nsproxy; | 命名空间代理 |
struct signal_struct *signal; struct sighand_struct *sighand; sigset_t blocked, real_blocked; sigset_t saved_sigmask; struct sigpending pending; | 信号处理, signal_struct 用于存储和管理进程的信号状态。 sighand_struct 管理进程的信号处理程序。 blocked 和 real_blocked 控制进程哪些信号被阻塞,避免信号干扰进程执行。 saved_sigmask 保存和恢复进程的信号掩码状态。 sigpending 存储待处理的信号。 |
1.3 命名空间ns(namespace)
和虚拟机相比,容器是一种轻量级的虚拟化技术,直接使用宿主机的内核,使用命名空间隔离资源。
命名空间(Namespace)是 Linux 内核中的一种重要特性,它用于隔离不同进程的资源,使得多个进程能够在同一台机器上“仿佛”运行在独立的系统中。命名空间技术是实现容器(如 Docker)的核心技术之一。命名空间通过提供资源的隔离,使得不同进程或容器能够有自己独立的资源视图,如进程号(PID)、网络、挂载等。
命名空间结构图
Linux 中的命名空间类型
Linux 支持多种类型的命名空间,每种命名空间用于隔离系统的某种资源。以下是 Linux 中常见的几种命名空间类型:
-
进程号命名空间(PID Namespace)
- 进程号命名空间使得每个进程可以拥有自己的进程号(PID)。在不同的进程号命名空间中,同一个 PID 号可以表示不同的进程。
- 父进程的 PID 在子命名空间内不再是唯一的,而是局限于该命名空间中。比如,PID 1 代表的是该命名空间中的第一个进程,而不是宿主机上的第一个进程。
- 这种隔离有助于容器化应用的管理,因为它们在容器内可以拥有从 1 开始的 PID,而与宿主机的进程号不冲突。
-
挂载命名空间(Mount Namespace)
- 挂载命名空间允许不同的进程在不同的命名空间中看到不同的文件系统视图。
- 它能够实现容器内进程看到的文件系统与宿主机或其他容器内进程看到的文件系统完全不同。例如,在容器中,你可以挂载不同的目录,而这些挂载操作不会影响宿主机的文件系统。
- 挂载命名空间是实现文件系统隔离的基础。
-
网络命名空间(Network Namespace)
- 网络命名空间提供了进程之间网络资源(如 IP 地址、路由表、网络设备等)的隔离。
- 每个网络命名空间有自己的网络接口、路由表、防火墙等配置。这样,容器或进程在不同的网络命名空间中,彼此之间不能直接通信,除非通过某种方式显式地配置网络连接。
- 网络命名空间使得容器能够拥有自己的 IP 地址,甚至在不同的容器之间实现隔离的虚拟网络。
-
IPC 命名空间(IPC Namespace)
- IPC 命名空间用于隔离进程间通信(IPC)机制,如信号量、消息队列和共享内存。
- 在不同的 IPC 命名空间中,进程看到的共享内存段、消息队列等资源是隔离的。即使两个进程有相同的 PID,它们也不能相互访问对方的 IPC 资源。
-
UTS 命名空间(UTS Namespace)
- UTS 命名空间用于隔离主机名和域名系统(DNS)信息。
- 在不同的 UTS 命名空间中,进程可以拥有独立的主机名和域名,进而可以在容器中使用不同的主机名而不影响宿主机。
-
用户命名空间(User Namespace)
- 用户命名空间用于隔离进程的用户和组 ID(UID/GID)。
- 在一个用户命名空间内,进程可以有一个与宿主机完全不同的 UID 和 GID 映射。例如,容器中的进程可以运行在 UID 0(即 root 用户),而在宿主机上可能对应的是一个普通用户。这使得容器中的进程能够具有 root 权限,但在宿主机上却是以普通用户身份运行,从而提高安全性。
-
时间命名空间(Time Namespace)(Linux 5.6 及以后版本)
- 时间命名空间允许进程有自己独立的时间视图。每个时间命名空间可以有独立的系统时间(即时间和时区设置)。这使得容器能够有自己的时钟和时间管理系统,而不依赖于宿主机的时间。
-
cgroup 命名空间(Cgroup Namespace)(Linux 4.5 及以后版本)
- cgroup 命名空间用于隔离进程的控制组(cgroup)信息。cgroup 是一种内核机制,用于对进程进行资源限制、优先级调度等管理。cgroup 命名空间允许每个进程组(如容器)有自己独立的 cgroup 层次结构。
命名空间(Namespace)是 Linux 内核提供的一种资源隔离机制,它通过为进程提供独立的资源视图,确保不同进程或容器之间的隔离。命名空间的类型包括 PID、网络、挂载、IPC、UTS、用户等,这些命名空间在容器化技术、进程管理、安全性等方面起着关键作用。通过使用命名空间,Linux 能够在同一台机器上运行多个互相隔离的进程或容器,从而实现更高效和安全的资源管理。
1.4 进程标识符
- 进程标识符 pid
- 线程组标识符 tgid
- 进程组标识符 pgid
- 会话标识符 sid
sid是多个兄弟在一起(shell),pgid是爸爸带一堆儿子(fork)
会话和进程组被设计用来支持 shell 作业控制,shell 为执行单一命令或者管道的进程创建一个进程组
2. 创建一个进程的流程
2.1 CLONE宏
想要了解进程的创建流程首先需要了解,clone都具备哪些标识
标志 | 类别 | 作用 |
---|---|---|
CSIGNAL | 信号相关标志 | 子进程退出时发送给父进程的信号掩码。 |
CLONE_VM | 资源共享标志 | 父子进程共享虚拟内存空间,通常用于线程。 |
CLONE_FS | 资源共享标志 | 父子进程共享文件系统信息(如当前工作目录、根目录等)。 |
CLONE_FILES | 资源共享标志 | 父子进程共享打开的文件描述符。 |
CLONE_SIGHAND | 资源共享标志 | 父子进程共享信号处理程序和阻塞信号。 |
CLONE_PIDFD | 进程级别标志 | 在父进程中为子进程创建一个 pidfd (PID文件描述符)。 |
CLONE_PTRACE | 进程级别标志 | 允许追踪继续,父进程可以继续对其子进程进行追踪。 |
CLONE_VFORK | 进程级别标志 | 父进程希望子进程在释放其内存时唤醒父进程。 |
CLONE_PARENT | 进程级别标志 | 子进程和父进程保持相同的父进程。 |
CLONE_THREAD | 线程/进程级别标志 | 子进程和父进程属于同一个线程组,通常用于线程。 |
CLONE_NEWNS | 命名空间相关标志 | 创建一个新的挂载命名空间。 |
CLONE_SYSVSEM | 进程级别标志 | 父子进程共享 System V 信号量(SEM_UNDO)语义。 |
CLONE_SETTLS | 线程级别标志 | 为子进程创建新的 TLS(线程本地存储)。 |
CLONE_PARENT_SETTID | 线程级别标志 | 将线程标识符 (TID) 设置到父进程的 parent_tid 指向的位置。 |
CLONE_CHILD_CLEARTID | 线程级别标志 | 线程退出时清除其 TID(线程标识符)。 |
CLONE_DETACHED | 进程级别标志 | 已废弃,忽略。 |
CLONE_UNTRACED | 进程级别标志 | 如果设置了该标志,父进程不能强制对子进程进行追踪。 |
CLONE_CHILD_SETTID | 线程级别标志 | 子进程首次调度时将 TID 设置到 child_tid 指向的位置。 |
CLONE_NEWCGROUP | 命名空间相关标志 | 创建一个新的 cgroup(控制组)命名空间。 |
CLONE_NEWUTS | 命名空间相关标志 | 创建一个新的 UTS(UNIX时间共享)命名空间,通常用于主机名和域名的隔离。 |
CLONE_NEWIPC | 命名空间相关标志 | 创建一个新的 IPC(进程间通信)命名空间。 |
CLONE_NEWUSER | 命名空间相关标志 | 创建一个新的用户命名空间,通常用于进程的用户和组 ID 隔离。 |
CLONE_NEWPID | 命名空间相关标志 | 创建一个新的 PID(进程标识符)命名空间。 |
CLONE_NEWNET | 命名空间相关标志 | 创建一个新的网络命名空间。 |
CLONE_IO | 进程级别标志 | 子进程与父进程共享 I/O 上下文(I/O 调度等)。 |
2.2 创建进程系统调用
(1)fork(分叉):子进程是父进程的一个副本,采用了写时复制的技术。
(2)vfork:用于创建子进程,之后子进程立即调用 execve 以装载新程序的情况。为了避免复制物理页,父进程会睡眠等待子进程装载新程序。现在 fork 采用了写时复制的技术,vfork 失去了速度优势,已经被废弃。
(3)clone(克隆):可以精确地控制子进程和父进程共享哪些资源。这个系统调用的
主要用处是可供 pthread 库用来创建线程。
clone 是功能最齐全的函数,参数多,使用复杂,fork 是 clone 的简化函数。
那么在执行这三个系统调用,实际执行的是do_fork函数,注意在当前(2025/4)最新linux版本6.9中do_fork被更换为kernel_clone(), 具体执行流程和do_fork大体相同,主要变更在kernel_clone 扩展了 do_fork 的功能,并且增加了更多针对不同类型进程创建的支持,特别是在 clone() 调用中引入了更多控制参数和特性。
1. do_fork流程——v6.9-kernel_clone
在v6.9版本的内核中,do_fork被替换取而代之的是kernel_clone,两者核心流程类似
- 调用函数 copy_process 以创建新进程
- 关于第二个步骤中判断CLONE_PARENT_SETTID的操作——CLONE_PARENT_SETTID:这是 clone() 调用中的一个标志,表示 父进程想要在子进程创建时将子进程的 PID 设置到某个指定位置。这个标志位指示内核将子进程的 PID 写入父进程传入的 parent_tid 指针。
具体作用:在一些特定的多线程应用或线程库中,父进程或创建线程的控制者希望在用户空间中直接获得新创建线程(或进程)的 PID,便于后续的管理,比如设置线程的调度策略、处理进程间通信等。CLONE_PARENT_SETTID 可以帮助父进程或控制者直接获取子进程的 PID,避免了额外的查询操作。
- 调用函数 wake_up_new_task 以唤醒新进程。
- 如果是系统调用 vfork,那么当前进程等待子进程装载程序。
2. do_fork中copy_process流程
创建fork新进程的主要工作由函数 copy_process 实现
官方注释——只执行复制但是不启动,state设置为TASK_NEW
This creates a new process as a copy of the old one, but does not actually start it yet.
It copies the registers, and all the appropriate parts of the process environment (as per the clone flags). The actual kick-off is left to the caller.
接下来我们详细解读一下copy_process中每个流程
(1). 标志冲突判断
-
CLONE_NEWNS & CLONE_FS:
- CLONE_NEWNS 会创建一个新的挂载命名空间,意味着新的进程有一个独立的根目录(根文件系统)。而 CLONE_FS 是要求多个进程共享文件系统信息,包括根目录。如果同时设置这两个标志,会导致根目录和文件系统信息冲突,因此是无效的。
-
CLONE_NEWUSER & CLONE_FS:
- CLONE_NEWUSER 创建新的用户命名空间,使得新的进程具有独立的用户身份。而 CLONE_FS 会共享文件系统信息,这会导致新进程在文件系统方面不独立,违反了用户命名空间的隔离原则,因此这两个标志不能同时使用。
-
CLONE_THREAD & !CLONE_SIGHAND:
- 线程组的进程必须共享信号处理程序。因此,当设置 CLONE_THREAD 时,必须设置 CLONE_SIGHAND 来共享信号处理程序。如果没有设置 CLONE_SIGHAND,则无法确保线程组的一致性,导致冲突。
-
CLONE_SIGHAND & !CLONE_VM:
- 当进程共享信号处理程序(CLONE_SIGHAND)时,必须共享虚拟内存空间(CLONE_VM)。如果不共享虚拟内存,信号处理程序会因进程间内存空间不同而无法正常工作,因此此组合会产生冲突。
-
CLONE_PARENT & SIGNAL_UNKILLABLE:
- CLONE_PARENT 要求子进程的父进程为当前进程。全局 init 进程(init)是不可杀死的且没有父进程,因此不允许全局 init 进程创建其他兄弟进程。否则,会违反进程树结构,产生冲突。
-
CLONE_THREAD & (CLONE_NEWUSER | CLONE_NEWPID):
- 线程不允许跨越用户或 PID 命名空间。如果创建一个线程时,设置了 CLONE_NEWUSER 或 CLONE_NEWPID,则该线程将进入一个不同的命名空间,这会破坏线程组的共享,因此是无效的。
-
CLONE_PIDFD & CLONE_DETACHED:
- 如果设置了 CLONE_PIDFD,表示父进程会持有子进程的 PID 文件描述符,允许父进程跟踪子进程。而 CLONE_DETACHED 表示子进程是分离的,不需要父进程等待,因此不能同时设置这两个标志。分离进程不应持有 PID 文件描述符。
(2). dup_task_struct——分配task空间
函数 dup_task_struct:函数 dup_task_struct 为新进程的进程描述符分配内存,把当前进程的进程描述符复制一份,为新进程分配内核栈。
内核栈——task_struct中的stack指向内核栈
-
分配内核栈和 task_struct 所需空间
- 每个进程在内核中有一块私有栈(内核栈),和 task_struct 一起分配。
-
复制当前进程的 task_struct 数据到新结构体中
- 这是“浅拷贝”,后续会由 copy_xxx() 函数根据CLONE_FLAG做深拷贝处理(比如 copy_mm()、copy_files() 等)。
-
初始化调试字段、引用计数等
- 比如清除 task_struct->stack_canary,初始化调试状态。
(3). 检查用户的进程数量限制
对于普通用户:如果创建的进程数量超限,则失败
对于根用户:因为根用户默认拥有忽略资源限制的权限(CAP_SYS_RESOURCE)和系统管理权限(CAP_SYS_ADMIN),可以创建
(4). copy_creds复制或共享证书
什么是 cred 证书?
回答 :
cred
(全称 struct cred
)是 Linux 内核中用于描述进程安全相关信息的一种核心数据结构。它就是**“进程的身份证 + 安全通行证”,内核通过它判断当前进程能不能干某件事**。
🔐 cred
结构体包含什么?
这个结构体定义在 include/linux/cred.h
中,里面包含了以下这些字段(简化版):
字段 | 含义 |
---|---|
uid , gid | 实际用户/组 ID |
euid , egid | 有效用户/组 ID |
suid , sgid | 保存的用户/组 ID(用于切换身份) |
fsuid , fsgid | 文件系统相关的用户/组 ID(用于访问控制) |
cap_inheritable | 可继承的能力(capabilities) |
cap_permitted | 被允许的能力 |
cap_effective | 当前生效的能力(实际起作用的) |
cap_bset | bounding set,允许继承的最大能力集合 |
user | 指向 struct user_struct ,跟踪用户的资源使用(比如进程数) |
security | 安全模块使用(如 SELinux、AppArmor) |
🧠 作用是什么?
内核中,几乎所有需要安全判断的操作,比如:
- 访问文件
- 打开 socket
- 调用某些系统调用(比如
mount
、kill
、ptrace
) - 进入 namespace
- 修改资源限制
- 获取 debug 权限(如
/proc/*
)
都会通过 current->cred
来做决策。
比如:
if (capable(CAP_SYS_ADMIN)) {// 允许系统管理操作
}
这里的 capable()
内部其实就是读取当前线程的 cred->cap_effective
,看看有没有这个 capability。
🐾 谁用它?
除了内核自身判断权限外,像:
ptrace()
setuid()
,setgid()
capset()
- LSM(如 SELinux)
- 容器安全模型
都会操作或依赖 cred
。
一句话总结
cred是进程的安全身份信息,它决定了一个进程能干什么、能访问什么、有没有权限。
对于kernel_clone(do_fork)copy_cred()
如果设置了标志 CLONE_THREAD
,即新进程和当前进程属于同一个线程组,那么新进程和当前进程共享证书。
否则,复制cred。
(5). 检查线程数量限制
全局变量
nr_threads
存放当前的线程数量;max_threads
存放允许创建的线程最大数量,默认值是 MAX_THREADS。
如果线程数量达到允许的线程最大数量,那么不允许创建新进程。
(6). sched_fork——设置调度器相关参数
- 将task->state设为TASK_NEW,确保新进程不会被运行,也不会被信号唤醒,不会被加入就绪队列
rq(runqueue)
- 把新进程的调度优先级设置为当前进程的正常优先级
- 因为当前进程可能因为占有实时互斥锁而被临时提升了优先级
- unlikely检查是否设置了
SCHED_RESET_ON_FORK
标志,要求创建新进程时把新进程的调度策略和优先级设置为默认值 - 拒绝
dl(dealline)
任务fork - SMP 多核支持初始化
(7). copy_xxx——根据CLONE_FLAG复制或共享资源
Linux 内核在创建新进程时执行的一系列子系统状态复制流程,它的作用是将父进程的资源、状态等复制或共享给子进程,以确保新进程具有完整的运行环境。
✅ 步骤及作用一览表
在5-10
步骤中,只有相同线程组的线程间才会进行共享,人话:一个老爹生的
步骤 | 函数调用 | 作用 | 失败时回滚标签 |
---|---|---|---|
1 | perf_event_init_task(p, clone_flags) | 初始化 perf 事件监控(性能分析支持) | bad_fork_sched_cancel_fork |
2 | audit_alloc(p) | 初始化审计上下文,用于安全审计(audit 子系统) | bad_fork_cleanup_perf |
3 | shm_init_task(p) | 初始化与 System V 共享内存相关的结构 | 无回滚(无失败) |
4 | security_task_alloc(p, clone_flags) | 安全模块(如 SELinux)相关初始化 | bad_fork_cleanup_audit |
5 | copy_semundo(clone_flags, p) | 复制 System V 信号量 undo 状态 | bad_fork_cleanup_security |
6 | copy_files(clone_flags, p, args->no_files) | 复制或共享文件描述符表(如 open files ) | bad_fork_cleanup_semundo |
7 | copy_fs(clone_flags, p) | 复制或共享文件系统信息(如 cwd、root) | bad_fork_cleanup_files |
8 | copy_sighand(clone_flags, p) | 复制或共享信号处理器(signal handler) | bad_fork_cleanup_fs |
9 | copy_signal(clone_flags, p) | 复制或共享信号状态(阻塞信号集等) | bad_fork_cleanup_sighand |
10 | copy_mm(clone_flags, p) | 复制或共享内存描述符(内存空间) | bad_fork_cleanup_signal |
11 | copy_namespaces(clone_flags, p) | 创建或共享命名空间(如 UTS、IPC、Mount 等) | bad_fork_cleanup_mm |
12 | copy_io(clone_flags, p) | 复制或共享 IO 上下文(如 IO调度器相关) | bad_fork_cleanup_namespaces |
13 | copy_thread(p, args) | 初始化线程状态:栈、寄存器、TLS 等 | bad_fork_cleanup_io |
3. do_fork中的wake_up_new_task流程
那么在结束copy_process流程后,一个新的进程或线程就创建成功了,接下来就是要去运行它,还记得在copy_process()/sched_fork()
中将task->state
置为TASK_NEW
的作用吗?那么在wake_up_new_task
中把新进程的状态从 TASK_NEW
切换到 TASK_RUNNING
。
在 SMP 系统上,创建新进程是执行负载均衡的绝佳时机,为新进程选择一个负载最轻的处理器。
这时使用__set_task_cpu()
将任务放到负载最轻的处理器上,实现负载均衡
流程:
- 调整state为running
- __set_task_cpu(),负载均衡
- 锁rq
- 更新rq
- 释放锁
至此,一个新进程/线程的创建就正式结束了。
3. 进程的退出
3.1 退出概念简介
退出,又分为
- 主动退出
- exit,exit_group
- 被动退出
- kill,tgkill 被信号通知退出
当进程退出的时候,根据父进程是否关注子进程退出事件,处理存在如下差异。
(1)如果父进程关注子进程退出事件,那么进程退出时释放各种资源,只留下一个空的进程描述符,变成僵尸进程(即task_struct没有被完全释放)[因为系统需要保留一些关于子进程的信息,供父进程查询(例如子进程的退出状态)],发送信号 SIGCHLD(CHLD 是 child 的缩写)通知父进程,父进程在查询进程终止的原因以后回收子进程的进程描述符。
(2)如果父进程不关注子进程退出事件,那么进程退出时释放各种资源,释放进程描述符,自动消失。进程默认关注子进程退出事件,如果不想关注,可以使用系统调用 sigaction 针对信号SIGCHLD 设置标志 SA_NOCLDWAIT(CLD 是 child 的缩写),以指示子进程退出时不要变成僵尸进程,或者设置忽略信号 SIGCHLD。
3.2 exit_group线程组退出
1. exit_group简介
exit_group() 可以被看作是 信号传递式退出函数 的一种典型代表,它的语义是:终止整个线程组(即进程的所有线程)。
🧠 和普通 exit()
的区别:
函数 | 影响范围 | 特点 |
---|---|---|
exit() | 仅当前线程 | 不会终止进程中其他线程 |
exit_group() | 当前线程 + 同一进程中的所有线程 | 会向整个线程组中的所有线程发出终止信号 |
🧩 内核行为概览
exit_group()
最终调用的是do_group_exit()
- 它会设置
signal->group_exit_code
,标记线程组整体退出; - 然后会逐个 wake up 其他线程,让它们也进入退出流程;
- 所有线程会逐个进入
do_exit()
,释放资源、触发钩子等。
📦 为什么说它是“信号式”的?
尽管 exit_group()
本质是一个系统调用,但其实现通过内部机制模拟了类似“信号传播”的退出方式: 通过共享的 struct signal_struct
对线程组成员**“广播”退出状态**,这就像是“向整个线程组传递了一个退出意图”。
2. exit_group具体流程
假设一个线程组有两个线程,称为线程 1 和线程 2,线程 1 调用 exit_group 使线程组退
出,线程 1 的执行过程如下。
(1)把退出码保存在信号结构体的成员 group_exit_code 中,传递给线程 2。
(2)给线程组设置正在退出的标志。
(3)向线程 2 发送杀死信号,然后唤醒线程 2,让线程 2 处理杀死信号。
(4)线程 1 调用函数 do_exit 以退出。
线程 2 退出的执行流程如下图所示,线程 2 准备返回用户模式的时候,发现收到
了杀死信号,于是处理杀死信号,调用函数 do_group_exit,函数 do_group_exit 的执行过
程如下。
3.3 kill
系统调用 kill(源文件“kernel/signal.c”)负责向线程组或者进程组发送信号。
(1)如果参数 pid 大于 0,那么调用函数 kill_pid_info 来向线程 pid 所属的线程组发送信号。
(2)如果参数 pid 等于 0,那么向当前进程组发送信号。
(3)如果参数 pid 小于−1,那么向组长标识符为-pid 的进程组发送信号。
(4)如果参数 pid 等于−1,那么向除了 1 号进程和当前线程组以外的所有线程组发送信号。
相关文章:
linux内核进程管理(1)——创建,退出
linux源码阅读——进程管理(1) 1. 进程的基本介绍1.1 linux中进程和线程的区别1.2 task_struct中的基本内容1.3 命名空间ns(namespace)命名空间结构图Linux 中的命名空间类型 1.4 进程标识符 2. 创建一个进程的流程2.1 CLONE宏2.2 创建进程系统调用1. do…...
容器修仙传 我的灵根是Pod 第8章 护山大阵(DaemonSet)
第三卷:上古遗迹元婴篇 第8章 护山大阵(DaemonSet) 九霄之上,雷云如怒海翻腾。 天调度宗的护山大阵「九霄雷光阵」正发出悲鸣,七十二根镇山雷柱已有半数熄灭。每根雷柱底部,本该守护节点的「雷符傀儡」&a…...
使用Python将YOLO的XML标注文件转换为TXT文件格式
使用Python将YOLO的XML标注文件转换为TXT文件格式,并划分数据集 import xml.etree.ElementTree as ET import os from os import listdir, getcwd from os.path import join import random from shutil import copyfile from PIL import Image# 只要改下面的CLASSE…...
在面试中被问到spring是什么?
Spring框架的核心回答 1. 定义与定位 Spring是一个轻量级、开源的企业级应用开发框架,旨在简化Java应用的开发,提供全面的编程和配置模型。它的核心目标是解决企业应用开发的复杂性,通过模块化设计和松耦合架构,帮助开发者更高效…...
MongoDB Ubuntu 安装
MongoDB 安装 https://www.mongodb.com/zh-cn/docs/manual/installation/ https://www.mongodb.com/zh-cn/docs/manual/tutorial/install-mongodb-on-ubuntu/ https://www.mongodb.com/zh-cn/docs/manual/reference/ulimit/ https://www.mongodbmanager.com/download ubun…...
ElasticSearch深入解析(三):Elasticsearch 7的安装与配置、Kibana安装
文章目录 〇、简介1.Elasticsearch简介2.典型业务场景3.数据采集工具4.名词解释 一、安装1.使用docker(1)创建虚拟网络(2)Elasticsearch安装步骤 2.使用压缩包 二、配置1.目录介绍2.配置文件介绍3.elasticsearch.yml节点配置4.jvm.options堆配置问题:为什么说堆内存…...
初始SpringBoot
此文介绍一些有关我对SpringBoot的学习理解, 声明:此处我的IDEA是企业版的,可能和社区版会有一些差异 1. 第⼀个SpringBoot程序 1. SpringBoot介绍 我们看下Spring官方的介绍 可以看到,Spring让Java程序更加快速,简单和安全.Spring对于速…...
【算法笔记】动态规划基础(一):dp思想、基础线性dp
目录 前言动态规划的精髓什么叫“状态”动态规划的概念动态规划的三要素动态规划的框架无后效性dfs -> 记忆化搜索 -> dp暴力写法记忆化搜索写法记忆化搜索优化了什么?怎么转化成dp?dp写法 dp其实也是图论首先先说结论:状态DAG是怎样的…...
C++入门基础(2)
Hello~,欢迎大家来到我的博客进行学习! 目录 1.缺省参数2.函数重载3.引用3.1 引用的概念和定义3.2 引用的特性3.3引用的使用3.4 const引用3.5 指针和引用的关系扩展 4. nullptr 1.缺省参数 缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。在调用该函数时&…...
OpenCV-Python (官方)中文教程(部分一)_Day15
18.图像梯度 梯度简单来说就是求导。 OpenCV 提供了三种不同的梯度滤波器,或者说高通滤波器:Sobel, Scharr和Laplacian。Sobel,Scharr 其实就是求一阶或二阶导数。Scharr 是对 Sobel(使用小的卷积核求解求解梯度角度时)的优化。Laplacian 是…...
大厂面试:MySQL篇
前言 本章内容来自B站黑马程序员java大厂面试题和小林coding 博主学习笔记,如果有不对的地方,海涵。 如果这篇文章对你有帮助,可以点点关注,点点赞,谢谢你! 1.MySQL优化 1.1 定位慢查询 定位 一个SQL…...
软件工程的13条“定律”:从Hyrum定律到康威定律,再到Zawinski定律
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
Linux删除大文件df空间avail空间不增加
背景 根磁盘被同事写满,使用> 删除一些安装包后,df中的avail空间还是0 排除有进程正在占用文件,已使用lsof命令检测过我所删的文件是没有进程在使用 原因 是文件系统预留空间在作祟 解决 # 文件系统预留块 tune2fs -l /dev/vda2 | gr…...
【C语言-选择排序算法】实现对十个数进行排序
目录 前言 一、选择排序算法原理 二、选择排序算法实现对十个数进行排序 三、代码运行示例 四、选择排序算法的时间复杂度和空间复杂度分析 五、选择排序算法的优缺点 六、总结 前言 在计算机科学领域,排序算法是基石般的存在,它们就像是整理杂乱…...
驱动开发硬核特训 · Day 18:深入理解字符设备驱动与子系统的协作机制(以 i.MX8MP 为例)
日期:2025年04月23日 回顾:2025年04月22日(Day 17:Linux 中的子系统概念与注册机制) 本日主题:字符设备驱动 子系统协作机制剖析 学习目标:理解字符设备的注册原理,掌握其与子系统间…...
SQL Server 2022 常见问题解答:从安装到优化的全场景指南
SQL Server 2022 作为微软最新的数据库管理系统,在性能、安全性和云集成方面带来了多项革新。然而,用户在实际使用中仍可能遇到各类问题。本文将围绕安装配置、性能优化、备份恢复、安全设置、高可用性方案、兼容性问题及错误代码解析等核心场景…...
软件开发版本库命名规范说明
背景:近期一直再更新自己所开发的一个前端大文件上传npm库(enlarge-file-upload),为了让库的发版更加规范,于是参考了各种文档写下了这篇关于软件开发库的版本命名规范,且不仅局限于前端的版本命名规范,适用于整个软件…...
Kafka 详解
1.基本概念:Kafka 是分布式发布 - 订阅消息系统,具有高吞吐量、可扩展性等优势,支持点对点和发布订阅两种消息模式,涉及 Broker、Topic、Partition 等多种角色。 2.安装步骤:需先安装 JDK 和 Zookeeper,下…...
【Qwen2.5-VL 踩坑记录】本地 + 海外账号和国内账号的 API 调用区别(阿里云百炼平台)
API 调用 阿里云百炼平台的海内外 API 的区别: 海外版:需要进行 API 基础 URL 设置国内版:无需设置。 本人的服务器在香港,采用海外版的 API 时,需要进行如下API端点配置 / API基础URL设置 / API客户端配置…...
硬核解析:整车行驶阻力系数插值计算与滑行阻力分解方法论
引言:阻力优化的核心价值 在汽车工程领域,行驶阻力是影响动力性、经济性及排放的核心因素。根据统计,车辆行驶中约60%的燃油消耗用于克服阻力(风阻、滚阻、传动内阻等)。尤其在电动化趋势下,阻力降低1%可提…...
【网络原理】TCP提升效率机制(一):滑动窗口
目录 一. 前言 二. 滑动窗口 三. 丢包现象 1)ACK报文丢失 2)数据丢失 四. 总结 一. 前言 TCP最核心的机制就是可靠传输 ,确认应答,超时重传,连接管理这些都保证了可靠传输,得到了可靠传输,…...
移动端使用keep-alive将页面缓存和滚动缓存具体实现方法 - 详解
1. 配置组件名称 确保列表页组件设置了name选项,(组合式API额外配置): <!-- vue2写法 --> export default {name: UserList // 必须与 <keep-alive> 的 include 匹配 }<!-- vue3写法 --> defineOptions({na…...
工作记录9
1.点击按钮发送AJAX请求 <!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title&…...
Java 异常 SSLException: fatal alert: protocol_version 全解析与解决方案
在 Java 网络通信中,SSLException: fatal alert: protocol_version 是典型的 TLS/SSL 协议版本不兼容异常。本文结合 Java 官方规范、TLS 协议标准及实战经验,提供体系化解决方案,帮助开发者快速定位并解决协议版本冲突问题。 一、异常本质&…...
连锁美业管理系统「数据分析」的重要左右分析︳博弈美业系统疗愈系统分享
美业管理系统中的数据分析功能在提升运营效率、优化客户体验、增强决策科学性等方面具有重要作用。 数据分析功能将美业从“经验驱动”升级为“数据驱动”,帮助商家在客户管理、成本控制、服务创新等环节实现精细化运营,最终提升盈利能力与品牌竞争力…...
Openharmony 和 HarmonyOS 区别?
文章目录 OpenHarmony 与 HarmonyOS 的区别:开源生态与商业发行版的定位差异一、定义与定位二、技术架构对比1. OpenHarmony2. HarmonyOS 三、应用场景差异四、开发主体与生态支持五、关键区别总结六、如何选择?未来展望 OpenHarmony 与 HarmonyOS 的区别…...
26.OpenCV形态学操作
OpenCV形态学操作 形态学操作(Morphological Operations)源自二值图像处理,主要用于分析和处理图像中的结构元素,对图像进行去噪、提取边缘、分割等预处理步骤。OpenCV库中提供了丰富的形态学函数,常见的包括…...
uniapp小程序使用echarts
1、引入插件 在Dcloud插件市场下载echarts插件:插件地址 2、页面使用简单示例: <template><view class"pie-view flex-center"><view style"width: 100%; height: 600rpx"><l-echart ref"chartRef&quo…...
Vue 中 使用 Mixins 解决 多页面共用相同组件的相关问题
1. 需要解决的问题 最近在vue项目中,有多个页面需要用到同一个组件,至于是什么组件,这里不重要,重要的这个组件需要被多个文件引用,而且有组件有一些控制逻辑。 1.1代码展示 <template><div class"ap…...
Rust 学习笔记:Rust 简介
Rust 学习笔记:Rust 简介 Rust 学习笔记:Rust 简介历史与发展历程核心特性优点缺点应用领域 Rust 学习笔记:Rust 简介 Rust 是一种系统级编程语言,由 Mozilla 研究院的 Graydon Hoare 于 2006 年设计,旨在提供内存安全…...
第六节:进阶特性高频题-自定义指令实现场景
示例:v-lazy(图片懒加载)、v-permission(权限控制) 钩子函数:mounted、updated、unmounted等 一、自定义指令核心机制 指令生命周期钩子 const myDirective {// 元素插入父节点时调用(初始化…...
未曾设想的道路1
写在前面: 与其转去读博,倾向自学就业。 中国科学技术大学数学科学学院拥有一支优秀的师资团队,以下是部分教授的简介: 陈发来教授: 荣誉:2024年6月13日,在德国莱布尼茨信息科学中心召开的国际…...
Axure按钮设计分享:打造高效交互体验的六大按钮类型
在产品设计过程中,按钮作为用户与界面交互的核心元素,其设计质量直接影响用户体验与操作效率。Axure作为一款强大的原型设计工具,为设计师提供了丰富的按钮设计选项。本文将围绕基础按钮、禁用按钮、圆角按钮、动态按钮、渐变按钮和图标按钮六…...
MySQL 8.4企业版 安装和配置审计插件
在最新的MySQL 8.4.4企业版上启用审计日志功能 操作系统:Ubuntu 24.04 数据库:8.4.4-commercial for Linux on x86_64 (MySQL Enterprise Server - Commercial) 1.查看安装脚本 下面2个脚本位于mysql安装目录 share 下,一个是window一个是linux可以用…...
AI大模型学习十一:尝鲜ubuntu 25.04 桌面版私有化sealos cloud + devbox+minio,实战运行成功
一、说明 用了ubuntu 25.04,内核为GNU/Linux 6.14.0-15-generic x86_64,升级了部分image,过程曲折啊 sealos 能干啥 对集群生命周期进行管理,一键安装高可用 Kubernetes 集群,增删节点清理集群自恢复等 通过 sealos…...
idea无法下载源代码
通过idea找到用户设置文件路径 查看 setting.xml 文件,找到了以下相关的配置,注释掉这个maven-default-http-blocker的镜像,这个东西阻碍了去阿里的镜像库查找依赖,注释掉。 然后重启idea就能下载了...
【敏矽微ME32G030系列】介绍、环境搭建、工程测试
【敏矽微ME32G030系列】介绍、环境搭建、工程测试 本文介绍了敏矽微ME32G030系列单片机及开发板、包括参数特点、原理图、应用场景,以及开发环境搭建、工程测试等流程。 简介 本节介绍了开发板主控、特点、开发板原理图、板载资源等信息。 主控 开发板采用 ME3…...
Hooks的使用限制及原因
Hooks的使用限制及原因 Hooks的核心限制 只能在函数组件顶层调用 ⭐不能在条件语句、循环、嵌套函数中调用 ⭐只能在React函数组件或自定义Hooks中调用 ⭐ 为什么有这些限制? 根本原因:React依赖Hooks的调用顺序 React内部使用数组来存储每个组件的…...
【JavaScript】二十六、正则表达式
文章目录 1、正则表达式1.1 定义1.2 校验 2、元字符2.1 边界符2.2 量词2.3 字符类2.3.1 方括号[ ]2.3.2 小点.2.3.3 预定义 2.4 案例:用户名验证 3、修饰符3.1 语法3.2 案例:过滤敏感词 1、正则表达式 Regular Expression,正则表达式&#x…...
Geek强大的电脑卸载软件工具,免费下载
一款强大的卸载电脑软件工具,无需安装 免费下载...
tomcat Server 连接服务器 进展
由于机房的环境变更,所接触的问题也不一样!!!! 但后来出现以下提示: 已连接到服务器 配置错误: 部署源 springmvc:war 无效[2025-04-23 11:19:50,192] 工件 springmvc:war: 部署工件时出错。请参阅服务器日…...
Elasticsearch 集群节点下线方案
Elasticsearch 集群节点下线方案 在 Elasticsearch(ES)集群中,节点(Node)下线可能会影响数据的可用性和集群的健康状态。因此,正确的下线步骤需要确保数据不会丢失,并且不会影响查询或写入。 &…...
【模板匹配】图像处理(OpenCV)-part10
19.1模板匹配 模板匹配就是用模板图(通常是一个小图)在目标图像(通常是一个比模板图大的图片)中不断的滑动比较,通过某种比较方法来判断是否匹配成功,找到模板图所在的位置。 不会有边缘填充。 类似于卷积,…...
VMware中CentOS 7虚拟机设置固定IP(NAT模式)完整教程
前言 在VMware中为CentOS 7虚拟机配置固定IP是搭建稳定服务环境的关键步骤。本文基于用户提供的最新配置文件,详细演示如何从DHCP自动获取IP调整为固定IP(192.168.89.129),并提供修改前后的配置对比及操作验证。 一、当前配置状态…...
Ragflow、Dify、FastGPT、COZE核心差异对比与Ragflow的深度文档理解能力和全流程优化设计
一、Ragflow、Dify、FastGPT、COZE核心差异对比 以下从核心功能、目标用户、技术特性等维度对比四款工具的核心差异: 核心功能定位 • Ragflow:专注于深度文档理解的RAG引擎,擅长处理复杂格式(PDF、扫描件、表格等)的…...
飞帆控件:在编辑模式下额外加载的库
飞帆是一个自由的控件设计平台。在飞帆中,我们可以很方便地创建基于 Vue 2 组件的控件,并使用控件来搭建网页。 他山之石,可以攻玉。在创建控件中,使用 js 、css 依赖库能让我们的控件更强大。 有些时候,在编辑模式下…...
Agentic AI——当AI学会主动思考与决策,世界将如何被重塑?
一、引言:2025,Agentic AI的元年 “如果ChatGPT是AI的‘聊天时代’,那么2025年将开启AI的‘行动时代’。”——Global X Insights[1] 随着Agentic AI(自主决策型人工智能)的崛起,AI系统正从被动应答的“工具…...
68元撬动未来:明远智睿2351开发板重塑嵌入式开发生态
在嵌入式开发领域,价格与性能的矛盾始终存在:高端开发板功能强大但成本高昂,低价产品则往往受限于性能与扩展性。明远智睿2351开发板以68元(含税)的定价打破这一僵局,通过四核1.4G处理器、全功能Linux系统与…...
C# 全局 Mutex 是否需使用 `Global\` 前缀
回顾一下Mutex在Windows中的作用。Mutex是用于同步多个进程或线程的机制,确保同一时间只有一个实例访问资源。当创建Mutex时,如果命名时没有指定Global\前缀,默认可能是在会话内创建的,也就是只在当前用户会话中可见。这样的话&am…...
C# 中的 `lock` 关键字本质
C# 中的 lock 关键字本质上是基于 Monitor 类实现的线程同步机制,其核心是通过 互斥锁(Mutex) 确保代码块的原子性执行。以下是其实现本质的分步解析: 1. 语法糖的转换 当使用 lock 关键字时: lock (obj) {// 临界区…...