《操作系统》的实验代码。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

411 lines
14 KiB

  1. #include <proc.h>
  2. #include <kmalloc.h>
  3. #include <string.h>
  4. #include <sync.h>
  5. #include <pmm.h>
  6. #include <error.h>
  7. #include <sched.h>
  8. #include <elf.h>
  9. #include <vmm.h>
  10. #include <trap.h>
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <assert.h>
  14. /* ------------- process/thread mechanism design&implementation -------------
  15. (an simplified Linux process/thread mechanism )
  16. introduction:
  17. ucore implements a simple process/thread mechanism. process contains the independent memory sapce, at least one threads
  18. for execution, the kernel data(for management), processor state (for context switch), files(in lab6), etc. ucore needs to
  19. manage all these details efficiently. In ucore, a thread is just a special kind of process(share process's memory).
  20. ------------------------------
  21. process state : meaning -- reason
  22. PROC_UNINIT : uninitialized -- alloc_proc
  23. PROC_SLEEPING : sleeping -- try_free_pages, do_wait, do_sleep
  24. PROC_RUNNABLE : runnable(maybe running) -- proc_init, wakeup_proc,
  25. PROC_ZOMBIE : almost dead -- do_exit
  26. -----------------------------
  27. process state changing:
  28. alloc_proc RUNNING
  29. + +--<----<--+
  30. + + proc_run +
  31. V +-->---->--+
  32. PROC_UNINIT -- proc_init/wakeup_proc --> PROC_RUNNABLE -- try_free_pages/do_wait/do_sleep --> PROC_SLEEPING --
  33. A + +
  34. | +--- do_exit --> PROC_ZOMBIE +
  35. + +
  36. -----------------------wakeup_proc----------------------------------
  37. -----------------------------
  38. process relations
  39. parent: proc->parent (proc is children)
  40. children: proc->cptr (proc is parent)
  41. older sibling: proc->optr (proc is younger sibling)
  42. younger sibling: proc->yptr (proc is older sibling)
  43. -----------------------------
  44. related syscall for process:
  45. SYS_exit : process exit, -->do_exit
  46. SYS_fork : create child process, dup mm -->do_fork-->wakeup_proc
  47. SYS_wait : wait process -->do_wait
  48. SYS_exec : after fork, process execute a program -->load a program and refresh the mm
  49. SYS_clone : create child thread -->do_fork-->wakeup_proc
  50. SYS_yield : process flag itself need resecheduling, -- proc->need_sched=1, then scheduler will rescheule this process
  51. SYS_sleep : process sleep -->do_sleep
  52. SYS_kill : kill process -->do_kill-->proc->flags |= PF_EXITING
  53. -->wakeup_proc-->do_wait-->do_exit
  54. SYS_getpid : get the process's pid
  55. */
  56. // the process set's list
  57. list_entry_t proc_list;
  58. #define HASH_SHIFT 10
  59. #define HASH_LIST_SIZE (1 << HASH_SHIFT)
  60. #define pid_hashfn(x) (hash32(x, HASH_SHIFT))
  61. // has list for process set based on pid
  62. static list_entry_t hash_list[HASH_LIST_SIZE];
  63. // idle proc
  64. struct proc_struct *idleproc = NULL;
  65. // init proc
  66. struct proc_struct *initproc = NULL;
  67. // current proc
  68. struct proc_struct *current = NULL;
  69. static int nr_process = 0;
  70. void kernel_thread_entry(void);
  71. void forkrets(struct trapframe *tf);
  72. void switch_to(struct context *from, struct context *to);
  73. // alloc_proc - alloc a proc_struct and init all fields of proc_struct
  74. static struct proc_struct *
  75. alloc_proc(void) {
  76. struct proc_struct *proc = kmalloc(sizeof(struct proc_struct));
  77. if (proc != NULL) {
  78. //LAB4:EXERCISE1 YOUR CODE
  79. /*
  80. * below fields in proc_struct need to be initialized
  81. * enum proc_state state; // Process state
  82. * int pid; // Process ID
  83. * int runs; // the running times of Proces
  84. * uintptr_t kstack; // Process kernel stack
  85. * volatile bool need_resched; // bool value: need to be rescheduled to release CPU?
  86. * struct proc_struct *parent; // the parent process
  87. * struct mm_struct *mm; // Process's memory management field
  88. * struct context context; // Switch here to run process
  89. * struct trapframe *tf; // Trap frame for current interrupt
  90. * uintptr_t cr3; // CR3 register: the base addr of Page Directroy Table(PDT)
  91. * uint32_t flags; // Process flag
  92. * char name[PROC_NAME_LEN + 1]; // Process name
  93. */
  94. proc->state = PROC_UNINIT;
  95. proc->pid = -1;
  96. proc->runs = 0;
  97. proc->kstack = 0;
  98. proc->need_resched = 0;
  99. proc->parent = NULL;
  100. proc->mm = NULL;
  101. memset(&(proc->context), 0, sizeof(struct context));
  102. proc->tf = NULL;
  103. proc->cr3 = boot_cr3;
  104. proc->flags = 0;
  105. memset(proc->name, 0, PROC_NAME_LEN);
  106. }
  107. return proc;
  108. }
  109. // set_proc_name - set the name of proc
  110. char *
  111. set_proc_name(struct proc_struct *proc, const char *name) {
  112. memset(proc->name, 0, sizeof(proc->name));
  113. return memcpy(proc->name, name, PROC_NAME_LEN);
  114. }
  115. // get_proc_name - get the name of proc
  116. char *
  117. get_proc_name(struct proc_struct *proc) {
  118. static char name[PROC_NAME_LEN + 1];
  119. memset(name, 0, sizeof(name));
  120. return memcpy(name, proc->name, PROC_NAME_LEN);
  121. }
  122. // get_pid - alloc a unique pid for process
  123. static int
  124. get_pid(void) {
  125. static_assert(MAX_PID > MAX_PROCESS);
  126. struct proc_struct *proc;
  127. list_entry_t *list = &proc_list, *le;
  128. static int next_safe = MAX_PID, last_pid = MAX_PID;
  129. if (++ last_pid >= MAX_PID) {
  130. last_pid = 1;
  131. goto inside;
  132. }
  133. if (last_pid >= next_safe) {
  134. inside:
  135. next_safe = MAX_PID;
  136. repeat:
  137. le = list;
  138. while ((le = list_next(le)) != list) {
  139. proc = le2proc(le, list_link);
  140. if (proc->pid == last_pid) {
  141. if (++ last_pid >= next_safe) {
  142. if (last_pid >= MAX_PID) {
  143. last_pid = 1;
  144. }
  145. next_safe = MAX_PID;
  146. goto repeat;
  147. }
  148. }
  149. else if (proc->pid > last_pid && next_safe > proc->pid) {
  150. next_safe = proc->pid;
  151. }
  152. }
  153. }
  154. return last_pid;
  155. }
  156. // proc_run - make process "proc" running on cpu
  157. // NOTE: before call switch_to, should load base addr of "proc"'s new PDT
  158. void
  159. proc_run(struct proc_struct *proc) {
  160. if (proc != current) {
  161. bool intr_flag;
  162. struct proc_struct *prev = current, *next = proc;
  163. local_intr_save(intr_flag);
  164. {
  165. current = proc;
  166. load_esp0(next->kstack + KSTACKSIZE);
  167. lcr3(next->cr3);
  168. switch_to(&(prev->context), &(next->context));
  169. }
  170. local_intr_restore(intr_flag);
  171. }
  172. }
  173. // forkret -- the first kernel entry point of a new thread/process
  174. // NOTE: the addr of forkret is setted in copy_thread function
  175. // after switch_to, the current proc will execute here.
  176. static void
  177. forkret(void) {
  178. forkrets(current->tf);
  179. }
  180. // hash_proc - add proc into proc hash_list
  181. static void
  182. hash_proc(struct proc_struct *proc) {
  183. list_add(hash_list + pid_hashfn(proc->pid), &(proc->hash_link));
  184. }
  185. // find_proc - find proc frome proc hash_list according to pid
  186. struct proc_struct *
  187. find_proc(int pid) {
  188. if (0 < pid && pid < MAX_PID) {
  189. list_entry_t *list = hash_list + pid_hashfn(pid), *le = list;
  190. while ((le = list_next(le)) != list) {
  191. struct proc_struct *proc = le2proc(le, hash_link);
  192. if (proc->pid == pid) {
  193. return proc;
  194. }
  195. }
  196. }
  197. return NULL;
  198. }
  199. // kernel_thread - create a kernel thread using "fn" function
  200. // NOTE: the contents of temp trapframe tf will be copied to
  201. // proc->tf in do_fork-->copy_thread function
  202. int
  203. kernel_thread(int (*fn)(void *), void *arg, uint32_t clone_flags) {
  204. struct trapframe tf;
  205. memset(&tf, 0, sizeof(struct trapframe));
  206. tf.tf_cs = KERNEL_CS;
  207. tf.tf_ds = tf.tf_es = tf.tf_ss = KERNEL_DS;
  208. tf.tf_regs.reg_ebx = (uint32_t)fn;
  209. tf.tf_regs.reg_edx = (uint32_t)arg;
  210. tf.tf_eip = (uint32_t)kernel_thread_entry;
  211. return do_fork(clone_flags | CLONE_VM, 0, &tf);
  212. }
  213. // setup_kstack - alloc pages with size KSTACKPAGE as process kernel stack
  214. static int
  215. setup_kstack(struct proc_struct *proc) {
  216. struct Page *page = alloc_pages(KSTACKPAGE);
  217. if (page != NULL) {
  218. proc->kstack = (uintptr_t)page2kva(page);
  219. return 0;
  220. }
  221. return -E_NO_MEM;
  222. }
  223. // put_kstack - free the memory space of process kernel stack
  224. static void
  225. put_kstack(struct proc_struct *proc) {
  226. free_pages(kva2page((void *)(proc->kstack)), KSTACKPAGE);
  227. }
  228. // copy_mm - process "proc" duplicate OR share process "current"'s mm according clone_flags
  229. // - if clone_flags & CLONE_VM, then "share" ; else "duplicate"
  230. static int
  231. copy_mm(uint32_t clone_flags, struct proc_struct *proc) {
  232. assert(current->mm == NULL);
  233. /* do nothing in this project */
  234. return 0;
  235. }
  236. // copy_thread - setup the trapframe on the process's kernel stack top and
  237. // - setup the kernel entry point and stack of process
  238. static void
  239. copy_thread(struct proc_struct *proc, uintptr_t esp, struct trapframe *tf) {
  240. proc->tf = (struct trapframe *)(proc->kstack + KSTACKSIZE) - 1;
  241. *(proc->tf) = *tf;
  242. proc->tf->tf_regs.reg_eax = 0;
  243. proc->tf->tf_esp = esp;
  244. proc->tf->tf_eflags |= FL_IF;
  245. proc->context.eip = (uintptr_t)forkret;
  246. proc->context.esp = (uintptr_t)(proc->tf);
  247. }
  248. /* do_fork - parent process for a new child process
  249. * @clone_flags: used to guide how to clone the child process
  250. * @stack: the parent's user stack pointer. if stack==0, It means to fork a kernel thread.
  251. * @tf: the trapframe info, which will be copied to child process's proc->tf
  252. */
  253. int
  254. do_fork(uint32_t clone_flags, uintptr_t stack, struct trapframe *tf) {
  255. int ret = -E_NO_FREE_PROC;
  256. struct proc_struct *proc;
  257. if (nr_process >= MAX_PROCESS) {
  258. goto fork_out;
  259. }
  260. ret = -E_NO_MEM;
  261. //LAB4:EXERCISE2 YOUR CODE
  262. /*
  263. * Some Useful MACROs, Functions and DEFINEs, you can use them in below implementation.
  264. * MACROs or Functions:
  265. * alloc_proc: create a proc struct and init fields (lab4:exercise1)
  266. * setup_kstack: alloc pages with size KSTACKPAGE as process kernel stack
  267. * copy_mm: process "proc" duplicate OR share process "current"'s mm according clone_flags
  268. * if clone_flags & CLONE_VM, then "share" ; else "duplicate"
  269. * copy_thread: setup the trapframe on the process's kernel stack top and
  270. * setup the kernel entry point and stack of process
  271. * hash_proc: add proc into proc hash_list
  272. * get_pid: alloc a unique pid for process
  273. * wakeup_proc: set proc->state = PROC_RUNNABLE
  274. * VARIABLES:
  275. * proc_list: the process set's list
  276. * nr_process: the number of process set
  277. */
  278. // 1. call alloc_proc to allocate a proc_struct
  279. // 2. call setup_kstack to allocate a kernel stack for child process
  280. // 3. call copy_mm to dup OR share mm according clone_flag
  281. // 4. call copy_thread to setup tf & context in proc_struct
  282. // 5. insert proc_struct into hash_list && proc_list
  283. // 6. call wakeup_proc to make the new child process RUNNABLE
  284. // 7. set ret vaule using child proc's pid
  285. if ((proc = alloc_proc()) == NULL) {
  286. goto fork_out;
  287. }
  288. proc->parent = current;
  289. if (setup_kstack(proc) != 0) {
  290. goto bad_fork_cleanup_proc;
  291. }
  292. if (copy_mm(clone_flags, proc) != 0) {
  293. goto bad_fork_cleanup_kstack;
  294. }
  295. copy_thread(proc, stack, tf);
  296. bool intr_flag;
  297. local_intr_save(intr_flag);
  298. {
  299. proc->pid = get_pid();
  300. hash_proc(proc);
  301. list_add(&proc_list, &(proc->list_link));
  302. nr_process ++;
  303. }
  304. local_intr_restore(intr_flag);
  305. wakeup_proc(proc);
  306. ret = proc->pid;
  307. fork_out:
  308. return ret;
  309. bad_fork_cleanup_kstack:
  310. put_kstack(proc);
  311. bad_fork_cleanup_proc:
  312. kfree(proc);
  313. goto fork_out;
  314. }
  315. // do_exit - called by sys_exit
  316. // 1. call exit_mmap & put_pgdir & mm_destroy to free the almost all memory space of process
  317. // 2. set process' state as PROC_ZOMBIE, then call wakeup_proc(parent) to ask parent reclaim itself.
  318. // 3. call scheduler to switch to other process
  319. int
  320. do_exit(int error_code) {
  321. panic("process exit!!.\n");
  322. }
  323. // init_main - the second kernel thread used to create user_main kernel threads
  324. static int
  325. init_main(void *arg) {
  326. cprintf("this initproc, pid = %d, name = \"%s\"\n", current->pid, get_proc_name(current));
  327. cprintf("To U: \"%s\".\n", (const char *)arg);
  328. cprintf("To U: \"en.., Bye, Bye. :)\"\n");
  329. return 0;
  330. }
  331. // proc_init - set up the first kernel thread idleproc "idle" by itself and
  332. // - create the second kernel thread init_main
  333. void
  334. proc_init(void) {
  335. int i;
  336. list_init(&proc_list);
  337. for (i = 0; i < HASH_LIST_SIZE; i ++) {
  338. list_init(hash_list + i);
  339. }
  340. if ((idleproc = alloc_proc()) == NULL) {
  341. panic("cannot alloc idleproc.\n");
  342. }
  343. idleproc->pid = 0;
  344. idleproc->state = PROC_RUNNABLE;
  345. idleproc->kstack = (uintptr_t)bootstack;
  346. idleproc->need_resched = 1;
  347. set_proc_name(idleproc, "idle");
  348. nr_process ++;
  349. current = idleproc;
  350. int pid = kernel_thread(init_main, "Hello world!!", 0);
  351. if (pid <= 0) {
  352. panic("create init_main failed.\n");
  353. }
  354. initproc = find_proc(pid);
  355. set_proc_name(initproc, "init");
  356. assert(idleproc != NULL && idleproc->pid == 0);
  357. assert(initproc != NULL && initproc->pid == 1);
  358. }
  359. // cpu_idle - at the end of kern_init, the first kernel thread idleproc will do below works
  360. void
  361. cpu_idle(void) {
  362. while (1) {
  363. if (current->need_resched) {
  364. schedule();
  365. }
  366. }
  367. }