Xv6内核分析(六)

gewenbin
gewenbin
gewenbin
188
文章
15
评论
2020年12月20日17:08:43 评论 708

main函数之userinit

1. userInit函数

userinit函数做的事就是初始化第一个进程运行所需要的栈中的数据:

Xv6内核分析(六)

initcode.S是第一个进程的用户空间程序,_binary_initcode_start表示这个程序代码在内存中的虚拟地址,_binary_initcode_size表示其大小,由于此段代码是链接到0地址上的,所以需要将代码映射到0虚拟地址上:

Xv6内核分析(六)

这个有点类似于多核的入口代码要映射到0x7000一样。

2. allocproc函数

这个函数的作用就是从进程控制块数组中寻找一个空闲的进程控制块。ptable.proc[64]是一个全局的进程控制块数组,默认有64个,allocproc首先在64个进程控制块中寻找状态为空闲的控制块:

Xv6内核分析(六)

如果找到,接下来对控制块中的成员初始化:

Xv6内核分析(六)

控制块的状态被标记为EMBRYO,然后为本PCB控制块分配一个PID,注意这里的PID是从1开始增加的。

  • 1.2.1 然后为进程分配内核栈,栈地址记录在p->kstack成员中。
  • 1.2.2 接着从栈顶开始为中断帧预留内存空间,并将中断帧起始地址记录在p->tf中。
  • 1.2.3 接着将trapret函数地址记录在栈中。
  • 1.2.4 然后预留context空间,并将context起始地址记录在p->context中,同时将context空间清零。
  • 1.2.5 最后设置context中eip位置的值为forkret的地址。

调用完allocproc后,进程内核栈中的数据分布如下:

Xv6内核分析(六)

不管是第一个进程还是fork出来的进程,都要调用allocproc函数,创建出的新进程的内核栈初始状况都是上图中的那样。

3. 设置进程页表

接着为新进程设置页目录和页表的内核空间部分的映射关系,一个进程的页表中的映射分为两部分,第一部分是表示内核空间的映射关系,另一部分表示用户空间的映射关系。

Xv6内核分析(六)

4. initcode重定位

然后调用inituvm将Initcode的代码拷贝到虚拟地址为0的地方,注意,initcode必须在4KB以内:

Xv6内核分析(六)

  • 分配一页物理内存,并清零。
  • 将物理内存映射到0虚拟地址处,页属性为用户页。
  • 映射完之后,将Initcoe的代码和数据拷贝到0地址上。

5. 设置内核栈

接着设置内核栈中的中断帧区域:

Xv6内核分析(六)

  • 中断帧被设置为了用户模式,用于假装使用用户模式产生中断进来的。
  • 中断帧中的CS和DS、ES、SS都被设置为用户模式。
  • 标志寄存器eflags被设置为允许中断模式。
  • esp栈寄存器表示用户栈的地址,被设置为虚拟地址4KB开始的地方,由此可知,第一个用户进程的代码和用户栈是在同一页中的。
  • eip设置为0,这是第一个进程initcode代码所在的起始地址,在xv6中,所有进程的虚拟起始地址都是在0地址处。

6. 设置当前进程工作目录

接着设置新进程的名字和当前工作目录:

Xv6内核分析(六)

7. 设置进程状态

最后将新进程的状态设置为RUNNABLE状态,注意,当这是第一个进程时,其他从核是一直在寻找可运行进程来运行的,由于userinit是主核运行的,主核设置第一个进程状态为可运行后,会有一个从核运行这第一个进程。

Xv6内核分析(六)

8. 主核调用mpmain

主核最后也调用mpmain,这个之前从核运行的一样。mpmain最后调用的是scheduler(),这是一个内核线程,每个cpu都有的线程。这个线程的功能如下:

Xv6内核分析(六)

gewenbin
  • 本文由 发表于 2020年12月20日17:08:43
  • 转载请务必保留本文链接:http://www.databusworld.cn/9416.html
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: