操作系统开发系列—13.d.多进程 ●
进程此时不仅是在运行而已,它可以随时被中断,可以在中断处理程序完成之后被恢复。进程此时已经有了两种状态:运行和睡眠。我们已经具备了处理多个进程的能力,只需要让其中一个进程处在运行态,其余进程处在睡眠态就可以了。
在main.c中进程A的代码的下面添加进程B:
void TestB()
{
int i = 0x1000;
while(1){
disp_str("B");
disp_int(i++);
disp_str(".");
delay(1);
}
}
打印的字母换成了B,i的初始值被设成了0x1000.
让我们来回忆一下当初准备第一个进程时还做了哪些工作。进程不外乎4个要素:进程表、进程体、GDT、TSS。
Minix系统中定义了一个数组叫做tasktab。这个数组的每一项定义好一个任务(“任何”和“进程”可以互换)的开始地址、堆栈等,在初始化的时候,只要用一个for循环依次读取每一项,然后填充到相应的进程表项中就可以了。
首先在proc.h中:
typedef struct s_task {
	task_f	initial_eip;
	int	stacksize;
	char	name[32];
}TASK;
一个进程只要有一个进程体和堆栈就可以运行了,所以这个数组只要有前两个成员其实就已经够了。这里我们还定义了name,以便给每个进程起一个名字。
PUBLIC	TASK	task_table[NR_TASKS] = {{TestA, STACK_SIZE_TESTA, "TestA"},
																			{TestB, STACK_SIZE_TESTB, "TestB"}};
别忘了在global.h中:
extern TASK task_table[];
记得把NR_TASKS的值修改为2.还有STACK_SIZE_TESTB:
/* Number of tasks */
#define NR_TASKS 2 /* stacks of tasks */
#define STACK_SIZE_TESTA 0x8000
#define STACK_SIZE_TESTB 0x8000 #define STACK_SIZE_TOTAL (STACK_SIZE_TESTA + \
STACK_SIZE_TESTB)
下面来做进程表的初始化工作。现在可以用for循环来做进程表的初始化工作了。
请看main.c的kernel_main()函数。在我们这个简单的例子中,进程之间区别真的不大。每一次循环的不同在于,从TASK结构中读取不同的任务入口地址、堆栈栈顶和进程名,然后赋给相应的进程表项。需要注意两点:
1.由于堆栈是从高地址往低地址生长的,所以在给每一个进程分配堆栈空间的时候也是从高地址往低地址进行。
2.我们为每一个进程都在GDT中分配一个描述符用来对应进程的LDT。我们在task_table中定义了几个任务,通过上文的for循环中的代码,GDT中就会有几个描述符被初始化,它们列在SELECTOR_LDT_FIRST之后。
此外,p_name和pid目前并没有什么实际的作用。
每一个进程都会在GDT中对应一个LDT描述符。可是ldt选择子仅仅是解决了where问题,通过它,我们能在GDT中找到相应的描述符,但描述符的具体内容是什么,what问题还没解决。
在protect.c中的init_prot函数也使用一个循环:
// 填充 GDT 中进程的 LDT 的描述符
int i;
PROCESS* p_proc = proc_table;
u16 selector_ldt = INDEX_LDT_FIRST << 3;
for(i=0;i<NR_TASKS;i++){
init_descriptor(&gdt[selector_ldt>>3],
vir2phys(seg2phys(SELECTOR_KERNEL_DS),
proc_table[i].ldts),
LDT_SIZE * sizeof(DESCRIPTOR) - 1,
DA_LDT);
p_proc++;
selector_ldt += 1 << 3;
}
另外,每个进程都有自己的LDT,所以当进程切换时需要重新加载ldtr,如下:
lldt [esp + P_LDT_SEL]
接下来修改中断处理程序来切换进程。
在任务切换过程中,首先,处理器中各寄存器的当前值被自动保存到TR(任务寄存器)所指定的TSS中;然后,下一任务的TSS的选择子被装入TR;最后,从TR所指定的TSS中取出各寄存器的值送到处理器的各寄存器中。
要想恢复不同的进程,只需要将esp指向不同的进程表就可以了,全局变量p_proc_ready是指向进程表结构的指针,我们只需要在下面这一句执行之前把它赋予不同的值就可以了。
mov esp, [p_proc_ready] ; 离开内核栈
我们再来学习一下Minix,创建一个clock.c。
PUBLIC void clock_handler(int irq)
{
disp_str("#");
}
在时钟中断例程中调用这个函数。
下面该进程切换了:
PUBLIC void clock_handler(int irq)
{
disp_str("#");
p_proc_ready++;
if (p_proc_ready >= proc_table + NR_TASKS)
p_proc_ready = proc_table;
}
在上面代码中,每一次我们让p_proc_ready指向进程表中的下一个表项,如果切换前已经到达进程表结尾则回到第一个表项。运行结果如下,我们看到了交替出现的“A”和“B”,还有各自不断增加的数字,实现了多进程:

【源码】
操作系统开发系列—13.d.多进程 ●的更多相关文章
- 操作系统开发系列—13.g.操作系统的系统调用 ●
		
在我们的操作系统中,已经存在的3个进程是运行在ring1上的,它们已经不能任意地使用某些指令,不能访问某些权限更高的内存区域,但如果一项任务需要这些使用指令或者内存区域时,只能通过系统调用来实现,它是 ...
 - 操作系统开发系列—13.i.进程调度 ●
		
上面的三个进程都是延迟相同的时间,让我们修改一下,尝试让它们延迟不同的时间. void TestA() { int i = 0; while (1) { disp_str("A." ...
 - 操作系统开发系列—13.h.延时操作
		
计数器的工作原理是这样的:它有一个输入频率,在PC上是1193180HZ.在每一个时钟周期(CLK cycle),计数器值会减1,当减到0时,就会触发一个输出.由于计数器是16位的,所以最大值是655 ...
 - 操作系统开发系列—13.e.三进程
		
我们再来添加一个任务,首先添加一个进程体: void TestC() { int i = 0x2000; while(1){ disp_str("C"); disp_int(i++ ...
 - 操作系统开发系列—13.c.进程之中断重入
		
现在又出现了另外一个的问题,在中断处理过程中是否应该允许下一个中断发生? 让我们修改一下代码,以便让系统可以在时钟中断的处理过程中接受下一个时钟中断.这听起来不是个很好的主意,但是可以借此来做个试验. ...
 - 操作系统开发系列—13.b.进程之丰富中断处理程序
		
首先打开时钟中断: out_byte(INT_M_CTLMASK, 0xFE); // Master 8259, OCW1. out_byte(INT_S_CTLMASK, 0xFF); // Sla ...
 - 操作系统开发系列—13.a.进程 ●
		
进程的切换及调度等内容是和保护模式的相关技术紧密相连的,这些代码量可能并不多,但却至关重要. 我们需要一个数据结构记录一个进程的状态,在进程要被挂起的时候,进程信息就被写入这个数据结构,等到进程重新启 ...
 - 微信公众号开发系列-13、基于RDIFramework.NET框架整合微信开发应用效果展示
		
1.前言 通过前面一系列文章的学习,我们对微信公众号开发已经有了一个比较深入和全面的了解. 微信公众号开发为企业解决那些问题呢? 我们经常看到微信公众号定制开发.微信公众平台定制开发,都不知道这些能给 ...
 - 操作系统开发系列—1.HelloWorld ●
		
org 07c00h ;伪指令,告诉编译器程序会被加载到7c00处 mov ax, cs mov ds, ax mov es, ax call DispStr ;调用显示字符串例程 jmp $ ;无限 ...
 
随机推荐
- C# Socket系列一 简单的创建socket的监听
			
socket的应用场景,在快速,稳定,保持长连接的数据传输代码.Http也是socket封装出来的,基于一次请求一次回复,然后断开的socket连接封装. 比如我们常见的游戏服务器,目前的很火的物联网 ...
 - Java 多线程下的单例模式
			
单例对象(Singleton)是一种常用的设计模式.在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在.正是由于这个特 点,单例对象通常作为程序中的存放配置信息的载体,因为它能保证 ...
 - 解决IIS7.0服务和用户上传的文件分别部署在不同的电脑上时,解决权限的问题
			
为解决IIS服务和用户上传的文件分别部署在不同的电脑上时,解决权限的问题. 定义: A:iis服务器 B:文件服务器 步骤: 1.在B上创建一个用户[uploaduser](并设置密码) 2.给B上的 ...
 - SCC重新建图
			
Tarjan或Kosaraju算法[对每个点归类belong]求出SCC之后,对num_scc个SCC重新建图,针对不同问题,考虑重边的问题. //************************** ...
 - C ~ 链式队列与循环队列
			
此处的链式与循环队列可以应用于BFS和树的层序遍历.下面是对其结构和基本操作的程序描述. 1.循环队列 解决循环队列的队空和队满的方法: [1].增加一个参数count,用来记录数组中当前 ...
 - HTTP 错误 500.19 - Internal Server Error 错误解决
			
今天在测试部署站点是遇到一个问题 HTTP 错误 500.19 - Internal Server Error 如下图,在网上搜了写解决方法,什么设置iis目录浏览,删除web.config中配置, ...
 - 阿里社招B2B
			
岗位描述:1. 按USE CASE进行业务需求分析和软件概要设计2. 进行软件详细设计和编码实现,确保性能.质量和安全3. 维护和升级现有软件产品,快速定位并修复现有软件缺陷岗位要求:1. 精通Web ...
 - [PHP] 使用Socket提供Http服务
			
我的SimpleLoader里面的一块 https://github.com/taoshihan1991/simpleloader <?php namespace Server; class S ...
 - No.014:Longest Common Prefix
			
问题: Write a function to find the longest common prefix string amongst an array of strings. 官方难度: Eas ...
 - CI框架源码阅读笔记6 扩展钩子 Hook.php
			
CI框架允许你在不修改系统核心代码的基础上添加或者更改系统的核心功能(如重写缓存.输出等).例如,在系统开启hook的条件下(config.php中$config['enable_hooks'] = ...