高级操作系统——xv6进程线程代码阅读

时间:2024-03-20 15:06:42

源代码阅读:
type.h:用于声明一些数据类型的简化名称,和声明页表指针的数据类型。
param.h:主要用于声明基本的一些常量,包括内核栈大小等。
memlayout.h:主要用于声明一些和内存与地址相关的常量与方法,包括虚拟内存转物理内存的方法以及物理内存转虚拟内存的方法等。
defs.h:声明在之后文件中要用到的函数。
x86.h:让c代码使用特殊的x86汇编的一些函数包括outb等,并声明trapframe。
asm.h:一些汇编上的宏定义
mmu.h:定义x86内存管理单元,包括控制寄存器CR上的不同位代表的含义。
elf.hELF:可执行文件的格式,包括ELF头的数据结构等。
vm.c:一些cpu虚拟内存管理的函数
proc.h:声明了CPU在内核中的数据结构表示struct cpu,进程在内核中的数据结构struct proc,上下文在内核中的数据结构struct context。
swtch.S声明用于上下文切换的swtch函数,汇编语言生成
kalloc.c声明一些物理内存分配的函数,包括分配的kalloc()和用于free的kfree()。

Proc.c:
Ptable:进程索引表
Nextpid:进程的ID
Allocproc:分配进程的内核栈和一些寄存器,将进程状态UNUSED改写为EMBRYO
Userinit:第一个进程的初始化操作
Growproc:增加或者减少所占用的内存空间
Fork():创建进程
Exit():退出当前进程,并将其父进程唤醒
Wait();等待子进程退出的
scheduler():内核运行时用于进程调度的循环
sched():切换到内核的
yield():放弃cpu占用
sleep();换成SLEEPING状态,并通过chan标志进程
wakeup();唤醒chan上所有的进程
kill():杀死进程
procdump;向控制台上打印当前进程状态的

(一) 什么是进程,什么是线程?操作系统的资源分配单位和调度单位分别是什么?XV6 中的
进程和线程分别是什么,都实现了吗?
答案:
(1)什么是进程:
1:操作系统由单道处理转向多道处理,传统的静态程序已经不能描述执行的并发性,所以由动态的进程对多道并发进行描述。
2:进程是程序,数据,程序控制块的集合
(2)什么是线程:
(3):进程占用空间多,切换时空消耗大。而为了降低时空消耗,引入了线程
引入线程前:资源分配单位和调度为进程
引入线程后:资源分配的单位是进程,调度的单位是线程
(4)XV6中只实现了进程proc,具体内容在proc.c中

(二) 进程管理的数据结构是什么?在 Windows,Linux,XV6 中分别叫什么名字?其中包含哪些内容?操作系统是如何进行管理进程管理数据结构的?它们是如何初始化的?
(1):进程管理的数据结构是进程控制块。
(2):Linux:task_struct
Windows:EPROCESS、KPROCESS、PEB
XV6:proc .h,proc.c
(3):包含 进程描述信息,进程控制信息,所拥有的资源和使用情况,CPU现场信息
1:Linux下为/include/linux/sched.h内部的struct task_struct,其中包括管理进程所需的各种信息。创建一个新进程时,系统在内存中申请一个空的task_struct ,并填入所需信息。同时将指向该结构的指针填入到task[]数组中。
2:Windows下拥有两个相关的对象,EPROCESS(执行体进程对象) KPROCESS(内核进程对象 )其中KPROCESS又称为PCB
EPEOCESS位于内核层之上它侧重于提供各种管理策略,同时为上层应用程序提供基本的功能接口。所以,在执行体层的进程和线程数据结构中,有些成员直接对应于上层应用程序中所看到的功能实体。
KPROCESS结构体位于内核层,存储着进程的必要信息

3:XV6下在proc .h内声明,包括进程 ID ,进程状态 ,父进程,context,cpu记录了内存地址和栈指针等
xv6的PCB就是proc.h中的proc结构体。其内容包括:
uint sz:进程的内存空间大小。
pde_t* pgdir:进程的页表。
char *kstack:进程的内核栈指针
enum procstate state:进程的状态(包括六种UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE)
int pid:进程的pid
struct proc *parent:进程的父进程指针。
struct trapframe *tf:位于x86.h,是中断进程后,需要恢复进程继续执行所保存的寄存器内容。
struct context *context:切换进程所需要保存的进程状态。切换进程需要维护的寄存器内容,定义在proc.h中。
void *chan:不为0时,保存该进程睡眠时的队列。
int killed:不为0时,则该进程被杀死。
struct file *ofile[NOFILE]:打开文件的组。
struct inode *cwd:进程当前的目录。
char name[16]:进程名。
(4):Xv6通过数据结构ptable对进程进行管理,代码如下:
struct {
struct spinlock lock;
struct proc proc[NPROC];
} ptable;
(5):Xv6的初始化操作:
1:第一个进程初始化:自举程序bootmain.c,调用主方法main.c,调用proc.c中的userinit()建立
2:第一个用户进程(分配了一个物理页,并将这个物理地址页映射到虚拟地址为0处,将原来的加载到物理地址为0处的initcode.S的代码加载到这个虚拟地址对应的物理页中。另外还设置进程的trapframe,以便能从内核态返回到用户态)
3:之后的进程初始化:调用fork()
4:无论是userinit()还是fork()都会调用allocproc()函数
5:allocproc的工作是在页表中分配一个槽(即结构体 struct proc),并初始化进程的状态,为其内核线程的运行做准备。简而言之,即设置好一个特别准备的内核栈和一系列内核寄存器,并将进程状态由UNUSED改写为EMBRYO

