有了可执行程序的地址和程序入口地址,就可以根据它们来创建进程。先修改进程控制块的数据结构:
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号