<font face="宋体">
- 一、基础编程实验1 :IA32 多任务运行管理程序设计
- 二、基础编程实验2:多任务微内核 OS 构建编程开发
一、基础编程实验1 :IA32 多任务运行管理程序设计
1. 开发环境配置与源程序架构简单分析
1)硬件环境
微型计算机(Intel x86 系列 CPU)一台;清华科教仪器厂 TPC-ZK-II 微机接口实验系统一台(支持一路外部中断);数字记忆示波器一台。
2)软件环境
(1) Windows XP 操作系统,编辑、汇编、链接和调试程序;运行环境为 DOS-裸机模式;
(2) TPC-PCI 集成开发环境软件一套及实验系统电子版资料;
(3) NASM0.98 开发环境。
3)源程序架构分析
- (1)、(2)表示系统段中的时钟中断子程序例程对任务调度而激活相应任务
- (3)、(5)表示8253外部中断来到执行中断程序;(4)、(6)表示外部中断结束返回原来被打断的任务
2. IA32多任务运行调度和任务间通信的基本原理
1)IA32多任务运行调度
软件框架 IA-32 主要是基于时间调度的思想,运行时只需要根据具体任务对时间的依赖性进行切换,每个任务相对独立运行。
时间切换调度由中断来实现。中断来到前执行某一任务,一旦中断来到将当前任务挂起,并保存当前任务的环境。同时激活下一个任务。在中断服务程序中进行任务的调度与切换,可以构建任务调度表确定任务的时间调度关系和优先级,根据任务就绪情况和优先级进行切换调度。程序执行过程中不要求单个任 务的程序段在规定时间内执行完毕。
IA-16 的调度也是基于时间调度的思想,但是其任务运行机制与这里的 IA-32 的有所不同。IA-16 环境下,中断来到时进入中断服务子程序中,判断某些特定条件决定该激活哪个任务,然后调用相应任务子程序,在下一次中断来到前结束当前任务子程序的执行,回到调用任务的中断服务子程序处,结束中断,最后返回主程序,等待下一次中断。而 IA-32 环境下,中断来到时进入中断子程序例程,保存当前任务的寄存器参数,激活下一个任务,加载新任务的寄存器参数,跳转到新任务开始执行。当下一个中断来到时,正在执行的任务可能没有执行完毕就被打断,并再次回到中断子程序例程进行当前任务的环境保存以及开始新一轮的任务调度。
2)任务间通信
vxWorks将所有任务都放在一个地址空间中简单且不受保护只要知道地址就可以直接访问。
OS提供了沟通的媒介供进程之间“对话”使用。沟通需要付出的成本。出于所解决问题的特性,OS提供了多种沟通的方式,每种方式的沟通成本也不尽相同,使用成本和沟通效率也有所不同。管道、消息队列、共享内存都是OS提供的供进程之间对话的方式。
既然是沟通,必然是沟通双方有秩序的说话,否则就成吵架了,谁也听不到对方说什么。因此OS必须提供此类的管制方式使得进程的沟通显的有序和谐。互斥锁、条件变量、记录锁、文件锁、信号灯均属此列。
管道、消息队列、共享内存都是OS所提供的对话的方式,进程所说出去的“话”至少需要暂时保存在某个地方,然后才能被别的进程取走(听到)。不同的实现对话方式,保存中间信息的媒介从逻辑上分有文件系统,内核和内存三个部分。其实保存在内核中也是保存在内存中的,只是这部分内存只能OS自己访问,普通进程不能直接读写。
3. 任务指示器和指示结果
任务指示器是反映任务运行状态的变量或信号。IA32实验里我们用不同高度的DA输出模拟量反映不同任务,你看看程序。其他的可以用(全局)变量反映检查,某个任务出现死循环了,马上可以从任务指示器发现。
-
现象:
产生中断时,流水灯依次点亮
4. 内核分时优先级调度修改方案
(i)创建任务指定采用分时调度策略,并指定优先级nice值(-20~19)。
(ii)将根据每个任务的nice值确定在CPU上的执行时间(counter)。
(iii)如果没有等待资源,则将该任务加入到就绪队列中。
(iv)调度程序遍历就绪队列中的任务,通过对每个任务动态优先级的计算权值(counter+20-nice)结果,选择计算结果最大的一个去运行,当这个时间片用完后(counter减至0)或者主动放弃CPU时,该任务将被放在就绪队列末尾(时间片用完)或等待队列(因等待资源而放弃CPU)中。
(v)此时调度程序重复上面计算过程,转到第4步。
(vi)当调度程序发现所有就绪任务计算所得的权值都为不大于0时,重复第2步[1]。
二、基础编程实验2:多任务微内核 OS 构建编程开发
1. Vmlinux 微内核程序开发工作原理
内核线程作为内核态的一个逻辑执行流,拥有私有的栈空间,但是除了私有栈之外,不拥有其他资源,所有的内核线程拥有相同的页表,共享所有的全局数据。Intel 的 x86 架构下 CPU 提供的任务切换机制是使用 TSS 段进行切换。这么做比较繁琐且效率低,现在的操作系统都不会完全采用硬件切换机制。本实验所阐述的任务切换只发生在内核态,不涉及特权级转移过程,可以完全由硬件实现。任务的切换就是保存当前任务的现场,恢复下一个要执行的任务的现场,所以需要一个数据结构 task_struct 来保存这些现场信息。这个数据结构一般会放置任务相关的一些信息并且以链表的形式组织起来,这个结构被称为 PCB(Process Control Block)或者 TCB(Task Control Block).
2. 微内核程序的理解,工程/项目模块结构分析,主要模块源程序分析说明
1)微内核程序的理解
微内核由一群尽可能将数量最小化的软件程序组成,它们负责提供实现一个操作系统所需要的各种机制与功能,微内核操作系统就是一种基于微内核架构的操作系统。
基本功能:
- a) 进程(线程)管理
- b) 低级存储器管理
- c) 中断和陷入处理
优点:
- a) 提高了可扩展性
- b) 增强了可靠性
- c) 增强了可移植性
- d) 提供了对分布式系统的支持
- e) 融入了面向对象技术
存在的问题:
在微内核OS中,由于采用了非常小的内核,以及客户/服务器模式和消息传递机制,这些虽给微内核OS带来了许多优点,但由此也使微内核OS存在着潜在的缺点。其中最主要的是,较之早期OS,微内核OS的运行效率有所降低[2]。
效率降低的最主要的原因是,在完成一次客户对OS提出的服务请求时,需要利用消息实现多次交互和进行用户/内核模式及上下文的多次切换。然而,在早期的OS中,用户进程在请求取得OS服务时,一般只需进行两次上下文的切换:一次是在执行系统调用后,由用户态转向系统态时;另一次是在系统完成用户请求的服务后,由系统态返回用户态时。
为了改善运行效率,可以重新把一些常用的操作系统基本功能,由服务器移入微内核中。这样可使客户对常用操作系统功能的请求所发生的用户/内核模式和上下文的切换的次数,由四次或八次降为两次。但这又会使微内核的容量明显地增大,在小型接口定义和适应性方面的优点也有所下降,同时也提高了微内核的设计代价[3]。
2)工程/项目模块结构分析
(1)主目录Exp
如图1,构建项目Exp,作为源代码的主目录,该目录包含所有的9个子目录、一个Makefile文件和一个vmlinux.lds文件,Makefile 文件是编译辅助工具软件 make 的参数配置文件。make 工具软件的主要作用是通过识别哪些文件已被修改过,从而自动地决定在一个含有多个源程序文件的程序系统中哪些文件需要被重新编译。因此,make工具软件是程序项目的管理软件。vmlinux.lds文件是链接脚本部分,链接脚本指定了怎么链接程序并将特定的代码放到专门的段区间。
(2)辅助工具目录bin
bin目录是辅助工具的配置目录,该目录包含了一些制作.iso文件的必要工具。
(3)引导启动目录boot
boot目录中包含一个AT&T汇编语言文件和一个C语言文件,该目录主要完成系统的启
动引导任务。具体实现的任务是基于Grub2,采用multiboot2标准,将一些指定的硬件信息
读入内存的指定位置,同时设置系统工作所需的临时GDT,IDT,堆栈,页表等,并使计算机从 16bit 的实模式进入 32bit 的保护模式。
(4)grub配置目录config
config目录包含一个grub.cfg文件,该文件是对grub开机引导目录的设置。
(5)驱动目录 drivers
drivers目录包含8个C语言文件和2个AT&T汇编语言文件,该目录主要是对全局描述符表GDT,中断描述符表IDT、键盘、定时器、显卡的图像模式以及任务切换显示“示波器”等的初始化设置。
(6)头文件主目录 include
include目录包含20个.h文件和一个sys文件夹,各头文件的作用及功能请参照其他各目录的作用与描述。
(7)内核初始化目录init
init目录包含2个C语言文件,用于执行内核所有的初始化工作,然后开始设定实验任务的执行与切换。
(8)内核程序主目录 kernel
kernel目录共包含了3个C语言文件和3个AT&T汇编语言文件,该目录主要是对任务创建、任务调度、字符串打印等功能进行的设置。
(9)内核库函数目录lib
lib目录包含了3个C语言文件,由于操作系统级的程序不能使用标准C函数库以及其
它的一些函数库,此处对一些必要的功能函数进行了实现。
(10)内存管理程序目录mm
mm目录包含了3个C语言文件,该目录主要用于管理程序对主内存区的使用,实现了进程逻辑地址到线性地址以及线性地址到主内存区物理内存地址的映射,通过内存的分页管理机制,在进程虚拟内存页与主内存区建立了对应关系。同时,它也提供了 I/O 端口到内存的映射关系和完成了内存堆的设置。
3)主要模块源程序分析说明
(i) 全局初始化
init_all();
(ii) 声明进程标识符
int pidA=-1, pidB=-1, pidC=-1;
(iii) 内核线程创建
pidA = kernel_thread(task1,40,NULL);
pidB = kernel_thread(task2,80,NULL);
pidC = kernel_thread(task3,120,NULL);
// 内核线程创建
int32_t kernel_thread(int (*fn)(void *), int priority, void *arg);
- 函数返回进程标识符pid
-
(*fn)(void *)
: 任务子程序 -
priority
: 优先级 -
*arg
: NULL
(iv) 作用域设置
scopeSet(pidA, pidB, pidC);
void scopeSet(int A, int B, int C)
{
scopeApid = A;
scopeBpid = B;
scopeCpid = C;
}
(v) 循环已创建的线程
while(1){;}
3. 基于时间片的分时操作系统微内核程序分时调度与通信原型源程序的实现分析
(1) 无修改的源程序
- 三个task按照优先级顺序执行
(2) 调换线程B和线程A的创建顺序
pidB = kernel_thread(task2,40,NULL);
pidA = kernel_thread(task1,20,NULL);
pidC = kernel_thread(task3,60,NULL);
- 线程B执行后线程A才开始执行
- 线程的创建顺序决定了任务的优先级顺序,即先创建的线程的优先级后,后创建的线程的优先级低,以此类推
(3) 短暂按下键盘按键产生中断
- 按键按下后,任务A,B,C被按键中断抢占了优先级,可以从图中看到白色的小点,但是键盘输出了三次,这是因为键盘设定了读键间隔较短,导致程序认为我按了三次按键。如果在读键的函数中添加
while(Is_Key_Pressed());
,可以防止按键抖动。 - 如果连续按住,按键中断就会一直占用CPU资源,直到释放按键才会释放CPU资源,CPU从中断的地方继续执行三个任务线程A、B、C。
4. 具有优先级和实时特性的 RTOS 微内核修改方案
修改后的程序如下:
void start_kernel()
{
init_all();
int pidA=-1, pidB=-1, pidC=-1;
pidA = kernel_thread(task1,40,NULL); // 内核线程创建
pidB = kernel_thread(task2,80,NULL);
pidC = kernel_thread(task3,120,NULL);
pidD = kernel_thread(task4,160,NULL);
pidE = kernel_thread(task5,200,NULL);
pidF = kernel_thread(task6,240,NULL);
scopeSet(pidA, pidB, pidC, pidD, pidE, pidF);
while(1){;}
}
- 增加了D、E、F三个线程
- 在
scopeSet()
中增加了三个参数变量 - 在输出打印的函数中增加了三个通道
程序运行后输出如下:
- 从图中可以看到,六个任务按照线程创建的优先级顺序轮流执行
当有按键发生时,程序输出如下:
- 从图中可以看到当有按键按下时,任务D被抢占,中断任务开始执行
- 按键中断子程序执行完后,任务D从被中断的地方继续开始执行