Skip to content

Latest commit

 

History

History
16 lines (13 loc) · 2.53 KB

readme.md

File metadata and controls

16 lines (13 loc) · 2.53 KB

血与泪

调试中遇到的问题

  1. loader读取扇区数量已经小于生成的二进制程序的大小了,现在增加到30个扇区,拷贝扇区增加到32个到0x00地址
  2. new_data_base = new_code_base = nr * TASK_SIZE + PG_NUM*4*1024*1024; 新进程的任务基地址要避开物理内存,目前每个进程4M内存空间,一共可以创建10个进程
  3. 由于页目录表的基地址和linux0.12的不同,我用的是0x8000而0.12用的是0x0,所以在移植memory.c中的copy_page_tables的时候要关注相关逻辑,把基地址相关的地址都要加上0x8000,并且invalidate()函数的实现也要改变成这样#define invalidate() __asm__("movl %%eax,%%cr3"::"a" (0x8000))

为什linux中的进程都是以1进程为模版而不是0进程

  • 假设创建的进程以0进程为模版,那么我们要复制的页表的逻辑地址是从0开始的,这里面有页目录表和页表(这里面的头部的逻辑地址和物理地址是相同的)
  • 那么由于写时复制机制,正常情况下要把老进程和新进程的页表都置为只读,这样当有一个进程有对页面有写操作的时候,再开始申请新的物理内存做映射,并把这一页复制到新的物理内存,并解锁该页。
  • 那么看这样的情况,如果刚好0进程先写了内存触发了写时复制机制,能不能分配一页物理内存给0进程然后复制页呢?
  • 不行,0进程的头部页面必须要保证逻辑地址和物理地址是相同的,因为你要给cr3一个物理地址来告诉它页目录表在哪,这个地址必须是物理地址。
  • 起始1号进程是以0号进程为模版创建的,但是为了避免上面所说的锁住0号进程的内存,0号进程在fork的时候不锁自己的页表只锁1号进程的页表。这就造成了这时0号进程不会写时复制,而1号进程会写时复制。那么让1号进程先写一下内存不久好了?答案是不行,因为fork过后0号和1号进程的执行顺序是未知的。所以这里对于fork的调用一定不能用栈,这样0号进程就不会写内存。于是就有了0号进程第一次调用fork是用一段汇编代码直接调用int 0x80
  • 那么问题来了,如果0号进程用了栈,会有什么问题?假设这种场景,0号进程fork了,栈中写入了fork的返回地址,这时cpu调度到了0号进程,0号进程执行了ret指令,把栈修改了,这个修改对1号进程是可见的,当1号进程要执行ret操作的时候栈上的返回地址已经不见了,1号进程就会崩溃。