有了可执行程序的地址和程序入口地址,就可以根据它们来创建进程。先修改进程控制块的数据结构:
typedef struct process_control_block
{
//进程号
u32 process_id;
//任务描述段
s_tss tss;
//代码段和数据段的局部描述符
s_gdt ldt[2];
//程序地址
void *run;
//程序大小
u32 run_size;
//页目录
void *page_dir;
//页表
void *page_tbl;
//程序0级栈
void *stack0;
} s_pcb;
创建进程,并为页目录、页表及0级栈申请内存空间:
//进程控制块
s_pcb *pcb = alloc_page(process_id, pages_of_pcb(), 0, 0);
if (pcb == NULL)
{
free_mm(run, run_pages);
return NULL;
}
//申请页目录
pcb->page_dir = alloc_page(process_id, P_PAGE_DIR_NUM, 0, 0);
//申请页表
pcb->page_tbl = alloc_page(process_id, P_PAGE_TBL_NUM, 0, 0);
//申请0级栈
pcb->stack0 = alloc_page(process_id, P_STACK0_P_NUM, 0, 0);
//初始化pcb
init_process(pcb, process_id, run, entry_point, run_size);
//将此进程加入链表
pcb_insert(pcb);
//进程号加一
process_id++;
//进程号
pcb->process_id = pid;
//程序地址
pcb->run = run;
//程序大小
pcb->run_size = run_size;
//程序入口地址
pcb->tss.eip = (u32) run + run_offset;
//永远是4G,但为了4K对齐保留了最后一个0x1000
pcb->tss.esp = 0xfffff000;
//程序0级栈
pcb->tss.esp0 = (u32) pcb->stack0 + P_STACK0_SIZE;
//页目录存入到cr3中
pcb->tss.cr3 = (u32) pcb->page_dir;
//地址
u32 address = 0;
u32 *page_dir = ((u32 *) pcb->page_dir);
u32 *page_tbl = ((u32 *) pcb->page_tbl);
/*
* 前16M系统内存为已使用并且为只读
*/
for (int i = 0; i < 1024; i++)
{
for (int j = 0; j < 1024; j++)
{
if (i < 4)
{
page_tbl[j] = address | 5;
}
else
{
page_tbl[j] = 6;
}
address += 0x1000;
}
page_dir[i] = (u32) page_tbl | 7;
page_tbl += 1024;
}
//初始化pcb所在的内存页
init_process_page((u32) pcb, pages_of_pcb(), pcb->page_dir);
//初始化pcb->stack0所在的内存页
init_process_page((u32) pcb->stack0, pages_of_pcb(), pcb->page_dir);
将一个使用地址加入到页表中:
void init_process_page(u32 address, u32 pages, u32 *page_dir)
{
//mm_pcb所在内存页目录索引
u32 page_dir_index = (address >> 22) & 0x3ff;
//mm_pcb所在内存页表索引
u32 page_table_index = (address >> 12) & 0x3ff;
address &= 0xFFC00000;
u32 *page_tbl = (u32 *) (page_dir[page_dir_index] & 0xfffff000);
//mm_pcb所在内存页表
for (int i = 0; i < 1024; i++)
{
//设置mm_pcb所在内存的页表
if (i >= page_table_index && i <= (page_table_index + 16))
{
page_tbl[i] = address | 7;
}
address += 0x1000;
}
//设置mm_pcb所在内存的页目录
page_dir[page_dir_index] = (u32) page_tbl | 7;
//设置pages个页面剩余页
if (page_table_index + pages >= 1024)
{
page_dir_index++;
page_tbl += 1024;
for (int i = 0; i < 1024; i++)
{
if (i < (pages - (1024 - page_table_index)))
{
page_tbl[i] = address | 7;
}
address += 0x1000;
}
page_dir[page_dir_index] = (u32) page_tbl | 7;
}
}
至此,我们就可以载入并执行一个可执行程序了,但还有最后一个问题,就是进程的调度。目前采用的是链表,按平均时间片执行所有程序。链表的内部实现是一个单向链表,如下:
/*
* 为列表插入一个新节点
*/
s_list* list_insert_node(s_list *list, s_list *p_list)
{
if (p_list == NULL)
{
return list;
}
p_list->next = NULL;
s_list* header = list;
if (header == NULL)
{
header = p_list;
}
else
{
p_list->next = header;
header = p_list;
}
return header;
}
/*
* 移除一个节点
*/
s_list* list_remove_node(s_list *list, s_list *p_list)
{
if (p_list == NULL)
{
return list;
}
s_list* header = list;
s_list* p = header;
s_list* fp = p;
while (p != NULL)
{
if (p == p_list)
{
if (p == fp)
{
header = p->next;
p->next = NULL;
return header;
}
else
{
if (p->next == NULL)
{
fp->next = NULL;
return header;
}
else
{
fp->next = p->next;
p->next = NULL;
return header;
}
}
}
if (p == fp)
{
p = p->next;
}
else
{
p = p->next;
fp = fp->next;
}
}
return header;
}
/*
* 将链表的头节点移动到尾部,即链表
*/
s_list* list_header2footer(s_list *list)
{
s_list* header = list;
if (header == NULL)
{
return NULL;
}
if (header->next == NULL)
{
return header;
}
s_list *fst = header;
s_list *p = header;
header = header->next;
while (p->next != NULL)
{
p = p->next;
}
p->next = fst;
fst->next = NULL;
return header;
}
/*
* 将进程加入到链表中
*/
void pcb_insert(s_pcb *pcb)
{
if (pcb == NULL)
{
return;
}
s_list *p_list = alloc_mm(sizeof(s_list));
p_list->node = pcb;
list_pcb = list_insert_node(list_pcb, p_list);
}
最后修改进程调度算法:
//进程链表
s_list *list_pcb = NULL;
//当前正在运行的进程
s_pcb *pcb_cur = NULL;
//上一次运行的进程
s_pcb *pcb_last_run = NULL;
/*
* 进程调度,目前只使用平均时间片轮转的算法
*/
void schedule()
{
//取得链表头
s_list *list_header = list_pcb;
if (list_header == NULL)
{
return;
}
//取得链表头的进程
pcb_cur = (s_pcb *) (list_header->node);
/*
* 如果当前进程不是上一次运行的进程,说明链表中有两个或两个以上的进程
* 链表中只有一个进程,不需要切换。
* 链表中有两个或两个以上的进程,需要切换。
*/
if (pcb_cur != pcb_last_run)
{
//将链表头移动到链表尾,链表
list_pcb = list_header2footer(list_pcb);
//设置tss和ldt
addr_to_gdt(GDT_TYPE_TSS, (u32) &(pcb_cur->tss), &gdts[4], GDT_G_BYTE, sizeof(s_tss) * 8);
addr_to_gdt(GDT_TYPE_LDT, (u32) (pcb_cur->ldt), &gdts[5], GDT_G_BYTE, sizeof(s_gdt) * 2 * 8);
//将上一次运行进程设置为当前进程
pcb_last_run = pcb_cur;
//在时钟中断时并没有切换ds和cr3寄存器
//但是在call tss时cr3会被修改为tss中的cr3
set_ds(0xf);
//切换进程
call_tss();
}
}
在start_kernel()函数中加入install_system();安装系统进程。在Makefile中为内核程序加入进程调度算法文件sche.c和system程序的编译链接代码。编译并运行:
源代码的下载地址为:
https https://github.com/magicworldos/lidqos.git
git git@github.com:magicworldos/lidqos.git
subverion https://github.com/magicworldos/lidqos
branch v0.18
Copyright © 2015-2023 问渠网 辽ICP备15013245号