64位操作系统-进程管理
进程管理模块进程是拥有执行资源的最小单位,它为每个程序维护着运行时的各种资源,如进程ID、进程的页表、进程执行现场的寄存器值、进程各个段地址空间分布信息以及进程执行时的维护信息等,它们在程序的运行期间会被经常或实时更新。这些资源被结构化到PCB(Process Control Block,进程控制结构体)内,PCB作为进程调度的决策信息供调度算法使用。
进程调度策略负责将满足运行条件或迫切需要执行的进程到空闲处理器中执行。进程调度策略直接影响程序的执行效率。
PCBPCB用于记录进程的资源使用情况(包括软件资源是硬件资源)和运行状态等。
1234567891011121314151617struct task_struct { struct list list; //连接各个进程控制结构体的双向链表 volatile long state; // 进程状态(运行态/停止态/可中断态)volatile修饰,处理器每次使用这个进程状态前都必须重新读取这个值而不能使用寄存器中的备份值 unsigned long flags; // 进程标志(进程/线程/内核线程) stru ...
64位操作系统-键盘驱动
键盘功能目前,键盘控制器芯片大多数采用Intel 8042芯片,键盘控制器芯片通过PS/2接口或USB接口与外部设备相连。键盘舍内通常会包含一个Intel 8048或兼容芯片,这个芯片会时刻扫描键盘设备的每个按键,并将扫描到的按键进行编码,每个按键的编码是唯一的。8042芯片还负责控制系统的其他功能,如鼠标、A20地址线等。
当8048芯片检测到按键被按下时,它会将按键对应的编码值通过PS/2接口发送到8042键盘控制器芯片中。8042键盘控制器在接收到编码值后,会将其解析并转换成统一键盘扫描码(第1套XT扫描码集)。并存放到输出缓冲区等待处理器读取。如果此时还有新键被按下,8042芯片将不再接收新的数据,直到输出缓冲区被清空后,8042芯片才会继续接收编码数据。
键盘扫描码共三套,第1套为原始XT扫描码;第2套为AT扫描码;第三套为PS/2扫描码。现在键盘默认使用第二套AT扫描码,出于兼容性考虑,第二套扫描码最终都会转换为第一套XT扫描码供处理器使用。
第1套扫描码,每个按键扫描码由1B数据组成,这1B数据的低7位代表按键的扫描码,最高位代表按键状态( ...
64位操作系统-中断处理
8259A PIC通常PC机会采用两片8258A芯片级联的方式,将外部硬件设备的中断请求和引脚与处理器的中断接收引脚关联起来。两个芯片级联如下:
其中一个8259A作为主芯片,与CPU的INTR引脚相连;另一个作为从芯片,与主芯片的IR2引脚相连。
通常情况下,会按照如下情况与外部设备相连:
一个8259A PIC包含两组寄存器,分别是ICW(初始化命令字)寄存器组和OCW(操作控制字)寄存器组。ICW用于初始化中断控制器,OCW用于操作中断控制寄存器。
PC采用I/O地址映射方式,将8259A PIC的寄存器映射到I/O端口地址空间,因此必须借助IN和OUT汇编指令才能访问8259A PIC。主8259A芯片的I/O端口地址是20h和21h,从8259A芯片的I/O端口地址是A0h和A1h。内部结构如下:
通过I/O端口可操作中断控制寄存器的ICW和OCW寄存器组,进而配置IRR、PR、ISR、IMR等寄存器。
IRR(Interrput Request Register,中断请求寄存器)用来保存IR0~IR7引脚上接收的中断 ...
64位操作系统-内存管理
获取物理内存信息之前再Loader阶段通过BIOS中断服务程序int 0x15; ax=0xe820,包保存到物理地址0x7e00。此处存储的物理地址空间信息存有若干组,描述计算机平台的地址空间划分情况,数据量回根据当前主板硬件配置和物理内存容量而定,每条物理地址空间信息占用20B,详细定义如下:
1234567struct Memory_E820_Formate { unsigned int address1; unsigned int address2; unsigned int length1; unsigned int length2; unsigned int type;};
该结构体格式化物理地址0x7e00处的数据。还必须经过页表映射后才能使用,转换后的线性地址是0xffff800000007e00。
显示内存信息:
123456789101112131415161718192021222324void memory_init() { int i, j; unsigned long total_memory; struct ...
64位操作系统-系统异常
异常的分类
错误(fault):错误是一种可以被修正的以上。只要错误被修正,处理器可将程序或任务的运行环境还原至异常发生前(已在栈中保存的CS和EIP寄存器值),并重新执行产生异常的指令,也就是异常的返回地址指向错误产生的指令,而不是其后的位置
陷阱(trap):陷阱异常同样允许处理器继续执行任务或程序,只不过处理器对跳过产生异常的指令,即陷阱异常的返回地址指向诱发陷阱指令之后的地址
终止(abort):终止异常用于报告非常严重的错误,它往往无法准确提供产生异常的位置,同时页不允许程序或任务继续执行,典型的终止异常有硬件错误或系统表存在不合逻辑、非法值。
INTEL处理器目前支持的异常/中断:
系统异常处理处理器采用类似汇编指令CALL的调用方法来执行异常/中断处理程序。当处理器捕获到异常/中断时,便会根据异常/中断向量号(Interrupt Vector)从中断描述符表IDT索引出对应的门描述符,再由门描述符定位到处理程序的位置。如果向量号索引到一个中断门或陷阱门,处理器将会像执行CALL指令访问调用门一般,去执行异常/中断处理 ...
64位操作系统-屏幕显示
为了在屏幕上显示颜色,必须通过桢缓冲存储器完成。桢缓冲存储器(Frame Buffer),简称桢缓存或桢存。它是屏幕显示画面的一个内存映像,桢缓存的每个存储单元对应屏幕的一个像素,整个桢缓存对应一幅图像。桢缓存的特点是可对每个像素点进行操作,不仅可以借助它在屏幕上画出屏幕上色彩,还可以在屏幕上用像素点绘制文字以及图片。
此前设置说显示芯片的显示模式(模式号:0x180,分辨率:1400*900,颜色深度:32bit),而且内核执行头程序还将桢缓存的物理基地址映射到线性地址0xffff800000000000和0xa00000处。
屏幕上显示色彩桢缓存格式:一个像素点能够显示的颜色值位宽。Loader引导加载程序设置的显示模式可支持32位颜色深度的像素点,其中0~7位代表蓝色,8~15位代表吝啬,16~23位代表红色,24~31位是保留位。
如果想设置屏幕上某个像素点的颜色,必须知道这个点在屏幕上的位置,并计算处该点距离屏幕原点的偏移值。屏幕坐标位于左上角。
显示屏幕色带如下:
123456789101112131415161718192021222324252627282930313 ...
64位操作系统-内核主程序
内核主程序相当于应用程序的主函数。负责调用各个系统模块的初始化函数,在这些模块初始化结束后,它会创建处系统的第一个init进程,并将控制权交给init进程。
当前主程序不具备任何功能,只是为了让内核执行头程序有目标跳转而已。
start_kernel函数实现:
12345void start_kernel(void) { while (1) { ; }}
编译脚本:
12$(BUILD_KERNEL)/main.o: $(KERNEL)/main.c gcc -mcmodel=large -fno-builtin -m64 -c $< -o $@
使用kernel.lds负责将编译生成的main.o文件和header.o文件链接成可执行程序,输出名为system。
123$(BUILD_KERNEL)/system: $(BUILD_KERNEL)/header.o \ $(BUILD_KERNEL)/main.o ld -b elf64-x86-64 -o $@ $^ -T $(KERNEL)/ker ...
64位操作系统-lds链接脚本
系统程序的链接过程使用到链接脚本文件,通常情况下链接器都会使用默认的链接脚本文件。内核程序段的位置需要精心设计,所以默认链接脚本就不能使用,段名往往由操作系统独立命令。
链接脚本的主要作用是描述如何输入文件中的各个程序段(数据段、代码段、堆、栈、BSS)部署到输出文件中,并规划输出文件各程序段在内存中的布局。
链接脚本:
123456789101112131415161718192021222324252627OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")OUTPUT_ARCH(i386:x86-64)ENTRY(_start)SECTIONS{ . = 0xffff800000000000 + 0x100000; .text : { _text = .; *(.text) _etext = .; } . = ALIGN(8); .data : { _dat ...
64位操作系统-内核头程序
内核头程序就是内核程序中的一小段汇编代码。内核的线性地址0xffff800000000000对应物理地址0处,内核程序的起始线性地址位 0xffff800000000000 + 0x100000处。
描述符和段结构信息1234567891011121314151617181920212223242526272829303132333435363738394041// GDT Table.section .data.global GDT_TableGDT_Table: .quad 0x0000000000000000 // 0 null descriptor 00 .quad 0x0020980000000000 // 1 kernel code 64bit segment 08 .quad 0x0000920000000000 // 2 kernel code 64bit segment 10 .quad 0x0020f80000000000 // 3 user code 64bit segment 18 .quad 0x0000f20000000000 // 4 user ...
64位操作系统-控制寄存器
GDT、LDT和IDT表都是描述符表。描述符表是由若干个描述符组成,每个描述符占用8个字节的内存空间,每个描述符表内最多可以有(8K)8129个描述符。描述符是描述一个段的大小,地址及各种状态的。
控制寄存器CR0~4:
GDTR、IDTR等:
GDT全局描述符GDT全局描述表(GDT Global Descriptor Table)。在整个系统中,全局描述符表GDT只有一张(一个处理器对应一个GDT),GDT可以被放在内存的任何位置。系统用GDTR寄存器存放当前GDT表的基地址。在保护模式下,对一个段的描述包括3个方面的因素:Base Address、Limit、Access,它们加在一起被放在一个64-bit长的数据结构中,被称为段描述符。段描述符结构如下:
GDTRGDTR是一个长度为48bit的寄存器,内容为一个32位的基地址和一个16位的段限。其中32位的基址是指GDT在内存中的地址。基地址指定GDT表中字节0在线性地址空间中的地址,表长度指明GDT表的字节长度值。指令LGDT和SGDT分别用于加载和保存GDTR寄存器的内容。在机器刚加电或处理器复位后,基地址被默认 ...


