kernel_clone
在5.0以后的内核中,do_fork被删除,取而代之的是kernel_clone函数。put_pid(pid)将新生成的pid号放入pid_namespace。
kernel_clone(struct kernel_clone_args *args)|--u64 clone_flags = args->flags;| struct completion vfork;| struct task_struct *p;| int trace = 0;|--检查子进程是否允许被跟踪| //复制出一个进程并返回task_struct指针|--p = copy_process(NULL, trace, NUMA_NO_NODE, args);| //获取pid|--pid = get_task_pid(p, PIDTYPE_PID);| //获取虚拟的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);| //将进程加入到就绪队列|--wake_up_new_task(p);|--if (clone_flags & CLONE_VFORK)| //等待子进程调用exec()或exit()| if (!wait_for_vfork_done(p, &vfork))| ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);|--put_pid(pid);//返回pid号给用户空间\--return nr;
copy_process
从当前进程复制出一个进程结构体(只是复制数据,尚未启动),复制之前需要对传入的flag进行判断,要求是:
(1)不允许与不同命名空间的进程共享根目录
(2)线程组也必须共享信号,而且分离的线程只能在线程组内启动。
(3)共享的信号处理程序意味着共享的虚拟机。通过上述方式,线程组也意味着共享虚拟机。屏蔽这种情况可以 在其他代码中进行各种简化。
(4)全局init进程的子进程在退出时仍然是僵尸,因为他们没有被它们的父代(交换器)回收。为了解决这个问题,并避免多根进程树,要防止全局和容器init进程创建子进程。
(5)如果新进程将处于不同的pid或用户命名空间中, 则不允许它与分叉任务共享一个线程组。
(6)如果新进程将处于不同的时间命名空间中, 则不允许它与分叉任务共享VM或线程组。
在正式复制之前还需要对信号量进行处理, 强制在此之前收到的任何信号在复制发生之前传递。 收集发送至多个进程的、发生在复制过程中的信号,并将其延迟,使其看起来发生在复制之后。
io进程的处理:将新的进程标记为一个IO工作者,并阻止任何非致命的信号,死亡或停止的信号。
static __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;u64 clone_flags = args->flags;struct nsproxy *nsp = current->nsproxy;//从当前进程复制出一个子节点p = dup_task_struct(current, node);// 这必须发生在我们调用free_task()之前,即在我们跳转到任何bad_fork_*标签之前。//这是为了避免释放p->set_child_tid,它被(ab)用作内核线程(PF_KTHREAD)的kthread的数据指针。p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? args->child_tid : NULL;//超过资源限制也要退出if (is_ucounts_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_free;}//进程数限制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);cgroup_fork(p);/* Perform scheduler related setup. Assign this task to a CPU. *///将新进程/线程告诉调度器retval = sched_fork(clone_flags, p);shm_init_task(p);retval = security_task_alloc(p, clone_flags);retval = copy_semundo(clone_flags, p);retval = copy_files(clone_flags, p);retval = copy_fs(clone_flags, p);retval = copy_sighand(clone_flags, p);retval = copy_signal(clone_flags, p);//内存结构体retval = copy_mm(clone_flags, p);//命名空间retval = copy_namespaces(clone_flags, p);//ioretval = copy_io(clone_flags, p);//进程中的线程retval = copy_thread(clone_flags, args->stack, args->stack_size, p, args->tls);stackleak_task_init(p);p->pid = pid_nr(pid);if (clone_flags & CLONE_THREAD) {//设置线程的组p->group_leader = current->group_leader;p->tgid = current->tgid;} else {//设置新进程的组核pidp->group_leader = p;p->tgid = p->pid;}//判断cgroup允许复制retval = cgroup_can_fork(p, args);init_task_pid_links(p);if (likely(p->pid)) {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);/** Inherit has_child_subreaper flag under the same* tasklist_lock with adding child to the process tree* for propagate_has_child_subreaper optimization.*/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++;atomic_inc(¤t->signal->live);refcount_inc(¤t->signal->sigcnt);task_join_group_stop(p);list_add_tail_rcu(&p->thread_group,&p->group_leader->thread_group);list_add_tail_rcu(&p->thread_node,&p->signal->thread_head);}attach_pid(p, PIDTYPE_PID);nr_threads++;}total_forks++;proc_fork_connector(p);sched_post_fork(p, args);cgroup_post_fork(p, args);perf_event_fork(p);trace_task_newtask(p, clone_flags);uprobe_copy_process(p, clone_flags);copy_oom_score_adj(clone_flags, p);return p;
}
再回到kernel_clone中,使用wake_up_new_task(p)将新进程p加入调度器中。
----mark:pid_namespace---