MIT 6.828学习笔记
- 第0章 操作系统接口
以下包含 xv6-book阅读笔记以及xv6源码剖析
第0章 操作系统接口
xv6使用了传统的内核概念: 内核是一个向其他运行中程序提供服务的特殊程序. 每一个运行中程序(进程)都包含指令,数据,栈的内存空间. 指令实现程序运算, 数据是用于运算过程的变量, 栈管理程序的过程调用.
进程通过系统调用使用内核服务,系统调用进入内核,内核执行服务并返回.进程总是在用户空间和内核空间之间交替运行.
内核使用CPU__硬件保护机制__保证用户进程只能访问自己的内存空间.内核有实现保护机制的硬件权限而用户程序没有.
用户程序进行系统调用时,硬件会提升特权级来执行一些内核中定义的功能.
xv6的进程包含两部分: 1. 用户内存空间(指令,数据,栈) 2. 仅内核可见的进程状态.
分时特性: 在可用CPU中不断切换,决定哪一个等待中的进程被执行.当一个进程不在进行时,xv6会保存它CPU寄存器,当再次执行时恢复寄存器的值. 内核将进程和一个pid(process identifier)关联起来.
一个进程可以通过fork()创建一个子进程,一个进程调用fork() 后, 系统优先给新进程分配资源, 然后把原来进程的全部资源都复制到新的进程中, 然后寄存器指向新进程. fork()函数在父进程,子进程中都返回一个值(一次调用两次返回,子进程的返回值是在子进程的栈中构造好数据之后从栈中获取), 对于父进程返回pid,对子进程返回0.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| int pid pid = fork();
if (pid > 0) { printf("parent: child="%d"\n", pid); pid = wait(); printf("child %d is done\n", pid); } else if (pid == 0) { printf("child: exiting\n"); } else { printf("fork error\n"); }
|
在执行fork()函数之前,只有一个进程这段代码, 调用后就成为了两个进程在执行.因此父进程中的pid被赋值1234(随便写的),子进程的pid被赋值0,所以就打印了这样的一个结果.
这个过程可以理解为链表, 父进程的pid(pointer)指向子进程的pid,子进程的子进程等于NULL,所以子进程的pid==0.
补充: 子进程拥有自己独立的内存空间, 在fork()函数执后开始执行下面的内容.
fork的实现分为两步: 1. 复制当前进程资源 2.执行fork出的进程
复制进程的资源包括以下几步: 1. 进程pid 2.程序体(即代码数据段) 3.用户栈 4.内核栈 5.虚拟内存池 6.页表
代码入下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
static int32_t copy_pcb_vaddrbitmap_stack0(task_struct *child_thread, task_struct *parent_thread) { memcpy(child_thread, parent_thread, PG_SIZE); child_thread->pid = fork_pid(); child_thread->elapsed_ticks = 0; child_thread->status = TASK_READY; child_thread->ticks = child_thread->priority; child_thread->parent_pid = parent_thread->pid; child_thread->general_tag.prev = child_thread->general_tag.next = NULL; child_thread->all_list_tag.prev = child_thread->all_list_tag.next = NULL; block_desc_init(child_thread->u_block_desc); uint32_t bitmap_pg_cnt = DIV_ROUND_UP((0xc0000000 - USER_VADDR_START) / PG_SIZE / 8, PG_SIZE); void *vaddr_btmp = get_kernel_pages(bitmap_pg_cnt); if (vaddr_btmp == NULL) return -1;
memcpy(vaddr_btmp, child_thread->userprog_vaddr.vaddr_bitmap.bits, bitmap_pg_cnt * PG_SIZE); child_thread->userprog_vaddr.vaddr_bitmap.bits = vaddr_btmp;
ASSERT(strlen(child_thread->name) < 11); strcat(child_thread->name, "_fork"); return 0; }
static void copy_body_stack3(task_struct *child_thread, task_struct *parent_thread, void *buf_page) { uint8_t *vaddr_btmp = parent_thread->userprog_vaddr.vaddr_bitmap.bits; uint32_t btmp_bytes_len = parent_thread->userprog_vaddr.vaddr_bitmap.btmp_bytes_len; uint32_t vaddr_start = parent_thread->userprog_vaddr.vaddr_start; uint32_t idx_byte = 0; uint32_t idx_bit = 0; uint32_t prog_vaddr = 0;
while (idx_byte < btmp_bytes_len) { if (vaddr_btmp[idx_byte]) { idx_bit = 0; while (idx_bit < 8) { if ((BITMAP_MASK << idx_bit) & vaddr_btmp[idx_byte]) { prog_vaddr = (idx_byte * 8 + idx_bit) * PG_SIZE + vaddr_start;
memcpy(buf_page, (void *)prog_vaddr, PG_SIZE);
page_dir_activate(child_thread); get_a_page_without_opvaddrbitmap(PF_USER, prog_vaddr);
memcpy((void *)prog_vaddr, buf_page, PG_SIZE);
page_dir_activate(parent_thread); } idx_bit++; } } idx_byte++; } }
static int32_t build_child_stack(task_struct *child_thread) { intr_stack *intr_0_stack = (intr_stack *)((uint32_t)child_thread + PG_SIZE - sizeof(intr_stack)); intr_0_stack->eax = 0;
uint32_t *ret_addr_in_thread_stack = (uint32_t *)intr_0_stack - 1;
uint32_t *ebp_ptr_in_thread_stack = (uint32_t *)intr_0_stack - 5;
*ret_addr_in_thread_stack = (uint32_t)intr_exit;
child_thread->self_kstack = ebp_ptr_in_thread_stack; return 0; }
static int32_t copy_process(task_struct *child_thread, task_struct *parent_thread) { void *buf_page = get_kernel_pages(1); if (buf_page == NULL) { return -1; }
if (copy_pcb_vaddrbitmap_stack0(child_thread, parent_thread) == -1) { return -1; }
child_thread->pgdir = create_page_dir(); if (child_thread->pgdir == NULL) { return -1; }
copy_body_stack3(child_thread, parent_thread, buf_page);
build_child_stack(child_thread);
update_inode_open_cnts(child_thread);
mfree_page(PF_KERNEL, buf_page, 1); return 0; }
pid_t sys_fork(void) { task_struct *parent_thread = running_thread(); task_struct *child_thread = get_kernel_pages(1); if (child_thread == NULL) { return -1; } ASSERT(INTR_OFF == intr_get_status() && parent_thread->pgdir != NULL);
if (copy_process(child_thread, parent_thread) == -1) { return -1; }
ASSERT(!elem_find(&thread_ready_list, &child_thread->general_tag)); list_append(&thread_ready_list, &child_thread->general_tag); ASSERT(!elem_find(&thread_all_list, &child_thread->all_list_tag)); list_append(&thread_all_list, &child_thread->all_list_tag);
return child_thread->pid; }
|
执行进程就是将该进程加入就绪队列.
script>