(三) 进程有哪些状态?请画出 XV6 的进程状态转化图。在 Linux,XV6 中,进程的状态分别 包括哪些?你认为操作系统的设计者为什么会有这样的设计思路?
(1):进程状态分别有:UNUSED,EMBRYO,RUNNABLE,RUNNING,SLEEPING,ZOMBIE
1:UNUSED:进程的初始化状态(enum枚举特性决定)
2:fork()或者userinit()中调用allocproc()即分配内核栈后改为EMBRYO
3:fork()或者userinit()进行一系列初始化操作后改为RUNNABLE
4:scheduler()会一直执行选择进程改为RUNNING,进程调用yield()改为RUNNABLE, 调用sleep()改为SLEEPING,而wakeup()由SLEEPING改为RUNNABLE
5:程序调用exit()和wait()会将其变为ZOMBIE
高级操作系统——xv6进程线程代码阅读

(2):Linux进程状态:创建,就绪,执行,阻塞,就绪挂起,阻塞挂起,结束高级操作系统——xv6进程线程代码阅读高级操作系统——xv6进程线程代码阅读

(3):提高CPU的利用率,增加并发能力

(四) 如何启动多进程(创建子进程)?如何调度多进程?调度算法有哪些?操作系统为何要 限制一个 CPU 最大支持的进程数?XV6 中的最大进程数是多少?如何执行进程的切换?什么是进程上下文?多进程和多 CPU 有什么关系?

(1):利用操作系统创建或者父进程创建,在xv6中,第一个进程是由Userinit()创建初始化,之后通过fork()创建初始化

(2):利用调度算法进行调度

(3):先来先服务,短作业先服务,时间片轮转发,优先级调度法,多级反馈轮转法,高响应比优先法

(4):一是考虑内存空间,进程数量过多占用大量内存 二是增加了缺页中断的可能性,会导致cpu不断的执行页面换入换出,浪费大量时间

(5):XV6的最大进程数见param.h文件中的#define NPROC 64,最大64;
#define NPROC 64 // maximum number of processes

(6):切换上下文:
1:当前进程通过调用yield()(可能是时间片用完),进行进程切换。yield函数调用sched函数,sched函数启动switch函数完成进程切换,而switch又会触发scheduler(void),scheduler(void)中会选择一个进程切换上下文,仍调用swtch(&cpu->scheduler, proc->context);
2:整个流程是这样的:yield->sched->switch->scheduler->switch
3:其中switch的函数原型void swtch(struct context **old, struct context *new);
4:它的工作主要包括:1. 保存当前(old)进程的上下文。 2. 加载新进程(new)的上下文到机器寄存器中。可以理解为是汇编实现

(7):进程运行时,其硬件状态保存在CPU 上的寄存器中,寄存器:程序计数器、程序状态寄存器、栈指针、通用寄存器、其他控制寄存器的值。进程不运行时,这些寄存器的值保存在PCB 中。将CPU 硬件状态从一个进程换到另一个进程的过程称为
上下文切换

(8):没有必然联系。一个CPU在某一时刻只能运行一个进程,宏观上并行,微观上串行。而多个CPU可以同时运行多个程序

(五) 内核态进程是什么?用户态进程是什么?它们有什么区别?
(1):系统分为内核态和用户态。在内核里产生的进程称为内核级进程,在用户态上产生的进程称为用户态进程。
(2)区别:
1)运行在不同的系统状态,用户态进程执行在用户态,内核态进程执行在内核态
2)进入方式不同,用户态进程可直接进入,内核态必须通过运行系统调用命令
3)返回方式不同,用户态进程直接返回,内核态进程有重新调度过程
4)内核态进程优先级要高于用户态进程,并且内核态进程特权级别最高,它可以执行系统级别的代码
联系:
用户级进程和内核级进程存在一对一,多对一,多对多的关系

(六) 进程在内存中是如何布局的,进程的堆和栈有什么区别?
进程在内存中拥有独立的地址空间,这个空间是逻辑空间。内存分为内核空间和用户空间,内核空间一般运行操作系统程序,而用户空间一般运行用户程序
对于64BIT,通常大小为2^48,分为4级页表,999912分布
对于34BIT,通常大小为2^32,分为2级页表,101012分布
用户空间分为:程序,数据,栈,堆
栈:是存放程序执行时局部变量、函数调用信息、中断现场保留信息的空间堆
堆:程序执行时, 按照程序需要动态分配的内存空间,使用malloc、 calloc、realloc函数分配的空间都在堆上分配。

参考资源:
https://www.jianshu.com/p/e01680c00de4
https://blog.csdn.net/zhanglei8893/article/details/6101076
https://blog.csdn.net/qq_36347375/article/details/91354349
https://www.cnblogs.com/LittleHann/p/3454748.html
https://www.cnblogs.com/itzxy/p/9293677.html