kernel_clone
用户空间通过 fork()
函数, vfork()
函数, pthread_create()
函数创建进程/线程,内核空间通过 kernel_thread()
函数创建线程,最终都会通过kernel_clone()
函数创建。
pid_t kernel_clone(struct kernel_clone_args *args)
{
struct task_struct *p;
struct pid *pid;
...
// 创建进程的主要函数,包括复制父进程的相关资源;
p = copy_process(NULL, trace, NUMA_NO_NODE, args);
...
// 为子进程分配PID
pid = get_task_pid(p, PIDTYPE_PID);
...
// 将子进程加入就绪队列,等待被调度;
wake_up_new_task(p);
...
}
copy_process
copy_process()
| p = dup_task_struct(current, node); // 创建新的task_struct结构体,然后复制父进程的task_struct到子进程;
| tsk = alloc_task_struct_node(node); // 从kmem_cache中创建task_struct结构体;
| err = arch_dup_task_struct(tsk, orig); // 复制父进程的task_struct到子进程;
| err = alloc_thread_stack_node(tsk, node); // 为task_struct分配内核栈空间;
| clear_tsk_need_resched(tsk); // 清空子进程需要调度的标志位;
| set_task_stack_end_magic(tsk); // 设置栈顶标记,用来检测栈溢出;
| retval = copy_creds(p, clone_flags); // 复制父进程的credentials;
| retval = sched_fork(clone_flags, p);
| __sched_fork(clone_flags, p); // 对task_struct中与调度相关的信息进行初始化;
| p->__state = TASK_NEW; // 设置进程状态,表示新创建的进程;
| p->prio = current->normal_prio; // 设置进程优先级;
| if (rt_prio(p->prio)) p->sched_class = &rt_sched_class;
| else p->sched_class = &fair_sched_class; // 设置进程调度类;
| init_task_preempt_count(p); // 初始化进程preempt_count;
| retval = copy_files(clone_flags, p); // 复制进程打开的文件描述符表;
| retval = copy_fs(clone_flags, p); // 复制进程的文件系统信息;(当前工作目录、根目录、挂载的文件系统等信息)
| retval = copy_sighand(clone_flags, p); // 复制进程的信号处理程序(signal handler)表;
| retval = copy_signal(clone_flags, p); // 复制信号相关的条件,比如rlimit、oom_score_adj;
| retval = copy_mm(clone_flags, p); // 复制进程的内存信息;
| if (clone_flags & CLONE_VM) mm = oldmm; // 如果设置了CLONE_VM,新进程则与当前进程共享mm;
| else mm = dup_mm(tsk, current->mm); // 否则,复制当前进程的mm_struct;(分配一个mm_struct,复制父进程的VMA)
| retval = copy_namespaces(clone_flags, p); // 根据clone时传的标志,来为子进程创建独立的命名空间(mnt/uts/ipc/pid/cgroup/net/time)
| retval = copy_thread(p, args); // 复制进程的CPU体系结构的相关信息;
| pid = alloc_pid(...); // 分配pid结构;
| copy_seccomp(p); // 复制父进程的安全过滤器(seccomp filter);
copy_thread
struct task_struct {
struct thread_info thread_info;
...
void *stack;
...
/* CPU-specific state of this task: */
struct thread_struct thread;
};
/* arm64 */
struct cpu_context {
unsigned long x19;
unsigned long x20;
unsigned long x21;
unsigned long x22;
unsigned long x23;
unsigned long x24;
unsigned long x25;
unsigned long x26;
unsigned long x27;
unsigned long x28;
unsigned long fp;
unsigned long sp;
unsigned long pc;
};
struct thread_struct {
struct cpu_context cpu_context; /* cpu context */
...
};
struct pt_regs {
union {
struct user_pt_regs user_regs;
struct {
u64 regs[31];
u64 sp;
u64 pc;
u64 pstate;
};
};
...
};
cpu_context
:在进程切换时用来保存上一个进程的寄存器的值;thread_struct
:在内核态的两个进程发生切换时,用来保存上一个进程相关寄存器;pt_regs
:当用户态进程发生异常(系统调用、中断等)并进入内核态时,用来保存用户态进程的寄存器状态;
copy_thread()
| memset(&p->thread.cpu_context, 0, sizeof(struct cpu_context));
| p->thread.cpu_context.pc = (unsigned long)ret_from_fork; // 设置新进程的PC指针为ret_from_fork;新进程运行时会从ret_from_fork运行,它是汇编语言编写的;
wake_up_new_task
wake_up_new_task(struct task_struct *p)
| WRITE_ONCE(p->__state, TASK_RUNNING); // 设置进程状态为TASK_RUNNING
| __set_task_cpu(p, select_task_rq(p, task_cpu(p), WF_FORK)); // 选择在哪个CPU上运行;
| activate_task(rq, p, ENQUEUE_NOCLOCK); // 加入该CPU的就绪队列,等待调度;
ret_from_fork
SYM_CODE_START(ret_from_fork)
bl schedule_tail
cbz x19, 1f // not a kernel thread
mov x0, x20
blr x19
1: get_current_task tsk
mov x0, sp
bl asm_exit_to_user_mode
b ret_to_user
SYM_CODE_END(ret_from_fork)
NOKPROBE(ret_from_fork)