do_fork 源码分析

本文详细分析了do_fork函数,阐述了其在进程创建中的关键作用,包括分配进程描述符、处理跟踪标志、创建子进程数据结构等步骤。通过对do_fork的解析,揭示了fork、vfork和clone系统调用的区别,并介绍了如何处理父子进程间的交互和状态控制。
摘要由CSDN通过智能技术生成


 对do_fork的源码进行了分析,看了好多遍,昨晚又看了一遍,现在能够自己顺下来大致执行过程,比之前理解更深刻。

先解释一下ptr_err,这在下面会用到。因为在内核中,有些函数,比如kmalloc是返回指针的,kmalloc是分配内存的,如果分配不到,就返回null指针。有些函数错误时,我们在知道它错了的基础上还要获得错误码,在用户空间,提供了error变量,获得错误码,在内核中就相应的有ptr_err,很明显,也是用来获得错误码的。在do_fork中,copy_process错误时,错误码保存在pid中。

do_fork函数的主要作用就是复制原来的进程使其成为一个新的进程,它完成了整个进程创建中的大部分工作。

Fork、vfork和clone三个系统调用所对应的系统调用服务例程都调用了do_fork()。只是所传递的参数不同,导致父进程和子进程在共享资源的程度上不同。fork是全部复制父进程,传给子进程,所以fork不带参数。而clone复制部分父进程资源,也就是说复制是有选择的。vfork准确来说,它创建的是线程而不是进程。并且vfork创建的子进程先于父进程执行。只有子进程执行结束或退出,父进程才被唤醒。

 

接下来分析do_fork()。

long do_fork(unsigned long clone_flags,
          unsigned long stack_start,
          struct pt_regs *regs,
          unsigned long stack_size,
          int __user *parent_tidptr,
          int __user *child_tidptr)

首先解释以上几个参数,

clone_flags: 通过clone标志可以有选择的对父进程的资源进行复制;fork,vfork和clone就是因为flag标志不同才加以区分的。

statck_start:子进程用户态堆栈的地址;

regs:指向pt_regs结构体的指针。当系统发生系统

下面是 `sched_post_fork` 函数的部分源码,代码摘自 Linux 内核版本 5.15.5: ```c void sched_post_fork(struct task_struct *p) { struct rq_flags rf; struct sched_entity *se = &p->se; struct task_struct *parent = p->parent; struct sched_entity *parent_se = &parent->se; if (parent_se->on_rq && !se->on_rq) { /* * parent is on a runqueue, but we just forked the child and it's * not on any runqueue yet. This means we need to do a few things * to get the child properly accounted for. */ p->se.load.weight = 0; se->vruntime = parent_se->vruntime; se->sum_exec_runtime = parent_se->sum_exec_runtime; se->prev_sum_exec_runtime = parent_se->prev_sum_exec_runtime; se->avg_overlap = parent_se->avg_overlap; /* * We need to add the child to the runqueue. This is tricky, as * we cannot just add it to the parent's runqueue, as that would * mess up the order of the tasks. Instead, we need to add it * to the right runqueue based on its priority. */ raw_spin_lock_irqsave(&rq_lock(p), rf); enqueue_task_rq(p, task_cpu(p), ENQUEUE_WAKEUP); raw_spin_unlock_irqrestore(&rq_lock(p), rf); } /* * We need to reset the child's CPU time and other accounting * information, as it is starting fresh. */ schedstat_set(p->se.statistics.wait_start, 0); cpuacct_clear_stats(p); memset(&p->sched_info, 0, sizeof(p->sched_info)); } ``` 该函数主要做了以下几件事情: 1. 复制父进程的调度实体信息,包括进程的优先级、调度策略、调度参数等; 2. 为子进程创建新的调度实体,并将其加入到任务队列中; 3. 重置子进程的 CPU 时间和其他计算信息,以确保子进程可以从头开始执行。 需要注意的是,`sched_post_fork` 函数只是为子进程更新调度信息和创建调度实体,并将其加入到任务队列中。具体的调度过程和调度策略等信息,是由 CFS 调度器来进行实现和维护的。因此,在理解 `sched_post_fork` 函数的实现时,需要结合 CFS 调度器的内部实现和调度策略等信息进行理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值