[TOC]
kernel/fork.c 进程创建(Process Creation) Unix/Linux的基石 历史与背景 这项技术是为了解决什么特定问题而诞生的? kernel/fork.c 是Linux内核的心脏之一,它实现了进程创建的机制。这项技术是为了解决多任务操作系统中的一个根本问题:如何动态地创建新的、独立的执行流(即进程) 。
在单任务系统中,整个系统只有一个执行上下文。为了实现并发和多用户,系统必须有能力创建新的进程。Unix的设计者为此提出了一个极其优雅且强大的模型:fork() 。
克隆与变形(Fork-and-Exec) :fork()的核心思想不是从零开始创建一个空进程,而是克隆(Clone)当前进程。子进程在创建的瞬间,几乎是父进程的一个完美副本(拥有相同的内存映像、打开的文件等)。然后,子进程通常会通过exec()系统调用来加载并执行一个 新的 程序,从而“变形”为一个完全不同的进程。
上下文继承 :这种模型的强大之处在于子进程可以继承父进程的上下文(如环境变量、文件描述符),这使得像Shell中的管道(|)和I/O重定向(>)等功能实现起来非常简洁。
kernel/fork.c 就是这一模型的内核实现,它提供了一套机制来复制进程所需的所有资源,并管理这个过程。
它的发展经历了哪些重要的里程碑或版本迭代? fork()的演进是Linux性能和功能发展的缩影。
原始fork() :最早的fork()实现非常“耿直”,它会完整地复制父进程的整个物理内存给子进程。对于大型进程来说,这个操作非常缓慢且浪费资源。
写时复制(Copy-on-Write, COW) :这是fork()发展史上最重要的里程碑。为了解决完整复制的性能问题,内核引入了COW机制。当fork()被调用时,内核不再复制物理内存页,而是让父子进程共享同一套物理内存页,同时将这些页的页表项标记为“只读”。fork()调用因此变得极快。只有当父进程或子进程尝试写入 某个共享页时,内核才会捕获这个写保护异常,此时才真正为写入方分配一个新的物理页,并将旧页的内容复制过去。
clone()系统调用 :Linux对fork()模型进行了重大扩展,引入了clone()系统调用。fork()可以看作是“全盘复制”,而clone()则像一个“瑞士军刀”,它允许调用者通过一系列标志(flags)精细地控制 哪些资源被子进程共享,哪些被复制。这个里程碑式的创新使得在Linux上高效实现线程 (共享地址空间、文件描述符等)和容器 (共享或隔离命名空间)成为可能。fork()和vfork()现在都只是clone()系统调用的一个特例封装。
目前该技术的社区活跃度和主流应用情况如何? kernel/fork.c是内核最核心、最稳定的代码之一。它的代码不会像某些驱动那样频繁地进行颠覆性修改,但由于其中心地位,任何对内存管理、调度器、安全、命名空间的修改都可能需要与之协同,因此它始终处于持续的维护和优化中。它是所有Linux系统上每一个进程创建的必经之路 ,从启动init进程到用户在shell中执行的每一条命令,都依赖于此文件中的逻辑。
核心原理与设计 它的核心工作原理是什么? kernel/fork.c的核心是_do_fork()函数(或其现代变体)。当用户空间调用fork(), vfork(), clone()时,最终都会汇集到这个内核函数,它按照一个精确的流程来创建一个新进程。
核心流程:
复制task_struct :内核首先调用dup_task_struct()来复制父进程的task_struct。这是内核中描述一个进程/线程所有信息的“总纲”数据结构。
检查限制 :检查当前用户创建的进程数是否超过了RLIMIT_NPROC等资源限制。
资源复制或共享 :这是最复杂的一步。_do_fork()会根据clone()传入的标志,决定如何处理各项资源:
copy_mm() : 处理内存地址空间。如果没有 指定CLONE_VM,则调用dup_mm()复制父进程的mm_struct,这包括复制页表,并应用**写时复制(COW)**策略。如果指定了CLONE_VM(创建线程的典型情况),则子进程与父进程共享同一个mm_struct。
copy_files(), copy_fs(), copy_sighand()等 :类似地,根据CLONE_FILES, CLONE_FS, CLONE_SIGHAND等标志,决定是复制文件描述符表、文件系统信息、信号处理器等,还是与父进程共享。
copy_namespaces() : 如果指定了CLONE_NEW*系列标志,则为子进程创建新的命名空间,这是容器技术的基础。
分配PID :调用alloc_pid()为新进程分配一个唯一的PID(在相应的PID命名空间中)。
唤醒子进程 :新创建的子进程处于不可运行状态。在所有复制工作完成后,内核会将其设置为可运行状态,并将其放入调度器的运行队列中。
返回 :fork()最奇特的特性之一是它“一次调用,两次返回”。在父进程中,它返回新创建子进程的PID;而在子进程中,它返回0。_do_fork()的末尾会处理这个逻辑。
它的主要优势体现在哪些方面?
效率 :得益于写时复制(COW),Linux的fork()调用非常快,其开销主要在于复制页表,而非整个内存。
灵活性和通用性 :clone()系统调用提供了一个极其强大的统一接口,可以用它来创建传统的重型进程、轻量级的线程以及隔离的容器。
简洁的编程模型 :fork-exec模型虽然在某些方面有其开销,但它允许子进程在继承父进程环境后进行精细的调整(如重定向I/O),然后再转变为新程序,这是一种非常强大且简洁的范式。
它存在哪些已知的劣势、局限性或在特定场景下的不适用性?
fork-exec的开销 :在子进程被创建后立即调用exec()的常见场景中,为子进程复制页表等操作实际上是浪费的,因为这些数据马上就会被新程序覆盖。vfork()和posix_spawn()就是为了优化这种情况而生的。
内存过载(Overcommit)的风险 :COW机制使得创建大量看似拥有海量内存的进程成为可能。但如果这些进程都开始写入它们各自的内存副本,物理内存可能会迅速耗尽,从而触发内核的OOM(Out-of-Memory) Killer。
vfork()的危险性 :vfork()是一种过时的优化。它让子进程在父进程的地址空间中运行,并阻塞父进程。子进程在调用exec()或_exit()之前对内存的任何修改都会影响到父进程,这非常危险,应极力避免使用。
使用场景 在哪些具体的业务或技术场景下,它是首选解决方案? 它是Linux上创建进程的唯一 内核级解决方案,因此所有需要创建新执行流的场景都是它的使用场景。
Shell执行命令 :当你在bash中输入ls -l时,bash进程会fork()一个子进程,然后子进程调用execve("ls", ...)来执行ls程序。
多进程服务 :像Apache Web服务器(在prefork模式下)会预先fork()出多个子进程来处理并发的HTTP请求。
创建线程 :用户空间的pthread_create()库函数,其底层实现就是调用了clone()系统调用,并传入了CLONE_VM, CLONE_SIGHAND, CLONE_FILES等一系列标志。
容器启动 :docker run等命令,其底层也是通过clone()并配合CLONE_NEW*系列标志来创建一个与宿主机隔离的新进程环境。
是否有不推荐使用该技术的场景?为什么? 如上所述,vfork()因其危险性而不被推荐。此外,对于fork-exec模式的性能敏感场景,可以考虑使用posix_spawn()。posix_spawn()是一个库函数,它可以在内部进行优化,可能会选择比fork-exec更高效的方式来创建进程并加载新程序,从而避免不必要的页表复制。
对比分析 请将其 与 其他相似技术 进行详细对比。 在kernel/fork.c的语境下,最有意义的对比是fork(), vfork()和clone()这三个核心调用。
特性
fork()
vfork() (不推荐使用)
clone()
本质
创建一个传统的、独立的子进程。
fork()的一种不安全的、过时的优化。
创建进程/线程的通用、可配置的底层原语。
地址空间 (mm_struct)
复制 (使用写时复制COW策略)。父子进程有独立的地址空间。
共享 。子进程在父进程的地址空间中运行。
可配置 。通过CLONE_VM标志决定是复制还是共享。
父进程行为
父子进程并发执行(由调度器决定顺序)。
阻塞 ,直到子进程调用exec()或_exit()。
父子进程并发执行。
资源处理
复制文件描述符、信号处理器等。
共享。
可配置 。通过CLONE_FILES, CLONE_SIGHAND等标志精细控制。
安全性
高。父子进程隔离良好。
极低 。子进程对内存的修改会直接影响父进程,极易出错。
高。开发者明确知道哪些资源被共享。
主要用途
标准的进程创建。
仅用于优化fork()后立即exec()的场景,但已被COW和posix_spawn()取代。
创建线程、创建容器、以及其他需要精细控制资源共享的场景。
与pthread_create()的对比 :pthread_create()是一个用户空间的库函数,而不是系统调用。它为程序员提供了符合POSIX标准的线程创建接口。在Linux上,pthread_create()的实现(在glibc/NPTL中)最终会调用clone()系统调用,并传入一套预设的标志(如CLONE_VM | CLONE_FS | CLONE_FILES | ...)来创建一个与调用者共享大部分资源的“轻量级进程”,即内核眼中的线程。
include/uapi/linux/sched.h cloning flags 进程或线程创建的标志 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 #define CSIGNAL 0x000000ff #define CLONE_VM 0x00000100 #define CLONE_FS 0x00000200 #define CLONE_FILES 0x00000400 #define CLONE_SIGHAND 0x00000800 #define CLONE_PIDFD 0x00001000 #define CLONE_PTRACE 0x00002000 #define CLONE_VFORK 0x00004000 #define CLONE_PARENT 0x00008000 #define CLONE_THREAD 0x00010000 #define CLONE_SETTLS 0x00080000 #define CLONE_NEWNS 0x00020000 #define CLONE_NEWCGROUP 0x02000000 #define CLONE_NEWUTS 0x04000000 #define CLONE_NEWIPC 0x08000000 #define CLONE_NEWUSER 0x10000000 #define CLONE_NEWPID 0x20000000 #define CLONE_NEWNET 0x40000000 #define CLONE_SYSVSEM 0x00040000 #define CLONE_PARENT_SETTID 0x00100000 #define CLONE_CHILD_CLEARTID 0x00200000 #define CLONE_DETACHED 0x00400000 #define CLONE_UNTRACED 0x00800000 #define CLONE_CHILD_SETTID 0x01000000 #define CLONE_IO 0x80000000
include/linux/sched/task.h get_task_struct 获取任务结构体 1 2 3 4 5 static inline struct task_struct *get_task_struct (struct task_struct *t) { refcount_inc(&t->usage); return t; }
put_task_struct 释放任务结构体 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 static inline void put_task_struct (struct task_struct *t) ```c { if (!refcount_dec_and_test(&t->usage)) return ; if (!IS_ENABLED(CONFIG_PREEMPT_RT) || preemptible()) { static DEFINE_WAIT_OVERRIDE_MAP (put_task_map, LD_WAIT_SLEEP) ; lock_map_acquire_try(&put_task_map); __put_task_struct(t); lock_map_release(&put_task_map); return ; } call_rcu(&t->rcu, __put_task_struct_rcu_cb); }
kernel/fork.c fork 和 vfork 的区别 vfork 和 fork 都是用于创建新进程的系统调用,但它们的行为和使用场景有所不同。以下是两者的主要区别:
1. 内存空间处理
fork :
父进程和子进程各自拥有独立的内存空间。
使用 写时复制(Copy-On-Write, COW) 技术,父子进程共享内存页,直到其中一个进程尝试写入时才会复制内存页。
适合需要独立运行的父子进程。
vfork :
子进程直接共享父进程的内存空间。
在子进程调用 execve 或 exit 之前,父进程会被阻塞,无法运行。
不使用写时复制,效率更高,但子进程在修改内存时会直接影响父进程。
2. 性能
fork :
因为需要设置写时复制机制,fork 的性能相对较低,尤其是在父进程的内存空间较大时。
vfork :
不需要设置写时复制,性能更高。
适合在子进程立即调用 execve 加载新程序的场景。
3. 父进程的行为
fork :
vfork :
父进程会被阻塞,直到子进程调用 execve 或 exit。
子进程运行期间,父进程无法执行任何操作。
4. 安全性
fork :
父子进程的内存空间独立,安全性更高。
子进程的操作不会影响父进程。
vfork :
子进程共享父进程的内存空间,可能会导致父进程的内存被意外修改。
使用不当可能引发严重问题。
5. 使用场景
fork :
适用于需要创建独立进程的场景,例如多进程服务器或守护进程。
父子进程可以独立运行,互不干扰。
vfork :
适用于子进程立即调用 execve 加载新程序的场景。
更高效,但需要谨慎使用,避免子进程修改父进程的内存。
6. 系统调用的实现
fork :
在 Linux 内核中通过 do_fork 和 copy_process 实现。
使用写时复制技术,减少内存复制的开销。
vfork :
在 Linux 内核中通过 do_fork 实现,但设置了特殊的标志(CLONE_VFORK 和 CLONE_VM)。
子进程共享父进程的内存空间,并阻塞父进程。
总结
特性
fork
vfork
内存空间
独立,使用写时复制
共享父进程内存空间
性能
较低,适合复杂场景
较高,适合快速加载新程序
父进程行为
父子进程并发运行
父进程阻塞,等待子进程完成
安全性
高,子进程不会影响父进程
较低,子进程可能修改父进程内存
使用场景
创建独立进程,适合复杂任务
快速加载新程序,适合简单任务
fork 提供了更强的隔离性和灵活性,而 vfork 提供了更高的性能,但需要谨慎使用以避免潜在的安全问题。
set_task_stack_end_magic 在栈底设置magic,用于检测栈溢出
注意设置堆栈时特地预留了8字节用于填入magic
1 2 3 4 5 6 7 void set_task_stack_end_magic (struct task_struct *tsk) { unsigned long *stackend; stackend = end_of_stack(tsk); *stackend = STACK_END_MAGIC; }
mm_cache_init 创建一个内存缓存(kmem_cache)
创建一个内存缓存(kmem_cache),专门用于分配和管理 mm_struct 结构体的内存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 static struct kmem_cache *mm_cachep ;void __init mm_cache_init (void ) { unsigned int mm_size; mm_size = sizeof (struct mm_struct) + cpumask_size() + mm_cid_size(); mm_cachep = kmem_cache_create_usercopy("mm_struct" , mm_size, ARCH_MIN_MMSTRUCT_ALIGN, SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT, offsetof(struct mm_struct, saved_auxv), sizeof_field(struct mm_struct, saved_auxv), NULL ); }
task_struct_whitelist 任务结构体白名单 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 static inline void arch_thread_struct_whitelist (unsigned long *offset, unsigned long *size) { *offset = *size = 0 ; } static void __init task_struct_whitelist (unsigned long *offset, unsigned long *size) { arch_thread_struct_whitelist(offset, size); if (unlikely(*size == 0 )) *offset = 0 ; else *offset += offsetof(struct task_struct, thread); }
set_max_threads 计算和设置系统允许的最大线程数 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 #define MIN_THREADS 20 static void __init set_max_threads (unsigned int max_threads_suggested) { u64 threads; unsigned long nr_pages = memblock_estimated_nr_free_pages(); if (fls64(nr_pages) + fls64(PAGE_SIZE) > 64 ) threads = MAX_THREADS; else threads = div64_u64((u64) nr_pages * (u64) PAGE_SIZE, (u64) THREAD_SIZE * 8UL ); if (threads > max_threads_suggested) threads = max_threads_suggested; max_threads = clamp_t (u64, threads, MIN_THREADS, MAX_THREADS); }
fork_init 设置与进程管理相关的核心数据结构和资源 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 void __init fork_init (void ) { int i; #ifndef ARCH_MIN_TASKALIGN #define ARCH_MIN_TASKALIGN 0 #endif int align = max_t (int , L1_CACHE_BYTES, ARCH_MIN_TASKALIGN); unsigned long useroffset, usersize; task_struct_whitelist(&useroffset, &usersize); task_struct_cachep = kmem_cache_create_usercopy("task_struct" , arch_task_struct_size, align, SLAB_PANIC|SLAB_ACCOUNT, useroffset, usersize, NULL ); arch_task_cache_init(); set_max_threads(MAX_THREADS); init_task.signal->rlim[RLIMIT_NPROC].rlim_cur = max_threads/2 ; init_task.signal->rlim[RLIMIT_NPROC].rlim_max = max_threads/2 ; init_task.signal->rlim[RLIMIT_SIGPENDING] = init_task.signal->rlim[RLIMIT_NPROC]; for (i = 0 ; i < UCOUNT_COUNTS; i++) init_user_ns.ucount_max[i] = max_threads/2 ; set_userns_rlimit_max(&init_user_ns, UCOUNT_RLIMIT_NPROC, RLIM_INFINITY); set_userns_rlimit_max(&init_user_ns, UCOUNT_RLIMIT_MSGQUEUE, RLIM_INFINITY); set_userns_rlimit_max(&init_user_ns, UCOUNT_RLIMIT_SIGPENDING, RLIM_INFINITY); set_userns_rlimit_max(&init_user_ns, UCOUNT_RLIMIT_MEMLOCK, RLIM_INFINITY); #ifdef CONFIG_VMAP_STACK cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "fork:vm_stack_cache" , NULL , free_vm_stack_cache); #endif scs_init(); lockdep_init_task(&init_task); uprobes_init(); }
proc_caches_init 初始化与进程管理相关的内核缓存(slab caches) 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 void __init proc_caches_init (void ) { struct kmem_cache_args args = { .use_freeptr_offset = true , .freeptr_offset = offsetof(struct vm_area_struct, vm_freeptr), }; sighand_cachep = kmem_cache_create("sighand_cache" , sizeof (struct sighand_struct), 0 , SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_TYPESAFE_BY_RCU| SLAB_ACCOUNT, sighand_ctor); signal_cachep = kmem_cache_create("signal_cache" , sizeof (struct signal_struct), 0 , SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT, NULL ); files_cachep = kmem_cache_create("files_cache" , sizeof (struct files_struct), 0 , SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT, NULL ); fs_cachep = kmem_cache_create("fs_cache" , sizeof (struct fs_struct), 0 , SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT, NULL ); vm_area_cachep = kmem_cache_create("vm_area_struct" , sizeof (struct vm_area_struct), &args, SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_TYPESAFE_BY_RCU| SLAB_ACCOUNT); mmap_init(); nsproxy_cache_init(); }
__put_task_struct 释放任务结构体 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void __put_task_struct(struct task_struct *tsk){ WARN_ON(!tsk->exit_state); WARN_ON(refcount_read(&tsk->usage)); WARN_ON(tsk == current); sched_ext_free(tsk); io_uring_free(tsk); cgroup_free(tsk); task_numa_free(tsk, true ); security_task_free(tsk); exit_creds(tsk); delayacct_tsk_free(tsk); put_signal_struct(tsk->signal); sched_core_free(tsk); free_task(tsk); } EXPORT_SYMBOL_GPL(__put_task_struct);
alloc_task_struct_node 分配一个新的 task_struct 1 2 3 4 5 6 static struct kmem_cache *task_struct_cachep ;static inline struct task_struct *alloc_task_struct_node (int node) { return kmem_cache_alloc_node(task_struct_cachep, GFP_KERNEL, node); }
alloc_thread_stack_node 为任务分配线程栈 1 2 3 4 5 6 7 8 9 10 11 static int alloc_thread_stack_node (struct task_struct *tsk, int node) { struct page *page = alloc_pages_node(node, THREADINFO_GFP, THREAD_SIZE_ORDER); if (likely(page)) { tsk->stack = kasan_reset_tag(page_address(page)); return 0 ; } return -ENOMEM; }
account_kernel_stack 记录任务的内核栈使用情况 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 static void account_kernel_stack (struct task_struct *tsk, int account) { if (IS_ENABLED(CONFIG_VMAP_STACK)) { struct vm_struct *vm = task_stack_vm_area(tsk); int i; for (i = 0 ; i < THREAD_SIZE / PAGE_SIZE; i++) mod_lruvec_page_state(vm->pages[i], NR_KERNEL_STACK_KB, account * (PAGE_SIZE / 1024 )); } else { void *stack = task_stack_page(tsk); mod_lruvec_kmem_state(stack , NR_KERNEL_STACK_KB, account * (THREAD_SIZE / 1024 )); } }
dup_task_struct 为一个现有任务(task_struct)创建一个副本 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 int __weak arch_dup_task_struct (struct task_struct *dst, struct task_struct *src) { *dst = *src; return 0 ; } static struct task_struct *dup_task_struct (struct task_struct *orig, int node) { struct task_struct *tsk ; int err; if (node == NUMA_NO_NODE) node = tsk_fork_get_node(orig); tsk = alloc_task_struct_node(node); if (!tsk) return NULL ; err = arch_dup_task_struct(tsk, orig); if (err) goto free_tsk; err = alloc_thread_stack_node(tsk, node); if (err) goto free_tsk; #ifdef CONFIG_THREAD_INFO_IN_TASK refcount_set(&tsk->stack_refcount, 1 ); #endif account_kernel_stack(tsk, 1 ); #ifdef CONFIG_SECCOMP tsk->seccomp.filter = NULL ; #endif clear_tsk_need_resched(tsk); set_task_stack_end_magic(tsk); #ifdef CONFIG_STACKPROTECTOR tsk->stack_canary = get_random_canary(); #endif if (orig->cpus_ptr == &orig->cpus_mask) tsk->cpus_ptr = &tsk->cpus_mask; refcount_set(&tsk->rcu_users, 2 ); refcount_set(&tsk->usage, 1 ); tsk->splice_pipe = NULL ; tsk->task_frag.page = NULL ; tsk->wake_q.next = NULL ; tsk->worker_private = NULL ; kcov_task_init(tsk); kmsan_task_create(tsk); kmap_local_fork(tsk); return tsk; free_stack: exit_task_stack_account(tsk); free_thread_stack(tsk); free_tsk: free_task_struct(tsk); return NULL ; }
rt_mutex_init_task 初始化实时互斥锁相关的任务结构体 1 2 3 4 5 6 7 8 9 static void rt_mutex_init_task (struct task_struct *p) { raw_spin_lock_init(&p->pi_lock); #ifdef CONFIG_RT_MUTEXES p->pi_waiters = RB_ROOT_CACHED; p->pi_top_task = NULL ; p->pi_blocked_on = NULL ; #endif }
rcu_copy_process 复制进程的 RCU 相关数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 static inline void rcu_copy_process (struct task_struct *p) { #ifdef CONFIG_PREEMPT_RCU p->rcu_read_lock_nesting = 0 ; p->rcu_read_unlock_special.s = 0 ; p->rcu_blocked_node = NULL ; INIT_LIST_HEAD(&p->rcu_node_entry); #endif #ifdef CONFIG_TASKS_RCU p->rcu_tasks_holdout = false ; INIT_LIST_HEAD(&p->rcu_tasks_holdout_list); p->rcu_tasks_idle_cpu = -1 ; INIT_LIST_HEAD(&p->rcu_tasks_exit_list); #endif #ifdef CONFIG_TASKS_TRACE_RCU p->trc_reader_nesting = 0 ; p->trc_reader_special.s = 0 ; INIT_LIST_HEAD(&p->trc_holdout_list); INIT_LIST_HEAD(&p->trc_blkd_node); #endif }
copy_files 处理进程文件描述符复制 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 static int copy_files (unsigned long clone_flags, struct task_struct *tsk, int no_files) { struct files_struct *oldf , *newf ; oldf = current->files; if (!oldf) return 0 ; if (no_files) { tsk->files = NULL ; return 0 ; } if (clone_flags & CLONE_FILES) { atomic_inc (&oldf->count); return 0 ; } newf = dup_fd(oldf, NULL ); if (IS_ERR(newf)) return PTR_ERR(newf); tsk->files = newf; return 0 ; }
copy_fs 处理进程文件系统信息的复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 static int copy_fs (unsigned long clone_flags, struct task_struct *tsk) { struct fs_struct *fs = current->fs; if (clone_flags & CLONE_FS) { spin_lock(&fs->lock); if (fs->in_exec) { spin_unlock(&fs->lock); return -EAGAIN; } fs->users++; spin_unlock(&fs->lock); return 0 ; } tsk->fs = copy_fs_struct(fs); if (!tsk->fs) return -ENOMEM; return 0 ; }
copy_sighand 复制信号处理器 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 static int copy_sighand (unsigned long clone_flags, struct task_struct *tsk) { struct sighand_struct *sig ; if (clone_flags & CLONE_SIGHAND) { refcount_inc(¤t->sighand->count); return 0 ; } sig = kmem_cache_alloc(sighand_cachep, GFP_KERNEL); RCU_INIT_POINTER(tsk->sighand, sig); if (!sig) return -ENOMEM; refcount_set(&sig->count, 1 ); spin_lock_irq(¤t->sighand->siglock); memcpy (sig->action, current->sighand->action, sizeof (sig->action)); spin_unlock_irq(¤t->sighand->siglock); if (clone_flags & CLONE_CLEAR_SIGHAND) flush_signal_handlers(tsk, 0 ); return 0 ; }
copy_signal 复制信号结构体 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 static int copy_signal (unsigned long clone_flags, struct task_struct *tsk) { struct signal_struct *sig ; if (clone_flags & CLONE_THREAD) return 0 ; sig = kmem_cache_zalloc(signal_cachep, GFP_KERNEL); tsk->signal = sig; if (!sig) return -ENOMEM; sig->nr_threads = 1 ; sig->quick_threads = 1 ; atomic_set (&sig->live, 1 ); refcount_set(&sig->sigcnt, 1 ); sig->thread_head = (struct list_head)LIST_HEAD_INIT(tsk->thread_node); tsk->thread_node = (struct list_head)LIST_HEAD_INIT(sig->thread_head); init_waitqueue_head(&sig->wait_chldexit); sig->curr_target = tsk; init_sigpending(&sig->shared_pending); INIT_HLIST_HEAD(&sig->multiprocess); seqlock_init(&sig->stats_lock); prev_cputime_init(&sig->prev_cputime); #ifdef CONFIG_POSIX_TIMERS INIT_HLIST_HEAD(&sig->posix_timers); INIT_HLIST_HEAD(&sig->ignored_posix_timers); hrtimer_setup(&sig->real_timer, it_real_fn, CLOCK_MONOTONIC, HRTIMER_MODE_REL); #endif task_lock(current->group_leader); memcpy (sig->rlim, current->signal->rlim, sizeof sig->rlim); task_unlock(current->group_leader); sig->oom_score_adj = current->signal->oom_score_adj; sig->oom_score_adj_min = current->signal->oom_score_adj_min; mutex_init(&sig->cred_guard_mutex); init_rwsem(&sig->exec_update_lock); return 0 ; }
mm_init 初始化内存管理结构体(mm_struct) 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 static struct mm_struct *mm_init (struct mm_struct *mm, struct task_struct *p, struct user_namespace *user_ns) { mt_init_flags(&mm->mm_mt, MM_MT_FLAGS); atomic_set (&mm->mm_users, 1 ); atomic_set (&mm->mm_count, 1 ); seqcount_init(&mm->write_protect_seq); mmap_init_lock(mm); INIT_LIST_HEAD(&mm->mmlist); mm->map_count = 0 ; mm->locked_vm = 0 ; atomic64_set(&mm->pinned_vm, 0 ); memset (&mm->rss_stat, 0 , sizeof (mm->rss_stat)); spin_lock_init(&mm->page_table_lock); spin_lock_init(&mm->arg_lock); mm_init_cpumask(mm); RCU_INIT_POINTER(mm->exe_file, NULL ); init_tlb_flush_pending(mm); #if defined(CONFIG_TRANSPARENT_HUGEPAGE) && !defined(CONFIG_SPLIT_PMD_PTLOCKS) mm->pmd_huge_pte = NULL ; #endif if (current->mm) { mm->flags = mmf_init_flags(current->mm->flags); mm->def_flags = current->mm->def_flags & VM_INIT_DEF_MASK; } else { mm->flags = default_dump_filter; mm->def_flags = 0 ; } if (percpu_counter_init_many(mm->rss_stat, 0 , GFP_KERNEL_ACCOUNT, NR_MM_COUNTERS)) goto fail_pcpu; mm->user_ns = get_user_ns(user_ns); return mm; fail_pcpu: mm_destroy_cid(mm); fail_cid: destroy_context(mm); fail_nocontext: mm_free_id(mm); fail_noid: mm_free_pgd(mm); fail_nopgd: free_mm(mm); return NULL ; }
dup_mm 复制一个现有的内存管理结构(mm_struct) 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 #define allocate_mm() (kmem_cache_alloc(mm_cachep, GFP_KERNEL)) static struct mm_struct *dup_mm (struct task_struct *tsk, struct mm_struct *oldmm) { struct mm_struct *mm ; int err; mm = allocate_mm(); if (!mm) goto fail_nomem; memcpy (mm, oldmm, sizeof (*mm)); if (!mm_init(mm, tsk, mm->user_ns)) goto fail_nomem; err = dup_mmap(mm, oldmm); if (err) goto free_pt; mm->hiwater_rss = get_mm_rss(mm); mm->hiwater_vm = mm->total_vm; if (mm->binfmt && !try_module_get(mm->binfmt->module)) goto free_pt; return mm; free_pt: mm->binfmt = NULL ; mm_init_owner(mm, NULL ); mmput(mm); if (err) uprobe_end_dup_mmap(); fail_nomem: return NULL ; }
copy_mm 复制内存管理结构体 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 static int copy_mm (unsigned long clone_flags, struct task_struct *tsk) { struct mm_struct *mm , *oldmm ; tsk->min_flt = tsk->maj_flt = 0 ; tsk->nvcsw = tsk->nivcsw = 0 ; tsk->mm = NULL ; tsk->active_mm = NULL ; oldmm = current->mm; if (!oldmm) return 0 ; if (clone_flags & CLONE_VM) { mmget(oldmm); mm = oldmm; } else { mm = dup_mm(tsk, current->mm); if (!mm) return -ENOMEM; } tsk->mm = mm; tsk->active_mm = mm; return 0 ; }
pidfd_prepare 分配一个新的 pidfd_file 并保留一个 pidfd 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 int pidfd_prepare (struct pid *pid, unsigned int flags, struct file **ret_file) { struct file *pidfs_file ; if (!(flags & PIDFD_STALE)) { guard(spinlock_irq)(&pid->wait_pidfd.lock); if (!pid_has_task(pid, PIDTYPE_PID)) return -ESRCH; if (!(flags & PIDFD_THREAD) && !pid_has_task(pid, PIDTYPE_TGID)) return -ENOENT; } CLASS(get_unused_fd, pidfd)(O_CLOEXEC); if (pidfd < 0 ) return pidfd; pidfs_file = pidfs_alloc_file(pid, flags | O_RDWR); if (IS_ERR(pidfs_file)) return PTR_ERR(pidfs_file); *ret_file = pidfs_file; return take_fd(pidfd); }
copy_oom_score_adj 复制 OOM(Out of Memory)评分调整 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 static void copy_oom_score_adj (u64 clone_flags, struct task_struct *tsk) { if (!tsk->mm) return ; if ((clone_flags & (CLONE_VM | CLONE_THREAD | CLONE_VFORK)) != CLONE_VM) return ; mutex_lock(&oom_adj_mutex); set_bit(MMF_MULTIPROCESS, &tsk->mm->flags); tsk->signal->oom_score_adj = current->signal->oom_score_adj; tsk->signal->oom_score_adj_min = current->signal->oom_score_adj_min; mutex_unlock(&oom_adj_mutex); }
copy_process 创建一个新进程作为旧进程的副本 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 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 __latent_entropy struct task_struct *copy_process ( struct pid *pid, int trace, int node, struct kernel_clone_args *args) { int pidfd = -1 , retval; struct task_struct *p ; struct multiprocess_signals delayed ; struct file *pidfile = NULL ; const u64 clone_flags = args->flags; struct nsproxy *nsp = current->nsproxy; if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS)) return ERR_PTR(-EINVAL); if ((clone_flags & (CLONE_NEWUSER|CLONE_FS)) == (CLONE_NEWUSER|CLONE_FS)) return ERR_PTR(-EINVAL); if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND)) return ERR_PTR(-EINVAL); if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM)) return ERR_PTR(-EINVAL); if ((clone_flags & CLONE_PARENT) && current->signal->flags & SIGNAL_UNKILLABLE) return ERR_PTR(-EINVAL); if (clone_flags & CLONE_THREAD) { if ((clone_flags & (CLONE_NEWUSER | CLONE_NEWPID)) || (task_active_pid_ns(current) != nsp->pid_ns_for_children)) return ERR_PTR(-EINVAL); } if (clone_flags & CLONE_PIDFD) { if (clone_flags & CLONE_DETACHED) return ERR_PTR(-EINVAL); } sigemptyset(&delayed.signal); INIT_HLIST_NODE(&delayed.node); spin_lock_irq(¤t->sighand->siglock); if (!(clone_flags & CLONE_THREAD)) hlist_add_head(&delayed.node, ¤t->signal->multiprocess); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); retval = -ERESTARTNOINTR; if (task_sigpending(current)) goto fork_out; retval = -ENOMEM; p = dup_task_struct(current, node); if (!p) goto fork_out; p->flags &= ~PF_KTHREAD; if (args->kthread) p->flags |= PF_KTHREAD; if (args->user_worker) { p->flags |= PF_USER_WORKER; siginitsetinv(&p->blocked, sigmask(SIGKILL)|sigmask(SIGSTOP)); } if (args->io_thread) p->flags |= PF_IO_WORKER; if (args->name) strscpy_pad(p->comm, args->name, sizeof (p->comm)); p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? args->child_tid : NULL ; p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? args->child_tid : NULL ; rt_mutex_init_task(p); lockdep_assert_irqs_enabled(); retval = copy_creds(p, clone_flags); if (retval < 0 ) goto bad_fork_free; retval = -EAGAIN; if (is_rlimit_overlimit(task_ucounts(p), UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC))) { if (p->real_cred->user != INIT_USER && !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) goto bad_fork_cleanup_count; } current->flags &= ~PF_NPROC_EXCEEDED; retval = -EAGAIN; if (data_race(nr_threads >= max_threads)) goto bad_fork_cleanup_count; p->flags &= ~(PF_SUPERPRIV | PF_WQ_WORKER | PF_IDLE | PF_NO_SETAFFINITY); p->flags |= PF_FORKNOEXEC; INIT_LIST_HEAD(&p->children); INIT_LIST_HEAD(&p->sibling); rcu_copy_process(p); p->vfork_done = NULL ; spin_lock_init(&p->alloc_lock); init_sigpending(&p->pending); p->utime = p->stime = p->gtime = 0 ; prev_cputime_init(&p->prev_cputime); p->default_timer_slack_ns = current->timer_slack_ns; p->pagefault_disabled = 0 ; retval = sched_fork(clone_flags, p); if (retval) goto bad_fork_cleanup_policy; retval = copy_files(clone_flags, p, args->no_files); if (retval) goto bad_fork_cleanup_semundo; retval = copy_fs(clone_flags, p); if (retval) goto bad_fork_cleanup_files; retval = copy_sighand(clone_flags, p); if (retval) goto bad_fork_cleanup_fs; retval = copy_signal(clone_flags, p); if (retval) goto bad_fork_cleanup_sighand; retval = copy_mm(clone_flags, p); if (retval) goto bad_fork_cleanup_signal; retval = copy_namespaces(clone_flags, p); if (retval) goto bad_fork_cleanup_mm; retval = copy_io(clone_flags, p); if (retval) goto bad_fork_cleanup_namespaces; retval = copy_thread(p, args); if (retval) goto bad_fork_cleanup_io; if (pid != &init_struct_pid) { pid = alloc_pid(p->nsproxy->pid_ns_for_children, args->set_tid, args->set_tid_size); if (IS_ERR(pid)) { retval = PTR_ERR(pid); goto bad_fork_cleanup_thread; } } if (clone_flags & CLONE_PIDFD) { int flags = (clone_flags & CLONE_THREAD) ? PIDFD_THREAD : 0 ; retval = pidfd_prepare(pid, flags | PIDFD_STALE, &pidfile); if (retval < 0 ) goto bad_fork_free_pid; pidfd = retval; retval = put_user(pidfd, args->pidfd); if (retval) goto bad_fork_put_pidfd; } #ifdef CONFIG_BLOCK p->plug = NULL ; #endif if ((clone_flags & (CLONE_VM|CLONE_VFORK)) == CLONE_VM) sas_ss_reset(p); clear_task_syscall_work(p, SYSCALL_TRACE); p->pid = pid_nr(pid); if (clone_flags & CLONE_THREAD) { p->group_leader = current->group_leader; p->tgid = current->tgid; } else { p->group_leader = p; p->tgid = p->pid; } p->nr_dirtied = 0 ; p->nr_dirtied_pause = 128 >> (PAGE_SHIFT - 10 ); p->dirty_paused_when = 0 ; p->pdeath_signal = 0 ; p->task_works = NULL ; retval = sched_cgroup_fork(p, args); if (retval) goto bad_fork_cancel_cgroup; if (need_futex_hash_allocate_default(clone_flags)) { retval = futex_hash_allocate_default(); if (retval) goto bad_fork_core_free; } p->start_time = ktime_get_ns(); p->start_boottime = ktime_get_boottime_ns(); write_lock_irq(&tasklist_lock); if (clone_flags & (CLONE_PARENT|CLONE_THREAD)) { p->real_parent = current->real_parent; p->parent_exec_id = current->parent_exec_id; if (clone_flags & CLONE_THREAD) p->exit_signal = -1 ; else p->exit_signal = current->group_leader->exit_signal; } else { p->real_parent = current; p->parent_exec_id = current->self_exec_id; p->exit_signal = args->exit_signal; } spin_lock(¤t->sighand->siglock); rseq_fork(p, clone_flags); if (unlikely(!(ns_of_pid(pid)->pid_allocated & PIDNS_ADDING))) { retval = -ENOMEM; goto bad_fork_core_free; } if (fatal_signal_pending(current)) { retval = -EINTR; goto bad_fork_core_free; } copy_seccomp(p); init_task_pid_links(p); if (likely(p->pid)) { ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace); init_task_pid(p, PIDTYPE_PID, pid); if (thread_group_leader(p)) { init_task_pid(p, PIDTYPE_TGID, pid); init_task_pid(p, PIDTYPE_PGID, task_pgrp(current)); init_task_pid(p, PIDTYPE_SID, task_session(current)); if (is_child_reaper(pid)) { ns_of_pid(pid)->child_reaper = p; p->signal->flags |= SIGNAL_UNKILLABLE; } p->signal->shared_pending.signal = delayed.signal; p->signal->tty = tty_kref_get(current->signal->tty); p->signal->has_child_subreaper = p->real_parent->signal->has_child_subreaper || p->real_parent->signal->is_child_subreaper; list_add_tail(&p->sibling, &p->real_parent->children); list_add_tail_rcu(&p->tasks, &init_task.tasks); attach_pid(p, PIDTYPE_TGID); attach_pid(p, PIDTYPE_PGID); attach_pid(p, PIDTYPE_SID); __this_cpu_inc(process_counts); } else { current->signal->nr_threads++; current->signal->quick_threads++; atomic_inc (¤t->signal->live); refcount_inc(¤t->signal->sigcnt); task_join_group_stop(p); list_add_tail_rcu(&p->thread_node, &p->signal->thread_head); } attach_pid(p, PIDTYPE_PID); nr_threads++; } total_forks++; hlist_del_init(&delayed.node); spin_unlock(¤t->sighand->siglock); write_unlock_irq(&tasklist_lock); if (pidfile) */ fd_install(pidfd, pidfile); copy_oom_score_adj(clone_flags, p); return p; bad_fork_core_free: sched_core_free(p); spin_unlock(¤t->sighand->siglock); write_unlock_irq(&tasklist_lock); bad_fork_cancel_cgroup: cgroup_cancel_fork(p, args); bad_fork_put_pidfd: if (clone_flags & CLONE_PIDFD) { fput(pidfile); put_unused_fd(pidfd); } bad_fork_free_pid: if (pid != &init_struct_pid) free_pid(pid); bad_fork_cleanup_thread: exit_thread(p); bad_fork_cleanup_io: if (p->io_context) exit_io_context(p); bad_fork_cleanup_namespaces: exit_task_namespaces(p); bad_fork_cleanup_mm: if (p->mm) { mm_clear_owner(p->mm, p); mmput(p->mm); } bad_fork_cleanup_signal: if (!(clone_flags & CLONE_THREAD)) free_signal_struct(p->signal); bad_fork_cleanup_sighand: __cleanup_sighand(p->sighand); bad_fork_cleanup_fs: exit_fs(p); bad_fork_cleanup_files: exit_files(p); bad_fork_cleanup_semundo: exit_sem(p); bad_fork_cleanup_security: security_task_free(p); bad_fork_cleanup_audit: audit_free(p); bad_fork_cleanup_perf: perf_event_free_task(p); bad_fork_sched_cancel_fork: sched_cancel_fork(p); bad_fork_cleanup_policy: lockdep_free_task(p); bad_fork_cleanup_delayacct: delayacct_tsk_free(p); bad_fork_cleanup_count: dec_rlimit_ucounts(task_ucounts(p), UCOUNT_RLIMIT_NPROC, 1 ); exit_creds(p); bad_fork_free: WRITE_ONCE(p->__state, TASK_DEAD); exit_task_stack_account(p); put_task_stack(p); delayed_free_task(p); fork_out: spin_lock_irq(¤t->sighand->siglock); hlist_del_init(&delayed.node); spin_unlock_irq(¤t->sighand->siglock); return ERR_PTR(retval); }
kernel_clone 进程复制函数,用于实现进程或线程的创建
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 106 107 pid_t kernel_clone (struct kernel_clone_args *args) { u64 clone_flags = args->flags; struct completion vfork ; struct pid *pid ; struct task_struct *p ; int trace = 0 ; pid_t nr; if ((clone_flags & CLONE_PIDFD) && (clone_flags & CLONE_PARENT_SETTID) && (args->pidfd == args->parent_tid)) return -EINVAL; if (!(clone_flags & CLONE_UNTRACED)) { if (clone_flags & CLONE_VFORK) trace = PTRACE_EVENT_VFORK; else if (args->exit_signal != SIGCHLD) trace = PTRACE_EVENT_CLONE; else trace = PTRACE_EVENT_FORK; if (likely(!ptrace_event_enabled(current, trace))) trace = 0 ; } p = copy_process(NULL , trace, NUMA_NO_NODE, args); add_latent_entropy(); if (IS_ERR(p)) return PTR_ERR(p); trace_sched_process_fork(current, p); pid = get_task_pid(p, PIDTYPE_PID); nr = pid_vnr(pid); if (clone_flags & CLONE_PARENT_SETTID) put_user(nr, args->parent_tid); if (clone_flags & CLONE_VFORK) { p->vfork_done = &vfork; init_completion(&vfork); get_task_struct(p); } if (IS_ENABLED(CONFIG_LRU_GEN_WALKS_MMU) && !(clone_flags & CLONE_VM)) { task_lock(p); lru_gen_add_mm(p->mm); task_unlock(p); } wake_up_new_task(p); if (unlikely(trace)) ptrace_event_pid(trace, pid); if (clone_flags & CLONE_VFORK) { if (!wait_for_vfork_done(p, &vfork)) ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid); } put_pid(pid); return nr; }
kernel_thread 创建内核线程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 pid_t kernel_thread (int (*fn)(void *), void *arg, const char *name, unsigned long flags) { struct kernel_clone_args args = { .flags = ((lower_32_bits(flags) | CLONE_VM | CLONE_UNTRACED) & ~CSIGNAL), .exit_signal = (lower_32_bits(flags) & CSIGNAL), .fn = fn, .fn_arg = arg, .name = name, .kthread = 1 , }; return kernel_clone(&args); }
user_mode_thread 创建用户模式线程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 pid_t user_mode_thread (int (*fn)(void *), void *arg, unsigned long flags) { struct kernel_clone_args args = { .flags = ((lower_32_bits(flags) | CLONE_VM | CLONE_UNTRACED) & ~CSIGNAL), .exit_signal = (lower_32_bits(flags) & CSIGNAL), .fn = fn, .fn_arg = arg, }; return kernel_clone(&args); }
__mmdrop 释放内存管理结构 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 #define free_mm(mm) (kmem_cache_free(mm_cachep, (mm))) void __mmdrop(struct mm_struct *mm){ BUG_ON(mm == &init_mm); WARN_ON_ONCE(mm == current->mm); WARN_ON_ONCE(mm == current->active_mm); free_mm(mm); } EXPORT_SYMBOL_GPL(__mmdrop);
put_task_stack 释放任务栈 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 static void thread_stack_free_rcu (struct rcu_head *rh) { __free_pages(virt_to_page(rh), THREAD_SIZE_ORDER); } static void thread_stack_delayed_free (struct task_struct *tsk) { struct rcu_head *rh = tsk->stack ; call_rcu(rh, thread_stack_free_rcu); } static void free_thread_stack (struct task_struct *tsk) { thread_stack_delayed_free(tsk); tsk->stack = NULL ; } static void release_task_stack (struct task_struct *tsk) { if (WARN_ON(READ_ONCE(tsk->__state) != TASK_DEAD)) return ; free_thread_stack(tsk); } #ifdef CONFIG_THREAD_INFO_IN_TASK void put_task_stack (struct task_struct *tsk) { if (refcount_dec_and_test(&tsk->stack_refcount)) release_task_stack(tsk); } #endif
__put_task_struct 释放任务结构 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 void __put_task_struct_rcu_cb(struct rcu_head *rhp){ struct task_struct *task = container_of(rhp, struct task_struct, rcu); __put_task_struct(task); } EXPORT_SYMBOL_GPL(__put_task_struct_rcu_cb); void __put_task_struct(struct task_struct *tsk){ WARN_ON(!tsk->exit_state); WARN_ON(refcount_read(&tsk->usage)); WARN_ON(tsk == current); sched_ext_free(tsk); io_uring_free(tsk); cgroup_free(tsk); task_numa_free(tsk, true ); security_task_free(tsk); exit_creds(tsk); delayacct_tsk_free(tsk); put_signal_struct(tsk->signal); sched_core_free(tsk); free_task(tsk); } EXPORT_SYMBOL_GPL(__put_task_struct);
exit_mm_release 释放内存管理结构 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 static void complete_vfork_done (struct task_struct *tsk) { struct completion *vfork ; task_lock(tsk); vfork = tsk->vfork_done; if (likely(vfork)) { tsk->vfork_done = NULL ; complete(vfork); } task_unlock(tsk); } static void mm_release (struct task_struct *tsk, struct mm_struct *mm) { uprobe_free_utask(tsk); deactivate_mm(tsk, mm); if (tsk->clear_child_tid) { if (atomic_read (&mm->mm_users) > 1 ) { put_user(0 , tsk->clear_child_tid); do_futex(tsk->clear_child_tid, FUTEX_WAKE, 1 , NULL , NULL , 0 , 0 ); } tsk->clear_child_tid = NULL ; } if (tsk->vfork_done) complete_vfork_done(tsk); } void exit_mm_release (struct task_struct *tsk, struct mm_struct *mm) { mm_release(tsk, mm); }
ksys_unshare 实现进程上下文的分离
ksys_unshare 是一个内核函数,它允许一个正在运行的、活动的进程(由 current 指针表示)“解除共享”或分离其部分执行上下文。这些上下文原本是在进程创建时通过 clone 系统调用与父进程或同组进程共享的。此函数的核心原理是为当前进程创建并切换到新的、独立的资源实例(如命名空间、文件系统属性等),从而实现与原有环境的隔离。
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 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 int ksys_unshare (unsigned long unshare_flags) { struct fs_struct *fs , *new_fs = NULL ; struct files_struct *new_fd = NULL ; struct cred *new_cred = NULL ; struct nsproxy *new_nsproxy = NULL ; int do_sysvsem = 0 ; int err; if (unshare_flags & CLONE_NEWUSER) unshare_flags |= CLONE_THREAD | CLONE_FS; if (unshare_flags & CLONE_VM) unshare_flags |= CLONE_SIGHAND; if (unshare_flags & CLONE_SIGHAND) unshare_flags |= CLONE_THREAD; if (unshare_flags & CLONE_NEWNS) unshare_flags |= CLONE_FS; err = check_unshare_flags(unshare_flags); if (err) goto bad_unshare_out; if (unshare_flags & (CLONE_NEWIPC|CLONE_SYSVSEM)) do_sysvsem = 1 ; err = unshare_fs(unshare_flags, &new_fs); if (err) goto bad_unshare_out; err = unshare_fd(unshare_flags, &new_fd); if (err) goto bad_unshare_cleanup_fs; err = unshare_userns(unshare_flags, &new_cred); if (err) goto bad_unshare_cleanup_fd; err = unshare_nsproxy_namespaces(unshare_flags, &new_nsproxy, new_cred, new_fs); if (err) goto bad_unshare_cleanup_cred; if (new_cred) { err = set_cred_ucounts(new_cred); if (err) goto bad_unshare_cleanup_cred; } if (new_fs || new_fd || do_sysvsem || new_cred || new_nsproxy) { if (do_sysvsem) { exit_sem(current); } if (unshare_flags & CLONE_NEWIPC) { exit_shm(current); shm_init_task(current); } if (new_nsproxy) switch_task_namespaces(current, new_nsproxy); task_lock(current); if (new_fs) { fs = current->fs; spin_lock(&fs->lock); current->fs = new_fs; if (--fs->users) new_fs = NULL ; else new_fs = fs; spin_unlock(&fs->lock); } if (new_fd) swap(current->files, new_fd); task_unlock(current); if (new_cred) { commit_creds(new_cred); new_cred = NULL ; } } perf_event_namespaces(current); bad_unshare_cleanup_cred: if (new_cred) put_cred(new_cred); bad_unshare_cleanup_fd: if (new_fd) put_files_struct(new_fd); bad_unshare_cleanup_fs: if (new_fs) free_fs_struct(new_fs); bad_unshare_out: return err; }
/proc/sys/kernel/threads-max 配置接口:init_fork_sysctl 与 sysctl_max_threads 本代码片段展示了 Linux 内核中用于配置系统范围内最大线程数 的 sysctl 接口的实现。其核心功能是:在 /proc/sys/kernel/ 目录下创建一个名为 threads-max 的文件,允许系统管理员在运行时读取和修改 内核的 max_threads 全局变量。它通过一个自定义的处理函数 sysctl_max_threads 来实现对写入值的安全验证,确保只有合法的值才会被应用到系统中。
实现原理分析 此代码是 sysctl 机制中一个典型的、使用自定义处理函数 来实现安全参数配置的范例。其设计的精髓在于将验证逻辑与最终的写操作分离开,以保证内核全局变量的健壮性。
Sysctl 注册 (init_fork_sysctl) :
init_fork_sysctl 函数在内核启动过程中被 subsys_initcall 机制调用。
它调用 register_sysctl_init("kernel", fork_sysctl_table),将 fork_sysctl_table 中定义的接口注册到 /proc/sys/kernel/ 目录下。
自定义处理函数模式 (sysctl_max_threads) :
在 fork_sysctl_table 的定义中,.data 成员被显式地设置为 NULL,而 .proc_handler 指向了 sysctl_max_threads。这告诉 sysctl 框架,所有对该文件的读写操作都必须由这个自定义函数来全权处理。
核心技巧:本地副本与验证后更新 : sysctl_max_threads 函数的实现模式是内核中安全更新全局变量的最佳实践: a. 它首先在自己的栈上创建了一个临时的 ctl_table 结构 t,以及一个临时的 整型变量 threads,并用全局 max_threads 的当前值来初始化它。 b. 然后,它修改这个临时表 t,使其 .data 指针指向本地变量 threads ,并设置好 .extra1 和 .extra2 来定义允许的范围(1 到 MAX_THREADS)。 c. 接着,它调用标准的内核辅助函数 proc_dointvec_minmax,并将这个临时的、指向本地变量的 表 t 传给它。proc_dointvec_minmax 会处理所有与用户空间缓冲区的交互:如果是读操作,它会从本地 threads 读取;如果是写操作,它会验证用户输入的值是否在 min/max 范围内,如果合法,则将值写入本地 threads 变量 。 d. 最后,也是最关键的一步:if (ret || !write) 检查操作结果。只有在 proc_dointvec_minmax 成功返回(ret == 0)并且 这是一次写操作(write 为真)时,代码才会执行 max_threads = threads;。
目的 : 这个模式确保了全局变量 max_threads 只有在所有检查(包括范围验证)都通过之后,才会被一次性地、原子地(指单个 C 赋值语句)更新 。这完全避免了在验证过程中可能出现的竞态条件或将无效值写入全局变量的风险。
代码分析 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 static int sysctl_max_threads (const struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table t ; int ret; int threads = max_threads; int min = 1 ; int max = MAX_THREADS; t = *table; t.data = &threads; t.extra1 = &min; t.extra2 = &max; ret = proc_dointvec_minmax(&t, write, buffer, lenp, ppos); if (ret || !write) return ret; max_threads = threads; return 0 ; } static const struct ctl_table fork_sysctl_table [] = { { .procname = "threads-max" , .data = NULL , .maxlen = sizeof (int ), .mode = 0644 , .proc_handler = sysctl_max_threads, }, }; static int __init init_fork_sysctl (void ) { register_sysctl_init("kernel" , fork_sysctl_table); return 0 ; } subsys_initcall(init_fork_sysctl);