操作系统开发系列—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 $ ;无限 ...
随机推荐
- Azure ARM (5) ARM Template初探 - 本地JSON Template文件(1)
<Windows Azure Platform 系列文章目录> Azure ARM (1) 概览 Azure ARM (2) 概览 Azure ARM (3) ...
- redis学习之二from github
大概敲了一遍基本命令,熟悉了redis的存储方式.现在开始进一步系统的学习.学习教程目前计划有三个,一个是github上的https://github.com/JasonLai256/the-litt ...
- linux专题一之文件归档和压缩(tar、file、zip)
本文主要从以下几个方便来说明文件的归档和压缩,同时比较几种不同压缩方法的压缩比率及特点. 文件归档命令tar,tar.gz源码包的安装管理 创建tar包-解压-查询tar包内容 zip命令的用法 为 ...
- 用redux完成事务清单
今天再来一个例子,我们从组件开始. App.js import React, { PropTypes } from 'react' import { bindActionCreators } from ...
- js正则中的贪婪和非贪婪模式问题总结
var b="abeeee:eeeee:eeeeeab"; console.log(b.match(/e+\:e+/g));//["eeee:eeeee"]贪婪 ...
- LeetCode - Populating Next Right Pointers in Each Node
题目: Given a binary tree struct TreeLinkNode { TreeLinkNode *left; TreeLinkNode *right; TreeLinkNode ...
- 【原创】jQuery 仿百度输入标签插件
1.先上效果图 2.调用方式 <link href="/Styles/tagsinput.css" rel="stylesheet" type=" ...
- iOS阶段学习第18天笔记(Plist-Archiver-归档与解归档操作)
iOS学习(OC语言)知识点整理 一.归档与解归档的操作 1)归档是一个过程,将一个或多个对象存储起来,以便以后可以还原,包括将对象存入文件,以后再读取 将数据对象归档成plist文件 2)plist ...
- Ado.net[增删改查,GET传值]
1. <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Home.aspx.c ...
- JS获取子窗口中返回的数据
在开发的时候,遇到了这样一个问题,客户填写自己的收货地址,可以新建,但同时也可以选择之前填写的,由于我们的客户本身就是商户,地址繁多,把它之前的地址简单用个下拉框罗列出来显然不合适,并且客户要求能够对 ...