日期:2022-09-30
版本号:V1.0
作者:snow
一、基础概念
1.1、地址映射
1.1.1 分段机制
逻辑地址定义如下
段选择符定义见第二篇1.1节。根据段选择符的定义可以定义8192-1个可用段,每个段对应4GB。同时受限于段寄存器数量,CPU某个时刻只能同时访问六个段。段选择符的偏移即对应GDT或LDT表的偏移,段选择符的TI即对应访问LDT亦或GDT。
从GDT或则LDT中可以获取到当前任务某个段的段基址。
线性地址即对应=段基址+偏移
1.1.2 分页机制
线性地址定义如下:
(1)寄存器CR3:页目录表基址
(2)线性地址高10位:页目录表偏移
(3)线性地址中10位:页表偏移
(4)线性地址低12位:页内偏移
其中,以下内容对应实际物理地址:
页目录表基址=CR3寄存器
页表基址=(1)+(2)
页面基址=页表基址+(3)
线性地址=页面基址+(4)
二、创建进程1前准备
通过fork函数创建进程1
2.1、寄存器压栈
(1)通过软中断(同时传入fork函数编号2)将系统从用户态切换到内核态,同时由硬件将当前任务的5个与任务相关寄存器入栈,这5个寄存器值将会作为进程1的寄存器值模板。
(2)由软中断(系统调用)程序中的fork函数将ds、es、fs、edx、ecx、ebx压栈,这些数据将会用以初始化进程1的tss结构。
2.2、申请空闲进程号&任务号
(1)申请任务号,从任务号1开始与当前进程槽中所有进程的任务号比对,若发现可用,则标记为last_pid
(2)申请空闲进程号,在进程槽中从1开始搜索,若为使用则标记该进程项位置
2.3、寄存器压栈
压入gs、esi、edi、ebp数据,这些寄存器的值也将用来初始化进程1的tss
三、复制进程1信息
调用_copy__process程序拷贝进程1的信息(传入参数为2.2节返回的空闲进程号,此时为1)
3.1、申请空闲页
(1)通过get_free_page申请一个空闲页
(2)将进程1的进程描述指向该空闲页,作为进程1的栈空间。并将进程槽中该进程的进程管理结构指向该空闲页
task[nr]=p
3.2、复制进程描述符
current指向当前进程0的进程描述符,task[nr]指向进程1的进程描述符,通过正常复制即可完成相应拷贝。
3.3、进程1状态锁定
将进程1的状态设置为不可中断等待状态,该状态会一直保持到进程1创建完毕,避免进程1收到任何中断信号的影响。直至其创建完毕后将其改为就绪,才能让进程1可执行。
3.4、进程描述符调整
将进程1的进程描述部分内容进行调整
例如指定任务号为2.2节中空闲任务号
父进程为进程0
优先级
信号清空
执行周期默认
进程执行时间相关参数配置
3.5、初始化任务描述符
(1)将进程0的各项寄存器值拷贝到进程1的任务(状态)描述符中(即进程0上述对应时刻的寄存器状态)(即设置返回环境)
(2)将进程1的任务(状态)描述符中eax置0(即设置返回环境,该值可用以标记进程1返回还是进程0返回)
(3)将进程1的LDT设置为GDT的第1个LDT(任务0占用第0个)
(4)将进程1的trace_bitmap设置为0x80000000(信号位图&协处理器)
3.6、初始化进程1的页面
(1)获取当前进程(进程0)的代码段、数据段的段限长和基址
在进程3.4.1.4节加载了当前进程的LDT位置,此时通过get_limit访问
code_limit=get_limit(0x0f);//获取当前进程第1项LDT,访问权限:用户级
data_limit=get_limit(0x17); //获取当前进程第2项LDT,访问权限:用户级
(2)检查当前进程(进程0)的代码段、数据段成都是否合法(数据段需要不小于代码段)
(3)检查当前进程(进程0)的代码段、数据段的基地址是否一致
(4)定义新的进程1的数据段、代码段基地址(0x64MB偏移)
(5)将进程1的代码默认启动地址指向进程1代码段的基地址
(6)将进程1的数据段、代码段基地址装载到进程1的第1项ldt(代码)、第2项(数据)
(7)申请页目录项,并让进程1的进程管理结构指向该目录
(8)申请页表,并让进程1的页目录的第一项指向该页表
(9)将进程0的第0个页目录的第0个页表的前160个页面地址拷贝给进程1,使进程拥有相同的页面(即相同的数据)。(注:此时进程0通过页面访问物理内存0~64MB,进程1基于基地址64MB可以访问64~128MB,但是进程1的页表实际是指向0~64MB的160个页面)
四、进程1管理结构调整
4.1、文件管理结构初始化
经过复制后,进程0与进程1的进程管理结构基本一致(除了代码段、数据段基址),但是进程1作为子进程,需要有更加丰富功能-文件交互。故还需要针对进程1的文件相关的管理结构进行初始化。
4.2、进程1描述符表挂接
同理3.4.1.1节
将进程1的状态描述符表添加到gdt+(nr<<1)+FIRST_TSS_ENTRY处,也就是第5个描述符
将进程1的局部数据描述符表添加到gdt++(nr<<1)+FIRST_LDT_ENTRY处,也就是第6个描述符。
描述符的挂载意味着进程1创建完毕。
五、切换进程1
5.1、fork返回
(1)将进程1的状态改为就绪态,使其参与调度
(2)返回进程1的进程标号
5.2、进程0返回
进程0判断返回值是否为0,不为0(为进程1的进程标号1),继续进行进程0余下工作。
陷入死循环,并执行pause()函数。该函数主要用以产生软中断,在对应软中断函数中,会将本进程状态切换为可中断等待状态,并执行一次进程调度器。
5.3、进程调度器
(1)在进程调度器器中,会检索当前所有进程,若有信号量释放或者那种时间,则对相应进程信号进行相应处理(使进程状态发生变化,接收参数等)。
(2)会检索当前所有进程,根据调度规则,选择一个进程进行执行。
5.4、进程1返回
进程1从start开始运行,但是由于在3.5节多压入的一个0,所以进程1恢复现场时,会返回。
进程1判断返回值是否为0,为0,继续进行进程1余下工作。