JOS fork函数 实现机制分析

简直有点小鸡冻哇... 介个地方之前困惑了好一阵...现在叨叨关于fork那些事儿

文章会着重分析fork的两种实现策略:

1. 不使用COW 策略实现dumbfork (很暴力的拷贝)

2. 使用COW技术的fork(写时复制, parent process , child process任意一个进程对共同映射的空间有改动,就发生拷贝动作, 改动了哪页拷贝哪页, 不是全部user space空间的拷贝).

大家都知道, 在Unix类系统里面, 创建一个子进程最常用的就是fork.

而且有个很牛逼轰轰的技术,叫做COW(copy on write) 被应用在这上面.

首先说明一个连接器细节. 每个linker在生成最后的可执行程序的时候,都会在bss段的末尾做个标记 -- end.

可以把这个end当做一个全局变量,是个指针,指向bss段的末尾.(bss本来就是所有段的末尾,那么这个指针指向的可执行程序的末尾..而bss段又几乎不占空间的,于是其实又是指向数据段的末尾的.)

左边的是 obj/kern/kernel.sym的部分截图                          右边的是 obj/user/dumbfork.sym

我们关注end标记就可以了. 每个程序编译完了之后都会在 bss段后面加上 end.

在用户空间程序里面引用的就是用户空间这个end, 而不是内核那个

这是一个很有必要的background.下面我们来分析两种fork 策略

1. dumbfork.c  (我不贴全部的代码,只做重要的理论分析, 全部代码可以去github看, 这样写出来的东西才有意义)

这里sys_exofork仅仅只是为新进程分配了一个新的 env结构体, 用来描述新的子进程.

而子进程的用户空间内存还没有分配.

d

之前我在这里恐慌了好久, 我很狐疑, 为什么这里就敢给子进程的全局变量thisenv赋值呢? 后面可是会duppage把整个parent process的用户空间数据拷贝过去的啊. 不就覆盖了么. 这赋值操作不就白做了么? 我很当时很郁闷(年轻人啊, too young too naive啊...). 要知道这里子进程可还是没有运行的! 所以压根还不会发生thisenv的赋值操作, 子进程还没有运行, 等parent运行快完了, 才会把child
设置成 runable.之后才会运行子进程, 进而进入 if (envid == 0)

再继续看看,究竟怎么copy parent process到child process的.

调用duppage() 把从 UTEXT开始的地址处一直拷贝到end (客官如果忘记的话,往前翻)

其实这里UTEXT ~ end只有不是很大的一段用户空间. (建议自己去cprintf, 把这两个地址打印出来, 然后对照 memlayout.h去看, 瞬间就明白了. 我之前在这里被坑了几天, 各种毁三观)

最后, 我们把用户的可执行程序, 全局变量神马的都拷贝了(for循环里面的duppage).但是我们还没有拷贝栈啊. 栈的地址在 end的上面.

于是就有了 duppage(envid, ROUNDDOWN(& addr, PGSIZE));

这里又恐慌了好久, 因为我没有注意到他传入的是指针addr的地址, 而不是addr指向的地址.我伙呆, 因为这里addr是个函数局部变量, 是在栈上面的. 于是利用这个地址是个栈上地址, 再ROUNDDOWN就找到栈最低地址了,直接duppage. 于是就搞定了user space stack的拷贝. 也就完成了进程的拷贝.

2. lib/fork.c

要看懂这个fork实现一定要明白user space page fault handler机制.这个是N多策略的基础.

传送门:http://blog.csdn.net/cinmyheart/article/details/45271455

看前面和dumbfork还是很相似的,都是调用sys_exofork来获得一个新的struct env.

不同的是后面.究竟是怎么实现COW(copy on write)的呢?

后面两层for循环, 根据页目录也页表对存在的页(PTE_P), 除了异常栈之外统统映射.

而后, 异常栈是两个进程,发生write操作之前, 唯一不共享的内存区域. 后面单独给异常栈映射内存.

还是duppage.

策略超赞. 首先不管原来的页面是否是可以写的(PTE_W or PTE_COW), 都把当前进程的页面以

perm = PTE_U | PTE_P进行映射.

1. 如果有可以PTE_W或PTE_COW,

那么我们都以 perm = PTE_U | PTE_P | PTE_COW进行映射 ,注意不要给PTE_W权限了.

2.如果perm里面存在PTE_COW,那么就以perm = PTE_U | PTE_P | PTE_COW重新映射自身

等duppage完事的时候, 两个进程空间内, 相同的虚拟地址所有的权限都是一样的(还是除开异常栈).

两个进程中, 任意一个进程尝试对页面进行写操作的时候, 都会触发page fault, 因为没有 PTE_W权限.

而这里user space 的page fault handler则会PTE_P | PTE_W | PTE_U的权限重新申请一页物理内存去添加到对应进程中去.

哎, 感觉这么清楚直观的机制, 我之前怎么就死纠结捏.... 折腾好久了这个fork. 今天算是有个交代了~

时间: 2024-10-03 14:51:27

JOS fork函数 实现机制分析的相关文章

Linux fork函数详细图解-同时分析一道腾讯笔试题

原创blog,转载请注明出处 头文件: #include<unistd.h> #include<sys/types.h> 函数原型: pid_t fork( void); (pid_t 是一个宏定义,其实质是int 被定义在#include<sys/types.h>中) 返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID:否则,出错返回-1 注意,子进程是父进程的副本,拷贝父进程的数据空间,堆栈等资源.父子进程不共享上述资源. 每执行一次fork(

linux中fork()函数详解[zz]

转载自:http://www.cnblogs.com/york-hust/archive/2012/11/23/2784534.html 一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事. 一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间.然后把原来的进程的所有值都复制到新的新进程中,只有

fork( )函数详解

 一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程, 也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事. 一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间.然后把原来的进程的所有值都 复制到新的新进程中,只有少数值与原来的进程的值不同.相当于克隆了一个自己. 我们来看一个例子: /* *  fork_test.c *  version 1

linux fork()函数 转载~~~~

转自  ::  http://blog.csdn.net/jason314/article/details/5640969  一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程, 也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事. 一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间.然后把原来的进程的所有值都 复制到新的新进程中,只有少数值与

Linux中的fork()函数

 一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程, 也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事. 一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间.然后把原来的进程的所有值都 复制到新的新进程中,只有少数值与原来的进程的值不同.相当于克隆了一个自己. 我们来看一个例子: /* *  fork_test.c *  version 1

深入浅出--UNIX多进程编程之fork()函数

0前言 上周都在看都在学习unix环境高级编程的第八章--进程控制.也就是这一章中,让我理解了unix中一些进程的原理.下面我就主要按照进程中最重要的三个函数来进行讲解.让大家通过阅读这一篇文章彻底明白进程这点事.希望对大家有所帮助. 1进程环境 在学习进程之前,一定要了解一下unix的进程环境.系统如何对进程终止,和一个程序启动终止,程序运行的原理等,这些都有助于你理解进程的运行原理.这些内容都在我的上一篇文章中,请关注:http://blog.csdn.net/wallwind/articl

linux之fork()函数详解

一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程, 也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事. 一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间.然后把原来的进程的所有值都 复制到新的新进程中,只有少数值与原来的进程的值不同.相当于克隆了一个自己. 我们来看一个例子: [cpp] view plaincopy /* *  fork

C函数篇(fork函数)

一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事.    一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间.然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同.相当于克隆了一个自己. 我们来看一个例子: /* *  fork_test.c *  version 1

fork函数详解

一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事.    一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间.然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同.相当于克隆了一个自己. 我们来看一个例子: /*   *  fork_test.c   *  versi