Linux内核进程创建与调度机制详解
Linux内核进程创建机制深度解析从fork到进程调度1. 进程创建概述在Linux操作系统中进程创建是通过fork系统调用实现的。fork系统调用会创建一个与父进程几乎完全相同的子进程包括代码段、数据段、堆栈等内存空间的复制。本文将深入分析Linux内核中fork系统调用的实现机制。2. fork系统调用流程2.1 系统调用入口fork作为系统调用其执行流程遵循Linux系统调用的通用路径。当用户空间程序调用fork()时最终会在内核的sys_call_table中找到对应的系统调用入口sys_fork。SYSCALL_DEFINE0(fork) { return _do_fork(SIGCHLD, 0, 0, NULL, NULL, 0); }SYSCALL_DEFINE0宏用于定义无参数的系统调用这里定义了sys_fork函数它直接调用_do_fork函数完成实际工作。2.2 _do_fork函数框架_do_fork是进程创建的核心函数其原型如下long _do_fork(unsigned long clone_flags, unsigned long stack_start, unsigned long stack_size, int __user *parent_tidptr, int __user *child_tidptr, unsigned long tls) { struct task_struct *p; int trace 0; long nr; p copy_process(clone_flags, stack_start, stack_size, child_tidptr, NULL, trace, tls, NUMA_NO_NODE); if (!IS_ERR(p)) { struct pid *pid; pid get_task_pid(p, PIDTYPE_PID); nr pid_vnr(pid); if (clone_flags CLONE_PARENT_SETTID) put_user(nr, parent_tidptr); wake_up_new_task(p); put_pid(pid); } return nr; }_do_fork主要完成两项关键工作调用copy_process复制进程描述符和所需资源调用wake_up_new_task唤醒新创建的进程3. 进程结构复制机制3.1 copy_process函数框架copy_process是复制进程的核心函数负责创建子进程的task_struct结构并复制父进程资源static __latent_entropy struct task_struct *copy_process( unsigned long clone_flags, unsigned long stack_start, unsigned long stack_size, int __user *child_tidptr, struct pid *pid, int trace, unsigned long tls, int node) { int retval; struct task_struct *p; p dup_task_struct(current, node); // ...其他初始化代码... }3.2 任务结构复制dup_task_struct函数负责复制父进程的task_struct结构调用alloc_task_struct_node分配新的task_struct结构调用alloc_thread_stack_node创建内核栈调用arch_dup_task_struct复制task_struct内容调用setup_thread_stack设置thread_infop dup_task_struct(current, node);这一步骤完成后子进程拥有了与父进程相同的task_struct结构和独立的内核栈空间。3.3 权限凭证复制copy_creds函数负责复制进程的权限凭证retval copy_creds(p, clone_flags);该函数主要工作调用prepare_creds分配新的struct cred结构使用memcpy复制父进程的cred内容设置p-cred和p-real_cred指向新的cred结构3.4 调度相关初始化sched_fork函数初始化进程调度相关的变量retval sched_fork(clone_flags, p);主要工作包括调用__sched_fork初始化sched_entity设置进程状态为TASK_NEW初始化优先级相关变量设置调度类普通进程为fair_sched_class调用task_fork_fair初始化调度实体p-state TASK_NEW; p-sched_class fair_sched_class;3.5 文件系统相关复制copy_files和copy_fs函数负责复制进程的文件描述符和文件系统信息retval copy_files(clone_flags, p); retval copy_fs(clone_flags, p);copy_files通过dup_fd复制files_struct结构包含所有打开的文件描述符。copy_fs通过copy_fs_struct复制fs_struct结构维护进程的根目录和当前目录信息。3.6 信号处理初始化信号相关初始化包括init_sigpending(p-pending); retval copy_sighand(clone_flags, p); retval copy_signal(clone_flags, p);这些函数负责初始化待处理信号队列复制信号处理函数sighand-action分配并初始化signal_struct结构3.7 内存空间复制copy_mm函数处理进程内存空间的复制retval copy_mm(clone_flags, p);该函数通过dup_mm分配新的mm_struct结构并使用dup_mmap复制内存映射区域。对于写时复制(COW)的情况这里可能只设置页表而不实际复制内存。3.8 进程关系建立最后copy_process设置进程ID和进程关系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; } if (clone_flags (CLONE_PARENT|CLONE_THREAD)) { p-real_parent current-real_parent; } else { p-real_parent current; }这部分代码确定新进程的线程组关系、进程组关系以及父子进程关系。4. 新进程唤醒机制4.1 wake_up_new_task函数_do_fork在成功复制进程后调用wake_up_new_task唤醒新进程void wake_up_new_task(struct task_struct *p) { struct rq_flags rf; struct rq *rq; p-state TASK_RUNNING; activate_task(rq, p, ENQUEUE_NOCLOCK); p-on_rq TASK_ON_RQ_QUEUED; check_preempt_curr(rq, p, WF_FORK); }主要步骤设置进程状态为TASK_RUNNING调用activate_task将进程加入运行队列检查是否需要抢占当前进程4.2 进程入队机制activate_task最终调用调度类的enqueue_task方法。对于CFS调度器执行的是enqueue_task_fairstatic void enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) { struct cfs_rq *cfs_rq; struct sched_entity *se p-se; cfs_rq cfs_rq_of(se); enqueue_entity(cfs_rq, se, flags); cfs_rq-h_nr_running; }enqueue_entity函数将调度实体加入到CFS的红黑树中并更新运行统计量。4.3 进程抢占检查check_preempt_curr函数检查新进程是否需要抢占当前进程static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int wake_flags) { struct task_struct *curr rq-curr; struct sched_entity *se curr-se, *pse p-se; if (test_tsk_need_resched(curr)) return; update_curr(cfs_rq_of(se)); if (wakeup_preempt_entity(se, pse) 1) { goto preempt; } return; preempt: resched_curr(rq); }如果设置了sysctl_sched_child_runs_first参数子进程可能会标记父进程需要重新调度从而在系统调用返回时实现子进程先运行。5. 关键数据结构分析5.1 task_struct结构task_struct是Linux内核中最重要的数据结构之一包含进程的所有信息进程状态state调度信息sched_class, se内存管理mm_struct文件系统fs_struct信号处理signal_struct进程关系parent, children, sibling等5.2 进程调度实体sched_entity结构包含进程调度相关的信息struct sched_entity { struct load_weight load; struct rb_node run_node; u64 exec_start; u64 sum_exec_runtime; u64 vruntime; // ... };这些字段用于CFS调度器计算进程的虚拟运行时间决定调度顺序。5.3 内存描述结构mm_struct描述进程的内存地址空间struct mm_struct { struct vm_area_struct *mmap; pgd_t *pgd; atomic_t mm_users; atomic_t mm_count; // ... };包含页表指针、内存区域列表等信息fork时可以选择共享或复制。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2452933.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!