
美好的一天,亲爱的读者,很可能您看了我以前的文章,您自己可以在相当短的时间内编写出可行的OS。 好了,今天我们将讨论操作系统中多任务的实现。
好吧,您可能无法想象2018年的单任务操作系统,所以我决定谈论在我的OS中实现多任务的问题。 因此,第一件事-您需要确定多任务的类型,我选择了抢先式。
她是什么样的人? 抢占式多任务处理是一种用于在进程之间分配处理器计算能力的系统:每个进程都有自己的时间片,每个进程都有自己的优先级。 第一个问题是选择哪个长度的量子,在量子到期后如何停止进程运行? 实际上,一切都比以往更加轻松! 我们将使用PIT的初始设置频率为10026,每秒中断一分钱,在那里,我们解决了另一个问题:我们已经在停止先前的过程。 因此,让我们从PIT开始。
坑
PIT-可编程间隔计时器-一种计数器,一旦达到任何编程的递增数量,便发出信号。 另外,使用此计时器,您可以在计算机中发出尖叫声(通过设备测试后发出尖叫声)。 因此,他以1193182赫兹的频率计数,这意味着我们需要将其编程为119(1193182/119大约等于10026)。 为此,向第一个生成器的端口发送2个字节,首先是低字节,然后是高字节:
unsigned short hz = 119; outportb(0x43, 0x34); outportb(0x40, (unsigned char)hz & 0xFF);
现在值得从PIT开始对中断进行编程,它的IRQ为0,而在PIC重新映射后,它将为0x20m。 对于第一个PIC的IRQ,我编写了以下宏:
结构与流程
因此,正如您所了解的,我们需要为每个进程开发一个结构,以及一个允许我记住所有内存分配的结构。
这是我所拥有的:
typedef struct _pralloc { void * addr; struct _pralloc * next; } processAlloc; typedef struct { void * entry; processAlloc *allocs; } ELF_Process; typedef struct __attribute__((packed)) _E { unsigned int eax;
首先,我们需要了解以下内容:我们可以在全局地址的某个位置(例如,在0xDEAD处放置当前正在运行的进程的编号),然后可以在执行任何代码时确定:我们拥有当前正在运行的进程的编号,这意味着当访问malloc时,我们知道要向谁分配内存,并且我们可以立即将分配的内存地址添加到allocs列表中。
void addProcessAlloc(ELF_Process * p, void * addr) { void * z = p->allocs; p->allocs = malloc_wo_adding_to_process(sizeof(processAlloc)); p->allocs->addr = addr; p->allocs->next = z; }
好吧,我们用表的过程描述编写了表的结构,接下来又该如何切换任务呢?
首先,我想指出,例如,在处理程序中,局部变量存储在堆栈中,这意味着进入处理程序后,编译器破坏了我们的esp。 为了防止这种情况的发生,请创建一个具有绝对地址的变量,然后在调用处理程序之前,将ESP放在此处。 在处理程序中,我们需要将EOI发送到第一个PIC,并找到需要切换到的流程(我不会描述优先级机制:这很简单,就像交通拥堵一样)。 接下来-我们需要保存当前进程的所有寄存器和标志,因此在将ESP放入变量之前,我们将所有寄存器(包括段寄存器)保存到堆栈中。 在处理程序本身中,我们非常小心地需要将它们从堆栈中删除,同时还要保留标志和返回地址。 我要指出的是堆栈会增长(即ESP减少),这意味着您保存在堆栈上的最后一个寄存器将位于ESP,倒数第二个寄存器将为ESP +4,依此类推:

现在剩下的工作是将过程寄存器的值放入切换到的寄存器中并执行IRET。 赢利!
流程开始
启动进程时,足以为进程分配堆栈,然后将argc和argv放入其中,这是在进程完成后将得到控制的函数的地址。 您还需要将处理器标志设置为所需的值,例如,对于我的操作系统为0x216,您可以在Wikipedia上了解标志寄存器。
最后,我想祝您成功,很快我将写关于如何处理记忆和您感兴趣的其他文章的文章。
祝您好运,并遵守道德规范!