系统调用 fork()

用Simics跟踪一条条汇编分析页表映射、寄存器值还真是体力活啊。。

  1. 实现 Copy On Write 时,如果某一个用户态页面有多个进程共享,其中一个进程修改该页面时需要创建一个新的页面。一开始偶忘了把原来页面的内容复制到新的页面了 =_= 另外由于新的页面要代替老的页面,或者说它们的物理地址不同,但虚拟地址相同,我的方法是在内核态开辟一个大小为一个页面的空间作为中转。

  2. do_fork函数中,子进程复制父进程的页表的同时会把父进程页表项置为不可写,注意最后要flush tlb。因为一开始没有flush tlb,导致最后用户态fork返回以后读取的信息来自于tlb,直接改写了共享页面中fork的返回地址,导致切换到子进程时fork的返回地址丢失。这个bug让我郁闷了两三个小时。。

  3. 使用两次fork时,第二次fork返回的pid会被改掉。查了下发现为用户空间分配物理页面的代码里居然在分配好以后没有把对应的struct置为已使用,结果导致第二个子进程COW创建新页面时得到了原来的父进程页面,改写了父进程页面内容。

系统调用 exec()

  1. 清空页表的用户空间映射的函数一开始写得yts,bug到处都是,比如free的时候没使用指向内存块首地址的指针,记录内存地址的变量没有累加。
  2. exec传递给内核态的两个参数必须先在内核态保存一个副本,否则清空用户态页表后就无法得到这两个参数信息了。
  3. 分配给用户态的页面必须先清零,一方面考虑到安全性,另一方面不清零会隐藏一些潜在的bug。一开始我没有在内核执行exec的时候完整的复制所有的参数,而是直接指向了原进程的内存空间,由于清空页表后再次申请新页表时得到了原来的页面,结果正好原来那个保存参数的页面和新进程的该页面重合了 =_= 于是浪费了不少时间在这个bug上