fork源码总结

本文深入探讨了Linux系统中fork()函数的实现原理,揭示了它如何通过调用clone()来创建新进程,详细讲解了从调用dofork()开始,到分配PID、复制进程描述符、初始化子进程上下文,直至子进程运行的全过程。

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

在Linux系统下,fork()的实现是通过调用clone()实现的,这一调用通过不同的参数来指明父子进程之间需要共享的资源。

其中fork、vfork、_clone库函数都是通过系统调用clone(),然后再由clone调用do_fork()实现。

dofork()完成了创建的大部分工作,定义在kernel/fork.c中。

首先定义一个task_struct类型的指针p,每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息,Linux内核的进程控制块是task_struct结构体。他会被装载在RAM中并包含进程信息。他会保存进程的状态信息(即每个进程会将其信息存放在task_struct结构体中)。

通过调用alloc_pidmap()来为子进程分配一个新pid。

检查父进程是否被跟踪,如果为真,就检查debugger程序是否想跟踪子进程,并且子进程不是内核进程(CLONE_ UNTRACED未设置),那么就设置CLONE_PTRACE.CLONE_ UNTRACED防止进程在子进程上强制执行CLONE_PTRACE(继续调试子进程)。

调用copy_process()函数来复制进程描述符。

调用dup_task_struct为子进程创建一个内核栈、thread_info结构和task_struct这些值与当前进程相同。父子进程此时进程描述符完全相同。

alloc_task_struct宏为新进程获取进程描述符。

这里宏替换的是kmem_cache_alloc这个函数,定义在slab.c里,其返回的是__cache_alloc这个函数。

通过alloc_thread_info宏获取一块空闲内存区,存放新进程的thread_info结构和内核栈注意这块内存区字段大小为8KB或4KB。

将父进程的进程描述符复制到tsk所指向的task_struct结构中,并把thread_info置成ti

将父进程的thread_info结构复制到ti中,并将ti_task置成tsk

检查新创建子进程后,用户所拥有的进程数目没有超过可分配数目

 

然后子进程就要和父进程区分开来,这一阶段进程描述符中的很多成员都将被清零或初始化。注意进程描述符的成员不是继承而来,而是统计信息。进程描述符中大多数都是共享的

调用copy_flags,更新tsk_struct结构中的flags成员,表明进程是否拥有超拥护级权限,并表明进程还没有调用exec函数的PF_FORKNOEXEC标志被设置

为新进程获取有效的pid(内核设计第三版中是以getpid的方式获取有效的pid,这里应该是通过查找pidmap图,来获取有效的pid)

 

根据传递给clone的参数标志,copy_proess拷贝或共享打开的文件、系统信息、信号处理函数、进程地址空间和命名空间等,一般情况下,这些信息也会被给定的所有线程共享。否则,这些资源对待每个进程都是不同的,所以被拷贝在这里。

copy_files 拷贝文件描述符

copy_mm 拷贝内存描述符,描述进程地址空间的所以信息。

 

用父进程的现场信息初始化子进程的现场信息,并将子进程的寄存器的值强制改为0,这就是子进程fork返回为0的原因。

调用sched_fork将状态设置为就绪后让父子进程平分剩余的时间片。

最后copy_process返回一个子进程的指针。

回到dofork后,如果copy_process返回成功,那么将新进程唤醒并投入运行。

注意内核有意让子进程优先运行(但事实上并非总是如此),一般子进程都会优先调用exec函数,避免写实拷贝的额外开销。如果父进程优先进行,就会造成地址空间的写入。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值