linux中内核源码do_fork()的分析

本文详细分析了Linux内核中的do_fork()函数,讲解了进程创建的关键步骤,包括如何通过fork()、clone()调用do_fork(),以及do_fork()如何分配PID、复制进程描述符、初始化子进程状态和数据结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一般进程的创建分为两步:fork() + exec()
    fork()通过拷贝当前进程创建一个子进程。子进程与父进程的区别仅仅在于PID、PPID和某些资源和统计量。
    exec()函数负责读取可执行文件并将其载入地址空间开始运行。

fork(),vfork(),__clone()根据各自需要的参数标志去调用clone()。然后由clone()去调用do_fork()

    当我们要fork()、vfork(),__clone()时,我们要用一个struct pt_regs regs结构保存断点时的信息(即当用户态转到内核态时产生中断),这个结构里放着一系列的寄存器。比如:用 ebx 寄存器保存可执行文件路径的指针,用 ecx 寄存器保存命令行参数的指针,用 edx 寄存器保存环境变量的指针,等等。然后clone()去调用do_fork()struct pt_regs regs结构如下:

struct pt_regs {
	long ebx;
	long ecx;
	long edx;
	long esi;
	long edi;
	long ebp;
	long eax;
	int  xds;
	int  xes;
	long orig_eax;
	long eip;
	int  xcs;
	long eflags;
	long esp;
	int  xss;
};

    然后clone()去调用do_fork()

do_fork()函数的定义如下:

long do_fork(unsigned long clone_flags,
	        unsigned long stack_start,
	        struct pt_regs *regs,
	        unsigned long stack_size,
	        int __user *parent
下面是 `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、付费专栏及课程。

余额充值