-
-
[原创]Linux0.11写时复制机制详解
-
发表于:
2019-1-14 20:06
5988
-
当执行fork时,子进程除了通过get_free_page申请到一个页面用来存放内核栈和任务数据结构,子进程复制父进程的页表指向父进程空间,父进程空间被设置写保护,当父进程或子进程发生写的操作时,产生一个写保护中断,这时系统会再次申请一个空闲的页面,将原来的页面复制过来并重新映射到进程空间。
如果data_limit小于data_limit或者code_base不等于data_base则报错死机。在linux0.11中代码段基地址和数据段基地址必须是相等的,并且必须满足条件data_limit = code_limit+数据段长度,这一点理解起来有点奇怪。
设置子进程代码段和数据段基地址为进程基地址(64MB对齐)。
接下来就是最重要的步骤复制页表copy_page_tables。这里仅仅是复制父进程的页表,并没有整个复制父进程的进程空间,父子进程共享原来父进程的空间,只有当其中一个进程发生写页面操作的时候才会对要写的页面复制出一个副本对这个副本进行写操作,这样以来可以极大地节约内存空间和减少创建进程时的开销。
mm/memory.c
copy_page_tables接收三个参数,from源地址,也就是我们父进程的基地址;to目的地址,也就是子进程的基地址 = nr × 64MB;size表示要复制多少。
最后刷新cpu页缓冲器保存以上的修改。
对共享空间写会导致写保护异常,cpu就会执行写保护异常处理函数do_wp_page。
un_wp_page作用是解除写保护,他分两种情况处理。第一种情况是未共享页面,也就是页面引用数为1的页面,直接修改页表项属性;第二种是对共享页面处理,方法是申请一个空闲页面设置为可读可写,并把这个新页面映射到原来的线性地址,把共享页面内容复制过来,然后接下来对这个副本进行操作。注意,第二种情况中还要把页面引用数-1,当页面引用数为1时就直接修改页表项不用再复制页面了,算是一个以逸待劳的方法!
所以真正的写时复制是发生在un_wp_page中。
fork的核心是copy_mem,其作用是设置子进程的LDT并对父进程空间进行复制。
kernel/fork.c
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)