MaQueOS的总体架构如图0.1所示,它的主要工作是向下负责管理硬件资源,向上通过系统调用接口为应用程序提供各种服务。本节主要从进程管理、内存管理、文件系统、外设驱动和中断管理这五个方面对MaQueOS的功能进行介绍。
图0.1 MaQueOS的总体架构
在MaQueOS中,每个进程都由一个进程描述符管理,进程描述符对应process数据结构,如代码清单11.3所示。MaQueOS维护了一个用于管理系统中所有进程的进程描述符指针数组——process数组。某一时刻MaQueOS在系统中最多可以运行64个进程,因此process数组有64项。MaQueOS中运行的进程有4个状态:可运行状态(TASK_RUNNING)、可中断挂起状态(TASK_INTERRUPTIBLE)、不可中断挂起状态(TASK_UNINTERRUPTIBLE)和终止状态(TASK_EXIT)。
进程在运行过程中,可能运行在用户态或者内核态下。当进程运行在用户态下时,执行的是进程对应的应用程序的二进制可执行代码;当进程运行在内核态下时,执行的是内核的二进制可执行代码。不同进程运行各自的可执行代码,内核的可执行代码由所有进程共享。
进程通常在用户态下运行应用程序的可执行文件。当产生中断、例外或者主动调用系统调用(一种特殊的例外)后,进入内核态,当中断和例外处理完成后,再返回到用户态下运行。MaQueOS支持的中断详见0.1.5节。
MaQueOS支持基本的进程管理操作,包括进程创建与终止、进程挂起与唤醒以及进程调度。如图0.1所示,为了给应用程序提供基本的进程管理的功能,MaQueOS实现了4个系统调用:fork、exe、exit和pause。
在MaQueOS中,除了第1个进程(进程0)由process_init函数进行初始化,其他所有进程都是通过该进程的父进程调用fork系统调用创建的。之后根据是否加载可执行文件,子进程分为两种情况:第一种情况是,父子进程使用相同的可执行文件,在这种情况下,只需要调用fork系统调用即可;第二种情况是,父子进程使用不同的可执行文件,在这种情况下,子进程还需要调用exe系统调用,加载子进程的可执行文件,同时释放和父进程共享的内容。
子进程在结束运行前,通过调用exit系统调用,将自己的状态设置为终止状态(TASK_EXIT),再向父进程发送终止信号,之后调用进程切换函数。至此,子进程再也不会被调度运行,但是子进程占用的资源仍然没有被释放。当父进程接收到子进程发送的终止信号后,释放子进程占用的系统资源。
为了能够在一个处理器上运行多个进程,MaQueOS将处理器的运行时间划分为以1秒为单位的时间片,并在进程创建和时间片用完时,为进程分配固定数量的时间片。当前进程的时间片用完后,MaQueOS提供的进程调度函数通过遍历process数组找到一个处于可运行状态(TASK_RUNNING)的进程,将其切换到处理器上运行。
进程在运行过程中,会因为某些原因挂起。MaQueOS提供的进程挂起与唤醒机制涉及不可中断挂起唤醒和可中断挂起唤醒两种情况。
例如,在等待子进程终止运行时,父进程调用pause系统调用,通过将自己的状态设置为可中断挂起(TASK_INTERRUPTIBLE)来挂起该进程,当子进程给父进程发送终止信号后,父进程被唤醒,并释放子进程占用的资源。为了获取从键盘输入的字符,进程在用户态下调用input系统调用进入内核态,在input系统调用处理程序中,若检测到用于保存键盘输入字符的队列为空,则通过将进程的状态设置为不可中断挂起(TASK_UNINTERRUPTIBLE)来挂起该进程。当用户按下键盘上的按键后,键盘中断处理程序将等待键盘输入的该进程唤醒,该进程获取字符后,从内核态返回到用户态。
因此,两种进程挂起状态的区别是:处于可中断挂起状态的进程只能被信号唤醒;处于不可中断挂起状态的进程不能被信号唤醒,只能在等待的资源得到满足时才能被唤醒。
MaQueOS仅支持加载运行xt格式的可执行文件。如图9.1所示,xt可执行文件包括两部分:xt可执行文件头和二进制可执行代码。其中,xt可执行文件头的大小为512B,二进制可执行代码的大小必须按4KB对齐。MaQueOS提供了一个脚本,用于将一个使用LoongArch汇编指令编写的汇编应用程序,通过编译链接生成一个可以在MaQueOS上运行的xt格式的可执行文件。如前所述,在进程创建过程中,进程在用户态下通过调用exe系统调用来实现对xt格式的可执行文件的加载。
进程间通信指的是正在运行的进程之间通信的机制。进程间通信的机制有多种,MaQueOS采用的是基于共享内存的进程间通信机制。它的基本原理是:在物理内存中申请一个物理页,将其与需要通信的进程的虚拟页建立映射,并且将虚拟页的访问属性设置为可写,从而通过读写共享物理页达到进程间通信的目的。为了给应用程序提供基于共享内存的进程间通信的功能,MaQueOS实现shmem系统调用(如图0.1所示)。
处理器中的内存管理单元(MMU)将CPU访问的虚拟地址转换为物理地址。LoongArch架构中的MMU支持两种地址转换模式:直接地址翻译模式和映射地址翻译模式。MaQueOS采用映射地址翻译模式进行虚拟地址到物理地址的转换。映射地址翻译模式又分为直接映射地址翻译模式和页表映射地址翻译模式。当MMU处于映射地址翻译模式时,会优先选择直接映射地址翻译模式,若无法应用直接映射地址翻译模式,则选择页表映射地址翻译模式。
MaQueOS在内核态下采用直接映射地址翻译模式,为内核态下的虚拟地址空间0x9000000000000000~0x9000FFFFFFFFFFFF和物理地址空间0x0~0xFFFFFFFFFFFF建立一一映射。例如,在内核态下,CPU访问虚拟地址0x9000000000200000时,MMU将该虚拟地址转换为物理地址0x200000。
MaQueOS在用户态下采用页表映射地址翻译模式。MaQueOS中的页表映射使用二级页表结构。二级页表结构中的页目录(PD)、页表(PT)及物理页(PG)的大小都为4KB。其中,页目录的起始物理地址存放在PGDL寄存器中,页表的起始物理地址存放在页目录项(PDE)中,物理页的起始物理地址存放在页表项(PTE)中。MMU在进行虚拟地址到物理地址的转换时,需要将64位的虚拟地址分为页目录项索引、页表项索引和页内偏移3个部分。其中,页目录项索引用于在页目录中定位页目录项,页表项索引用于在页表中定位页表项,页内偏移用于在物理页中定位虚拟地址转换到的物理地址。
对于物理内存,MaQueOS支持128MB物理内存进行申请和释放。物理内存以页为单位进行申请和释放,页的大小为4KB。因此,128MB的物理内存总共包括128MB÷4KB=32768个物理页。MaQueOS实现了两个接口函数:get_page函数和free_page函数,其中get_page函数用于申请一个空闲物理页,free_page函数用于释放一个物理页。在MaQueOS中,使用一个char型数组mem_map记录所有32768个物理内存页的状态。若mem_map数组中某项的值为0,则表示该项对应的物理页空闲;若值为1,则表示该项对应的物理页被占用。
文件系统的主要作用是组织、管理存放在硬盘上的文件。在使用硬盘存放文件之前,需要将硬盘格式化为某种文件系统格式。MaQueOS目前只支持xtfs文件系统格式。如图8.1所示,在xtfs文件系统中,文件的数据存储在数据块中,每个文件对应1个用于管理文件的inode数据结构,所有inode数据结构存放在0号数据块的inode表中。在xtfs文件系统中,使用存放在1号数据块中的数据块位图表示数据块占用情况,若数据块位图中的比特为1,则表示对应的数据块已被占用;若为0,则表示处于空闲状态。
xtfs文件系统中数据块的大小为512B,因为每个数据块在数据块位图中占用1位,xtfs文件系统共有512×8=4096个数据块,所以在xtfs文件系统中最多可以存放4096×512B=2MB数据。因为inode数据结构的大小为16B,所以xtfs文件系统最多支持512÷16=32个文件。xtfs文件系统只能存放常规文件和可执行文件,不能存放目录文件,所以在xtfs文件系统中查找文件的操作非常简单,不用考虑文件路径的因素。
为了制作根文件系统,MaQueOS提供了两个工具:format和copy。其中,format工具的作用是将一个硬盘镜像文件格式化为xtfs文件系统格式,copy工具的作用是把一个文件复制到xtfs文件系统中。为了给应用程序提供挂载根文件系统的功能,MaQueOS实现了mount系统调用(如图0.1所示)。
MaQueOS支持xtfs文件系统中文件的创建与删除、打开与关闭,以及读写操作。为了给应用程序提供这6个基本的文件操作功能,MaQueOS对应地实现了6个系统调用:create、destroy、open、close、write和read(如图0.1所示)。
为了提供基本的交互功能,MaQueOS实现了键盘和显示器驱动,它们共同组成了一个控制台。其中,显示器驱动程序实现了字符显示和擦除、回车换行、卷屏和删除字符的功能。键盘驱动程序实现了保存用户输入的按键信息,并为进程提供按键信息的功能。为了给应用程序提供从键盘获取字符和向显示器显示字符的功能,MaQueOS分别实现了2个系统调用:input和output(如图0.1所示)。
除了上述的键盘和显示器驱动,MaQueOS还实现了硬盘驱动,提供了读写硬盘数据的接口函数。为了加快访问硬盘数据的速度,MaQueOS在内存中申请了一个用于缓存硬盘数据的内存缓冲区。内存缓冲区由大小为512B的缓冲块组成,缓冲块的大小和硬盘中扇区的大小相同。当需要读写硬盘时,硬盘驱动程序首先会判断需要读写的数据是否在缓冲区中,若在缓冲区中,则直接读写缓冲区中的数据;若不在缓冲区中,则需要先将硬盘中的数据加载到缓冲区,再进行读写操作。因此,为了给应用程序提供将内存缓冲区中的数据写回到硬盘的功能,MaQueOS实现了sync系统调用(如图0.1所示)。
LoongArch架构共支持13个中断,分别为1个核间中断(IPI)、1个定时器中断(TI)、1个性能监测计数溢出中断(PMI)、8个硬中断(HWI0~HWI7)和2个软中断(SWI0~SWI1)。其中,MaQueOS中的时钟中断使用了定时器中断(TI),键盘和硬盘中断使用了硬中断(HWI0)。为了给应用程序提供软件定时器的功能,MaQueOS实现了timer系统调用。