{"id":26907546,"url":"https://github.com/lpyleo/pintos","last_synced_at":"2026-05-03T02:42:11.543Z","repository":{"id":212897904,"uuid":"475282562","full_name":"lpyleo/pintos","owner":"lpyleo","description":"操作系统课程设计 pintos","archived":false,"fork":false,"pushed_at":"2022-03-29T04:44:31.000Z","size":882,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2023-12-18T06:20:29.146Z","etag":null,"topics":["c","pintos","shell"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lpyleo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-03-29T04:42:02.000Z","updated_at":"2022-05-08T05:52:50.000Z","dependencies_parsed_at":"2023-12-17T06:30:10.749Z","dependency_job_id":null,"html_url":"https://github.com/lpyleo/pintos","commit_stats":null,"previous_names":["lpyleo/pintos"],"tags_count":0,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lpyleo%2Fpintos","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lpyleo%2Fpintos/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lpyleo%2Fpintos/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lpyleo%2Fpintos/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lpyleo","download_url":"https://codeload.github.com/lpyleo/pintos/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246635953,"owners_count":20809331,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","pintos","shell"],"created_at":"2025-04-01T11:56:55.200Z","updated_at":"2026-05-03T02:42:06.523Z","avatar_url":"https://github.com/lpyleo.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OS课程设计\n\n## Project 1 Shell\n\n### Add suport for cd and pwd\n\n#### 1.cd实现\n\n对解析的cmdline参数分析，如果为“”或者“～”则使用`getenv`获取HOME路径作为目标路径，否则参数即位目标路径。调用`chdir`函数访问目标路径，若失败则输出相关内容，成功则通过`getcwd`获取当前路径并输出\n\n```c++\nint cmd_cd(struct tokens *tokens) {\n  char *targ_dir = tokens_get_token(tokens, 1);\n  if (targ_dir == NULL || strcmp(targ_dir, \"~\") == 0) {\n    targ_dir = getenv(\"HOME\");\n    if (targ_dir == NULL) {\n      printf(\"Error changing directory\\n\");\n      return -1;\n    }\n  }\n  int success = chdir(targ_dir);\n  if (success == -1) {\n    printf(\"Error changing directory\\n\");\n  }\n  char cwd[MAX_SIZE];\n  if (getcwd(cwd, sizeof(cwd)) != NULL) {\n        printf(\"%s\\n\", cwd);\n    }\n  return success;\n}\n```\n\n#### 2.pwd实现\n\n通过`getcwd`获取当前路径并输出\n\n```c++\nint cmd_pwd(unused struct tokens *tokens) {\n  char cur_dir[4096];\n  if (getcwd(cur_dir, 4096) == NULL) {\n    printf(\"Error printing current directory\\n\");\n  }\n  printf(\"%s\\n\", cur_dir);\n  return 1;\n}\n```\n\n\n\n### Program execution\n\n通过`fork`函数开启一个新线程来执行程序，对cmdline解析后的参数进行分析，如果存在\u003e或者\u003c则进行重定向，否则就向`execv`指定指令和参数进行执行。\n\n```c++\nint run_program(struct tokens *tokens) {\n  int length = tokens_get_length(tokens);\n  if (length == 0) {\n    // user pressed return\n    return 0;\n  }\n  int run_bg = length \u003e 1 \u0026\u0026 strcmp(tokens_get_token(tokens, length - 1), \"\u0026\") == 0;\n  int pid = fork();\n  int status = 0;\n  if (pid == 0) {\n    // process tokens into args array for exec, and redirect stdin/stdout\n    char *args[length + 1];\n    int redirect_stdin = 0, redirect_stdout = 0, num_args = 0;\n    for (int i = 0; i \u003c length; i++) {\n      char *token = tokens_get_token(tokens, i);\n      if (redirect_stdin) {\n        int fd = open(token, O_RDONLY);\n        if (redirect(fd, STDIN_FILENO) == -1) {\n          printf(\"Error with input %s\\n\", token);\n          exit(-1);\n        }\n        redirect_stdin = 0;\n      } else if (redirect_stdout) {\n        int fd = creat(token, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);\n        if (redirect(fd, STDOUT_FILENO) == -1) {\n          printf(\"Error with input %s\\n\", token);\n          exit(-1);\n        }\n        redirect_stdout = 0;\n      } else if (strcmp(token, \"\u003c\") == 0) {\n        redirect_stdin = 1;\n      } else if (strcmp(token, \"\u003e\") == 0) {\n        redirect_stdout = 1;\n      } else if (!(i == length - 1 \u0026\u0026 run_bg)) {\n        args[num_args++] = token;\n      }\n    }\n    args[num_args] = (char *) NULL;\n    char *prog = args[0];\n\n    // move process to own process group\n    setpgid(0, 0);\n    if (!run_bg) {\n      // move to foreground if input doesn't end with \"\u0026\"\n      tcsetpgrp(shell_terminal, getpgrp());\n    }\n\n    // override ignored signal handlers from shell to default signal handlers\n    for (int i = 0; i \u003c sizeof(ignore_signals) / sizeof(int); i++) {\n      signal(ignore_signals[i], SIG_DFL);\n    }\n    // execute new program in child process, searching thru path env var if needed\n    if (execv(prog, args) == -1 \u0026\u0026 run_program_thru_path(prog, args) == -1) {\n      printf(\"Error executing program %s\\n\", prog);\n      exit(-1);\n    }\n  } else {\n    int no_hang = run_bg ? WNOHANG : 0;\n    waitpid(pid, \u0026status, WUNTRACED|no_hang);\n    tcsetpgrp(shell_terminal, shell_pgid);\n  }\n  return status;\n}\n```\n\n### Path resolution\n\n对于需要环境变量的命令，在执行`run_program`时通过`run_program_thru_path`执行命令，函数内通过`getenv`获取PATH环境变量地址，通过遍历尝试是否存在此命令，存在则通过`execv`指定命令地址和参数运行。\n\n```c++\n\nint run_program_thru_path(char *prog, char *args[]) {\n  char *PATH = getenv(\"PATH\");\n  if (PATH == NULL) {\n    return -1;\n  }\n  char prog_path[4096];\n  char *path_dir = strtok(PATH, \":\");\n  while (path_dir != NULL) {\n    sprintf(prog_path, \"%s/%s\", path_dir, prog);\n    if (access(prog_path, F_OK) != -1) {\n      return execv(prog_path, args);\n    }\n    path_dir = strtok(NULL, \":\");\n  }\n  return -1;\n}\n```\n\n### Input/Output Redirection\n\n在`run_program`函数中对cmdline的参数遍历，查询是否存在\u003e或者\u003c符号，存在则进行重定向。\n\n```c++\nint redirect_stdin = 0, redirect_stdout = 0, num_args = 0;\n    for (int i = 0; i \u003c length; i++) {\n      char *token = tokens_get_token(tokens, i);\n      if (redirect_stdin) {\n        int fd = open(token, O_RDONLY);\n        if (redirect(fd, STDIN_FILENO) == -1) {\n          printf(\"Error with input %s\\n\", token);\n          exit(-1);\n        }\n        redirect_stdin = 0;\n      } else if (redirect_stdout) {\n        int fd = creat(token, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);\n        if (redirect(fd, STDOUT_FILENO) == -1) {\n          printf(\"Error with input %s\\n\", token);\n          exit(-1);\n        }\n        redirect_stdout = 0;\n      } else if (strcmp(token, \"\u003c\") == 0) {\n        redirect_stdin = 1;\n      } else if (strcmp(token, \"\u003e\") == 0) {\n        redirect_stdout = 1;\n      } else if (!(i == length - 1 \u0026\u0026 run_bg)) {\n        args[num_args++] = token;\n      }\n    }\n```\n\n`redirect`函数中通过调用`dup2`实现重定向功能\n\n```c++\nint redirect(int old_fd, int new_fd) {\n  if (old_fd == -1 || dup2(old_fd, new_fd) == -1 || close(old_fd) == -1) {\n    return -1;\n  }\n  return 1;\n}\n```\n\n### Signal Handling and Terminal Control\n\n使用`signal`函数对信号量进行控制，在初始化Shell时忽略信号量\n\n```c++\n/* Intialization procedures for this shell */\nvoid init_shell() {\n  /* Our shell is connected to standard input. */\n  shell_terminal = STDIN_FILENO;\n\n  /* Check if we are running interactively */\n  shell_is_interactive = isatty(shell_terminal);\n\n  if (shell_is_interactive) {\n    /* If the shell is not currently in the foreground, we must pause the shell until it becomes a\n     * foreground process. We use SIGTTIN to pause the shell. When the shell gets moved to the\n     * foreground, we'll receive a SIGCONT. */\n    while (tcgetpgrp(shell_terminal) != (shell_pgid = getpgrp()))\n      kill(-shell_pgid, SIGTTIN);\n\n    /* Saves the shell's process id */\n    shell_pgid = getpid();\n\n    /* Take control of the terminal */\n    tcsetpgrp(shell_terminal, shell_pgid);\n\n    /* Save the current termios to a variable, so it can be restored later. */\n    tcgetattr(shell_terminal, \u0026shell_tmodes);\n  }\n  //忽略信号（SIG_IGN）：忽略信号，即使没有意义，代码执行仍将继续。\n  for (int i = 0; i \u003c sizeof(ignore_signals) / sizeof(int); i++) {\n    signal(ignore_signals[i], SIG_IGN);\n  }\n}\n```\n\n在使用`run_program`调用`fork`创建新的线程时，在新线程内打开信号量默认处理方式\n\n```c++\nint run_program(struct tokens *tokens) {\n  int length = tokens_get_length(tokens);\n  if (length == 0) {\n    // user pressed return\n    return 0;\n  }\n  int run_bg = length \u003e 1 \u0026\u0026 strcmp(tokens_get_token(tokens, length - 1), \"\u0026\") == 0;\n  int pid = fork();\n  int status = 0;\n  if (pid == 0) {\n    ···\n    for (int i = 0; i \u003c sizeof(ignore_signals) / sizeof(int); i++) {\n      signal(ignore_signals[i], SIG_DFL);\n    }\n    ···\n  } else {\n    int no_hang = run_bg ? WNOHANG : 0;\n    waitpid(pid, \u0026status, WUNTRACED|no_hang);\n    tcsetpgrp(shell_terminal, shell_pgid);\n  }\n  return status;\n}\n\n```\n\n### Background processing\n\n使用系统调用`wait`函数一般用在父进程中等待回收子进程的资源，而防止僵尸进程的产生。\n\n```c++\nint cmd_wait(unused struct tokens *tokens) {\n  int status, pid;\n  while ((pid = wait(\u0026status))) {\n    if (pid == -1) {\n      break;\n    }\n  }\n  return 1;\n}\n```\n\n## Project 2 THREADS\n\n### 一. 设计概览\n\npintos 的第一个项目是实现线程，主要实现了以下部分:\n\n* 修改 timer_sleep 函数，设置thread阻塞时间。\n* 实现优先级调度，完成抢占机制功能和优先级捐赠功能。 \n* 实现多级反馈调度，更新线程的优先级。\n\n### 二. 设计细节\n\n#### 1. Timer_sleep\n\n基本思想：\n\n设置阻塞时间，阻塞线程\n\n结构设计：\n\n```c++\nstruct thread\n{\n    ···\n    int64_t blocking_ticks;                /* Ticks that the thread to block. */\n    ···\n}; \nstatic void thread_blocking_ticks_handler(struct thread *t, void *args UNUSED);\n```\n\n1. 在 `thread` 类中添加成员变量 `blocking_ticks` 来记录线程的剩余阻塞时间。\n2. 增加 `thread_blocking_ticks_handler` 函数用来更新 `blocking_ticks`。\n\n算法设计：\n调用 `timer_sleep` 函数时设置阻塞时间把线程阻塞， 在系统自身的`timer_interrupt`函数中加入对线程状态的检测函数`thread_timer`。\n\n```c++\nvoid timer_sleep(int64_t ticks)\n{\n  if (ticks \u003c= 0)\n  {\n    return;\n  }\n  ASSERT(intr_get_level() == INTR_ON);\n  enum intr_level old_level = intr_disable();\n  struct thread *current_thread = thread_current();\n  current_thread-\u003eblocking_ticks = ticks;\n  thread_block();\n  intr_set_level(old_level);\n}\nstatic void\ntimer_interrupt(struct intr_frame *args UNUSED)\n{\n  ticks++;\n  enum intr_level old_level = intr_disable();\n  thread_timer(timer_ticks() % TIMER_FREQ == 0);\n  intr_set_level(old_level);\n  thread_tick ();\n}\n```\n\n每次检测时通过`thread_foreach`调用 `thread_blocking_ticks_handler` 函数更新所有线程的`blocking_ticks`，若 `blocking_ticks` 为零则唤醒这个线程。\n\n```c++\nvoid\nthread_timer(bool full_second){\n  if (thread_mlfqs){\n    thread_add_recent_cpu();\n    if (full_second){\n      thread_update_load_avg();\n      thread_foreach(thread_update_recent_cpu, NULL);\n      thread_foreach(thread_update_priority, NULL);\n      thread_ready_list_sort();\n    }\n  }\n  thread_foreach(thread_blocking_ticks_handler, NULL);\n}\n```\n\n\n\n#### 2. Priority\n\n1）Preemption\n\n基本思想：\n维护就绪队列为一个优先级队列**，**且正在运行的进程的优先级是最高的。\n\n结构设计：\n\n```c++\nbool thread_priority_cmp (const struct list_elem *a_, const struct list_elem *b_,\n                           void *aux UNUSED);\nvoid thread_insert_ready_list (struct list_elem *elem);\n```\n\n1. 增加`thread_priority_cmp`函数比较优先级大小\n2. 增加`thread_insert_ready_list`插入线程到就绪队列\n\n算法设计：\n\n通过获取`thread`结构体下的`priority`变量进行比较\n\n```c++\nbool\nthread_priority_cmp (const struct list_elem *a_, const struct list_elem *b_,\n                      void *aux UNUSED)\n{\n  const struct thread *a = list_entry (a_, struct thread, elem);\n  const struct thread *b = list_entry (b_, struct thread, elem);\n\n  return thread_get_certain_priority (a) \u003e thread_get_certain_priority (b);\n}\n```\n\n使用list的`list_instert_ordered`函数有序插入线程，通过`thread_priority_cmp`函数比较线程优先级。\n\n```c++\nvoid\nthread_insert_ready_list (struct list_elem *elem)\n{\n  if (!list_empty (\u0026ready_list))\n    list_insert_ordered (\u0026ready_list, elem, thread_priority_cmp, NULL);\n  else\n    list_push_back (\u0026ready_list, elem);\n}\n```\n\n考虑一个进程在以下两种情况下进入就绪队列:\n\n1. `thread_unblock`\n\n   ```c++\n   void\n   thread_unblock (struct thread *t)\n   {\n     enum intr_level old_level;\n   \n     ASSERT (is_thread (t));\n   \n     old_level = intr_disable ();\n     ASSERT (t-\u003estatus == THREAD_BLOCKED);\n     thread_insert_ready_list (\u0026t-\u003eelem);\n     t-\u003estatus = THREAD_READY;\n     intr_set_level (old_level);\n   }\n   ```\n\n2. `thread_yield`\n\n   ```c++\n   void\n   thread_yield (void)\n   {\n     struct thread *cur = thread_current ();\n     enum intr_level old_level;\n   \n     ASSERT (!intr_context ());\n   \n     old_level = intr_disable ();\n     if (cur != idle_thread)\n       thread_insert_ready_list (\u0026cur-\u003eelem);\n     cur-\u003estatus = THREAD_READY;\n     schedule ();\n     intr_set_level (old_level);\n   }\n   \n   ```\n\n因此在这两个函数内使用`thread_insert_ready_list`。\n\n\n\n2）Donation\n\n基本思想：\n当高优先级的线程因为低优先级线程占用资源而阻塞时，就将低优先级线程的优先级提升到等待它所占有资源的最高优先级线程的优先级。\n\n结构设计：\n\n```c++\nstruct lock\n  {\n    struct thread *holder;      /* Thread holding lock (for debugging). */\n    struct semaphore semaphore; /* Binary semaphore controlling access. */\n    int max_donate;             /* Max donation. */\n    struct list donaters;       /* Donater list. */\n    struct list_elem elem;      /* List element for lock list. */\n  };\nstruct thread\n{\n    ···\n    struct list lock_list;              /* Locks owned by this thread. */\n    int priority_to_set;                /* Priority to be set. */\n    int max_donate;                     /* Max Donation. */\n    struct thread *father;              /* Thread who locks this thread. */\n    struct list_elem donate_elem;       /* List element for donation list of locks. */\n    ···\n};\n```\n\n1. 在`look`类内增加整型`max_donate`记录最大捐赠的优先级，链表`donaters`,`elem`记录捐赠队列和等待队列。\n2. 在 `thread `类加入`lock_list`队列记录获得的锁，`priority_to_set`整型记录临时优先级，整型`max_donate`记录最大捐赠的优先级，`father`指针记录当前占用`lock`的线程，`donate_elem`记录捐赠队列\n3. 重写 `lock_acquire`, `lock_release` 函数修改最大捐赠值，并修改优先级。\n4. 增加 `thread_update_priority` 函数更新优先级。\n\n算法设计：\n\n1. 在一个线程申请获取一个锁的时候， 如果拥有这个锁的线程优先级比自己低就提高它的优先级，使用`father`指针记录当前占有锁的线程，通过`list_push_back`函数把新线程放入捐赠队列中，并且如果这个锁还被别的锁锁着， 将会递归地捐赠优先级即调用`notify_father`函数，。\n\n   ```c++\n   void\n   lock_acquire (struct lock *lock)\n   {\n     ASSERT (lock != NULL);\n     ASSERT (!intr_context ());\n     ASSERT (!lock_held_by_current_thread (lock));\n   \n     /* The lock has been locked, so deal with the waiting relation and donation stuff. */\n     if (lock-\u003eholder != NULL \u0026\u0026 thread_get_priority () \u003e lock-\u003eholder-\u003epriority)\n       {\n         int delta = thread_get_priority () - lock-\u003eholder-\u003epriority;\n         struct thread *t = thread_current ();\n         t-\u003efather = lock-\u003eholder;\n   \n         list_push_back (\u0026lock-\u003edonaters, \u0026t-\u003edonate_elem);\n   \n         if (delta \u003e lock-\u003emax_donate)\n           {\n             lock-\u003emax_donate = delta;\n           }\n   \n         if (delta \u003e lock-\u003eholder-\u003emax_donate)\n           {\n             lock-\u003eholder-\u003emax_donate = delta;\n             notify_father (lock-\u003eholder);\n           }\n   \n       }\n   \n     sema_down (\u0026lock-\u003esemaphore);\n     lock-\u003eholder = thread_current ();\n   \n     /* Got the new lock successfully. Initialize it. */\n     list_push_back (\u0026lock-\u003eholder-\u003elock_list, \u0026lock-\u003eelem);\n   }\n   ```\n\n2. 使用迭代法遍历，如果不存在`father`则停止，否则就判断当前线程被捐赠的最大优先级是否小于等待队列中的优先级，若持有锁的线程优先级低，则把等待队列中最高优先级的优先级捐赠给持有锁的当前线程。\n\n   ```c++\n   void\n   notify_father (struct thread *t)\n   {\n     if (t == NULL)\n       return;\n     struct thread *father = t-\u003efather;\n     if (father == NULL)\n       return;\n     int old_donate = father-\u003emax_donate;\n   \n     struct list_elem *el, *ed;\n     struct lock *l;\n     struct thread *tmp;\n     for (el = list_begin (\u0026father-\u003elock_list); el != list_end (\u0026father-\u003elock_list); el = list_next (el))\n       {\n         l = list_entry (el, struct lock, elem);\n         for (ed = list_begin (\u0026l-\u003edonaters); ed != list_end (\u0026l-\u003edonaters); ed = list_next (ed))\n           {\n             tmp = list_entry (ed, struct thread, donate_elem);\n             if (tmp-\u003epriority + tmp-\u003emax_donate - father-\u003epriority \u003e l-\u003emax_donate)\n               l-\u003emax_donate = tmp-\u003epriority + tmp-\u003emax_donate - father-\u003epriority;\n           }\n         if (l-\u003emax_donate \u003e father-\u003emax_donate)\n           father-\u003emax_donate = l-\u003emax_donate;\n       }\n     if (father-\u003emax_donate != old_donate)\n       notify_father (father);\n   }\n   \n   ```\n\n   \n\n3. 遍历捐赠队列，使其`father`均指向NULL,并清空此队列。在释放锁对一个锁优先级有改变的时候应考虑其余被捐赠优先级和当前优先级。为保证优先队列在释放资源后调用`thread_revolt`来激活线程。\n\n   ```c++\n   void\n   lock_release (struct lock *lock)\n   {\n     ASSERT (lock != NULL);\n     ASSERT (lock_held_by_current_thread (lock));\n   \n     struct thread *t = lock-\u003eholder;\n   \n     /* Cut down the donation. */\n     struct list_elem *e;\n     for (e = list_begin (\u0026lock-\u003edonaters); e != list_end (\u0026lock-\u003edonaters); e = list_next (e))\n       {\n         struct thread *son = list_entry (e, struct thread, donate_elem);\n         son-\u003efather = NULL;\n       }\n     while (!list_empty (\u0026lock-\u003edonaters))\n       list_pop_front (\u0026lock-\u003edonaters);\n     list_remove (\u0026lock-\u003eelem);\n   \n     lock-\u003emax_donate = 0;\n     lock-\u003eholder = NULL;\n     sema_up (\u0026lock-\u003esemaphore);\n   \n     /* Update the lock holder's donation information and notify its father if necessary. */\n     int old_donate = t-\u003emax_donate;\n     t-\u003emax_donate = 0;\n     for (e = list_begin (\u0026t-\u003elock_list); e != list_end (\u0026t-\u003elock_list); e = list_next (e))\n       {\n         struct lock *l = list_entry (e, struct lock, elem);\n         if (l-\u003emax_donate \u003e t-\u003emax_donate)\n           t-\u003emax_donate = l-\u003emax_donate;\n       }\n     if (t-\u003emax_donate != old_donate)\n       {\n         notify_father (t);\n       }\n   \n     if (t-\u003emax_donate == 0 \u0026\u0026 t-\u003epriority_to_set \u003e -1)\n       {\n         t-\u003epriority = t-\u003epriority_to_set;\n         t-\u003epriority_to_set = -1;\n       }\n     thread_revolt ();\n   }\n   ```\n\n   4.为保证优先级队列，比较当前线程和就绪队列第一个线程的优先级大小，如果当前执行优先级低则调用`thread_yield`让步。\n\n   ```c++\n   void\n   thread_revolt (void)\n   {\n     if (thread_current () != idle_thread \u0026\u0026\n         !list_empty (\u0026ready_list) \u0026\u0026\n         thread_get_priority () \u003c\n         thread_get_certain_priority (\n                 list_entry (list_begin (\u0026ready_list), struct thread, elem)))\n     {\n       thread_yield ();\n     }\n   }\n   ```\n\n   \n\n#### 3 BSD Scheduler\n\n基本思想：\n维护了64个队列， 每个队列对应一个优先级， 从PRI_MIN到PRI_MAX。每隔一定时间，通过一些公式计算来计算出线程当前的优先级， 系统调度的时候会从高优先级队列开始选择线程执行， 这里线程的优先级随着操作系统的运转数据而动态改变。\n\n结构设计：\n\n```\nstruct thread\n{\n    ···\n    int nice;                           /* The nice level of thread, the higher the lower priority */\n    fixed_point_t recent_cpu; /* Thread recent cpu usage */\n    ···\n}；\nint thread_get_nice (void);\nvoid thread_set_nice (int);\nint thread_get_recent_cpu (void);\nint thread_get_load_avg (void);\nstatic void thread_update_load_avg(void);\nstatic void thread_update_recent_cpu(struct thread*, void*);\nvoid thread_add_recent_cpu(void);\n```\n\n1. \n\n算法设计：\n 根据http://web.stanford.edu/class/cs140/projects/pintos/pintos_7.html#SEC131提供的BSD调度算法实现更新\n\n priority = PRI_MAX - (recent_cpu / 4) - (nice * 2)\n\n recent_cpu = (2*load_avg)/(2*load_avg + 1) * recent_cpu + nice\n\n load_avg = (59/60)*load_avg + (1/60)*ready_threads.\n\n1. 使用浮点类`fixed-point.h`，用于计算优先级。\n\n2. 通过`nice`和`recent_cpu`计算`load_avg`和`priority`。\n\n3. 增加`thread_set_nice`,`thread_get_nice`获取和设置nice值\n\n   ```c++\n   int\n   thread_get_nice (void)\n   {\n     struct thread *cur = thread_current();\n     return cur-\u003enice;\n   }\n   \n   void\n   thread_set_nice (int nice)\n   {\n     thread_current()-\u003enice = nice;\n     if(thread_mlfqs){\n       thread_update_priority(thread_current(), NULL);\n     }\n   \n   }\n   \n   ```\n\n4. 增加`thread_get_recent_cpu ()`，`thread_get_load_avg ()`获取recent_cpu,load_avg值\n\n   ```c++\n   /* Returns 100 times the current thread's recent_cpu value. */\n   int\n   thread_get_recent_cpu (void)\n   {\n     ASSERT(thread_mlfqs);\n     return fix_round(fix_scale(thread_current()-\u003erecent_cpu, 100));\n     \n   }\n   \n   \n   /* Returns 100 times the system load average. */\n   int\n   thread_get_load_avg (void)\n   {\n     ASSERT(thread_mlfqs);\n     return fix_round(fix_scale(load_avg, 100));\n   }\n   ```\n\n5. 在每一次时间中断时，`thread_timer`先是通过``thread_add_recent_cpu()`函数更新`recent_cpt`,之后再通过调用`thread_foreach`函数遍历所有线程执行以下`thread_update_load_avg()`, `thread_update_recent_cpu()`, `thread_update_priority`函数更新计算`recent_cpu`,`load_avg`,`priority`\n\n   ```c++\n   /* Add recent_cpu of the running thread by 1 per second */\n   void thread_add_recent_cpu(void)\n   {\n     ASSERT(thread_mlfqs);\n     struct thread* cur = thread_current();\n     if (cur != idle_thread)\n       cur-\u003erecent_cpu = fix_add(cur-\u003erecent_cpu, fix_int(1));\n     thread_update_priority(thread_current(), NULL);\n   }\n   /* Update load_avg\n    * load_avg = 59/60 * load_avg + 1/60 * ready_threads\n    * */\n   static void\n   thread_update_load_avg(void)\n   {\n   \n     ASSERT(thread_mlfqs);\n     int ready_threads = 0;\n     struct list_elem *e;\n     for (e = list_begin (\u0026all_list); e != list_end (\u0026all_list);\n          e = list_next (e))\n     {\n       struct thread *t = list_entry (e, struct thread, allelem);\n       if ((t-\u003estatus == THREAD_RUNNING || t-\u003estatus == THREAD_READY) \u0026\u0026 t != idle_thread)\n         ready_threads++;\n     }\n     load_avg = fix_add(fix_unscale(fix_scale(load_avg, 59), 60), fix_unscale(fix_int(ready_threads), 60));\n   }\n   \n   /* Update recent_cpu\n    * recent_cpu = recent_cpu * (load_avg * 2 / (load_avg * 2 + 1)) + nice\n    * Nice is thread's nice value\n    * */\n   static void\n   thread_update_recent_cpu(struct thread *t, void* aux UNUSED)\n   {\n     ASSERT(thread_mlfqs);\n     int load_avg2 = thread_get_load_avg() * 2;\n      t-\u003erecent_cpu = fix_add(fix_mul(fix_div(fix_scale(load_avg, 2), fix_add(fix_scale(load_avg, 2), fix_int(1))),t-\u003erecent_cpu), fix_int(t-\u003enice));\n   }\n   \n   /* Returns 100 times the current thread's recent_cpu value. */\n   int\n   thread_get_recent_cpu (void)\n   {\n     ASSERT(thread_mlfqs);\n     return fix_round(fix_scale(thread_current()-\u003erecent_cpu, 100));\n     \n   }\n   static void\n   thread_update_priority(struct thread* t, void *args UNUSED){\n     if(t == idle_thread)\n       return;\n     int recent_cpu_div4 = fix_trunc(fix_unscale(t-\u003erecent_cpu, 4));\n     t-\u003epriority =  PRI_MAX - recent_cpu_div4 - t-\u003enice * 2;\n   }\n   ```\n\n6. 由于增加了调度算法，所以需要增加`thread_get_certain_priority`判断调度算法，返回当前优先级\n\n   ``` c++\n   /* Returns the certain thread's priority. */\n   int\n   thread_get_certain_priority (const struct thread *t)\n   {\n     return t-\u003epriority + (thread_mlfqs ? 0 : t-\u003emax_donate) ;\n   }\n   ```\n\n   \n\n## Project 3 USERPROG\n\n### 一. 设计概览\n\npintos 的第二个项目是实现用户程序，主要实现了以下部分:\n\n- 读取cmdline，并执行对应的应用程序(包含传参)\n- 通过syscall处理用户的进程控制操作， 例如终止当前进程 建立新的进程等\n- 通过syscall处理用户的文件操作，例如新建文件 删除文件 读写文件等\n- bonus 部分 (禁止写入正在执行的可执行文件)\n\n### 二. 设计细节\n\n#### 1. Process Termination Messages\n\n基本思想：\n\n设置变量记录返回值,并在process_eixt时输出\n\n结构设计：\n\n```c++\nstruct thread\n{\n    ···\n    int return_value;     /* Return value of the thread (anyway, nobody cares)*/\n    ···\n}; \n```\n\n1. 在 `thread` 类中添加成员变量 `return_value `来记录结束返回值0或-1，表示是否为正常结束。\n2. 增加 `thread_exit_with_return_value` 函数来设置返回值。\n\n算法设计：\n在线程退出时通过判断是否存在异常，使用`thread_exit_with_return_value`来改变返回值并退出线程。\n\n```c++\n/* Terminate thread with a return value FINAL_VALUE */\nvoid\nthread_exit_with_return_value(struct intr_frame *f, int return_value) {\n  struct thread *cur = thread_current();\n  cur-\u003ereturn_value = return_value;\n  f-\u003eeax = (uint32_t)return_value;\n  thread_exit();\n}\n```\n\n\n\n#### 2. Argument Passing\n\n基本思想：\n\n传递参数主要分为两步，第一步是分离参数，第二步是把参数告诉要执行的程序。\n使用`strtok_r`函数可以完成实现`command line`中参数的切割，将一整行命令切成可执行文件名和参数。参数分离完成之后，采用栈来使得用户程序能够读取到这些参数。\n\n算法设计：\n\n以命令`/bin/ls -l foo bar`为例, 参数在栈中的位置如下所示:\n\n    Address         Name                Data            Type\n    0xbffffffc      argv[3][...]        bar             char[4]\n    0xbffffff8      argv[2][...]        foo             char[4]\n    0xbffffff5      argv[1][...]        -l              char[3]\n    0xbfffffed      argv[0][...]        /bin/ls         char[8]\n    0xbfffffec      word-align          0               uint8_t\n    0xbfffffe8      argv[4]             0               char *\n    0xbfffffe4      argv[3]             0xbffffffc      char *\n    0xbfffffe0      argv[2]             0xbffffff8      char *\n    0xbfffffdc      argv[1]             0xbffffff5      char *\n    0xbfffffd8      argv[0]             0xbfffffed      char *\n    0xbfffffd4      argv                0xbfffffd8      char **\n    0xbfffffd0      argc                4               int\n    0xbfffffcc      return address      0               void (*) ()\n\n只需要将分离出来的参数按照 从右到左、data-align-pointer-argv-argc-return_addr 的顺序压入栈中，程序就可以正确地读取到参数\n\n1.在`process_execute`中对`file_name_`使用`strtok_r`进行切割划分出第一个值，作为为`thread_name`，在创建线程时使用。\n\n```c++\ntid_t\nprocess_execute (const char *file_name_)\n{\n  /* Pudding by Chen Started */\n  char *file_name;\n  char *fn_copy;\n  tid_t tid;\n\n  /* Make a copy of FILE_NAME.\n     Otherwise there's a race between the caller and load(). */\n  file_name = palloc_get_page(0);\n  strlcpy (file_name, file_name_, PGSIZE);\n  /* Pudding by Chen Ended */\n\n  fn_copy = palloc_get_page (0);\n  if (fn_copy == NULL)\n    return TID_ERROR;\n  strlcpy (fn_copy, file_name, PGSIZE);\n\n\n  char *thread_name, *save_ptr;\n  thread_name = strtok_r (file_name, \" \", \u0026save_ptr);\n\n  /* Create a new thread to execute FILE_NAME. */\n  tid = thread_create (thread_name, PRI_DEFAULT, start_process, fn_copy);\n  if (tid == TID_ERROR)\n    palloc_free_page (fn_copy);\n  palloc_free_page(file_name); /* this is part of pudding */\n\n  list_push_back (\u0026thread_current ()-\u003echild_list,\n                  \u0026thread_get_child_message (tid)-\u003eelem);\n\n\n  return tid;\n}\n```\n\n\n\n2.在`start_process`中通过`load`函数给`esp`赋值分配空间。获取`esp`堆栈指针后将`cmd`逐步切割获取所有参数放入用户栈，临时变量`args`数组存储参数地址，并使用`top`整型记录参数个数存入`argc`。由于需要加入双字的对齐，根据原始`esp`指针位置和当前位置继续减小栈指针位置使其为4的倍数。先放入0到用户栈后，逆序放入`args`内的指针以及`top`变量值，最后放入0作为`return address`。把esp指针赋值给`if_.esp`以此完成参数的切割并入栈\n\n```c++\n/* A thread function that loads a user process and starts it\n   running. */\nstatic void\nstart_process (void *file_name_)\n{\n  char *file_name = file_name_;\n  struct intr_frame if_;\n  bool success;\n\n\n  char *text, *save_ptr;\n  text = strtok_r (file_name, \" \", \u0026save_ptr);\n\n  /* Initialize interrupt frame and load executable. */\n  memset (\u0026if_, 0, sizeof if_);\n  if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG;\n  if_.cs = SEL_UCSEG;\n  if_.eflags = FLAG_IF | FLAG_MBS;\n  success = load (text, \u0026if_.eip, \u0026if_.esp);\n\n  if (success)\n  {\n\n#define MAX_ARGC 32\n    char *esp = if_.esp;\n    char *args[MAX_ARGC], *arg;   /* Args is a stack.  */\n    int top = 0;\n\n    for (arg = text; arg != NULL; arg = strtok_r (NULL, \" \", \u0026save_ptr))\n    {\n      int l = strlen (arg);\n      esp -= l + 1;\n      strlcpy (esp, arg, l + 1);\n      args[top] = esp, top++;\n    }\n\n    while (((char *) if_.esp - esp) % 4 != 0) esp--;\n    esp -= 4;\n    *((int *) esp) = 0;  /* alignment */\n\n    int argc = top;\n    while (top \u003e 0)\n    {\n      esp -= 4;\n      --top, *((char **) esp) = args[top];\n    }\n    char **argv = (char **) esp;\n\n    esp -= 4;\n    *((char ***) esp) = argv;\n    esp -= 4;\n    *((int *) esp) = argc;\n    esp -= 4;\n    *((int *) esp) = 0;\n\n    if_.esp = esp;\n#undef MAX_ARGC\n\n  }\n  /* If load failed, quit. */\n  palloc_free_page (file_name);\n  if (!success)\n  {\n    thread_current ()-\u003emessage_to_grandpa-\u003eload_failed = true;\n    thread_current ()-\u003emessage_to_grandpa-\u003ereturn_value = -1;\n    thread_current ()-\u003ereturn_value = -1;\n  }\n  sema_up (thread_current ()-\u003emessage_to_grandpa-\u003esema_started);\n  if (!success)\n    thread_exit ();\n\n\n  /* Start the user process by simulating a return from an\n     interrupt, implemented by intr_exit (in\n     threads/intr-stubs.S).  Because intr_exit takes all of its\n     arguments on the stack in the form of a `struct intr_frame',\n     we just point the stack pointer (%esp) to our stack frame\n     and jump to it. */\n  asm volatile (\"movl %0, %%esp; jmp intr_exit\" : : \"g\" (\u0026if_) : \"memory\");\n  NOT_REACHED ();\n}\n```\n\n\n\n#### 3. System call\n\n基本思想：\n\n用户程序有时候需要“借用”内核的权限，来进行一些比较底层的操作，比如执行一个新的程序、读写I\\O等等。Syscall 提供了一个给用户程序内核权限的接口，并且要进行严格的检查以保证用户程序的所有操作都是合法的。\n\n结构设计：\n\n```c++\nstatic struct lock filesys_lock;\n```\n\n定义filesys_lock变量为文件添加锁预防读写冲突。\n\n```c++\nstruct child_message\n{\n    struct thread *tchild;              /* Thread pointer to the child. */\n    tid_t tid;                          /* Thread ID. */\n    bool exited;                        /* If syscall exit() is called. */\n    bool terminated;                    /* If the child finishes running. */\n    bool load_failed;                   /* If the child has a load fail. */\n    int return_value;                   /* Return value. */\n    struct semaphore *sema_finished;    /* Semaphore to finish. */\n    struct semaphore *sema_started;     /* Semaphore to finish loading. */\n    struct list_elem elem;              /* List element for grandpa's child list. */\n    struct list_elem allelem;           /* List element for global child list. */\n};\n```\n\n定义`child_message`结构体记录子进程的相关信息，方便父进程对其的控制。\n\n```c++\nstruct file_handle{\n    int fd;\n    struct file* opened_file;\n    struct thread* owned_thread;\n    /* Implementation by ymt Started */\n#ifdef FILESYS\n    struct dir* opened_dir;\n#endif\n    /* Implementation by ymt Ended */\n    struct list_elem elem;\n};\n```\n\n定义`file_handle`结构体，作为文件句柄，在文件操作是记录打开的文件。\n\n在pintos中，一共涉及到了16个syscall操作，如下所示：\n\n```c++\nstatic void syscall_halt(struct intr_frame *f);\nstatic void syscall_exit(struct intr_frame *f, int return_value);\nstatic void syscall_exec(struct intr_frame *f, const char *cmd_line);\nstatic void syscall_wait(struct intr_frame *f, pid_t pid);\nstatic void syscall_open(struct intr_frame *f, const char *name);\nstatic void syscall_create(struct intr_frame *f, const char *name, unsigned initial_size);\nstatic void syscall_remove(struct intr_frame *f, const char *name);\nstatic void syscall_filesize(struct intr_frame *f, int fd);\nstatic void syscall_read(struct intr_frame *f, int fd, const void *buffer, unsigned size);\nstatic void syscall_write(struct intr_frame *f, int fd, const void *buffer, unsigned size);\nstatic void syscall_seek(struct intr_frame *f, int fd, unsigned position);\nstatic void syscall_tell(struct intr_frame *f, int fd);\nstatic void syscall_close(struct intr_frame *f, int fd);\n\nbool syscall_translate_vaddr(const void *vaddr, bool write);\nbool syscall_check_user_string(const char *str);\nbool syscall_check_user_buffer (const char *str, int size, bool write); \n```\n\n算法设计：\n\n1.`syscall_translate_vaddr`：判断用户占指针是否为空或错误，若是则结束执行，不是则把指针给内核\n\n```c++\n/* Transfer user Vaddr to kernel vaddr\n * Return NULL if user Vaddr is invalid\n * */\nbool\nsyscall_translate_vaddr(const void *vaddr, bool write){\n  if (vaddr == NULL || !is_user_vaddr(vaddr))\n    return false;\n  ASSERT(vaddr != NULL);\n  return pagedir_get_page(thread_current()-\u003epagedir, vaddr) != NULL;\n\n}\n```\n\n2.`syscall_check_user_string`：判断cmd字符串是否过长\n\n```c++\nbool\nsyscall_check_user_string(const char *ustr){\n  if (!syscall_translate_vaddr(ustr, false))\n    return false;\n  int cnt = 0;\n  while(*ustr != '\\0'){\n    if(cnt == 4095){\n      puts(\"String to long, please make sure it is no longer than 4096 Bytes!\\n\");\n      return false;\n    }\n    cnt++;\n    ustr++;\n    if (((int)ustr \u0026 PGMASK) == 0){\n      if (!syscall_translate_vaddr(ustr, false))\n        return false;\n    }\n  }\n  return true;\n}\n```\n\n3.`syscall_check_user_buffer`：判断缓冲是否溢出\n\n```c++\nbool\nsyscall_check_user_buffer(const char* ustr, int size, bool write){\n  if (!syscall_translate_vaddr(ustr + size - 1, write))\n    return false;\n\n  size \u003e\u003e= 12;\n  do{\n    if (!syscall_translate_vaddr(ustr, write))\n      return false;\n    ustr += 1 \u003c\u003c 12;\n  }while(size--);\n  return true;\n}\n```\n\n4.`syscall_write` ：指定输出对象和内容，获取文件操作资源的`lock`通过调用文件系统中得`file_write`完成写文件操作\n\n```c++\nstatic void\nsyscall_write(struct intr_frame *f, int fd, const void* buffer, unsigned size){\n  if (!syscall_check_user_buffer(buffer, size, false))\n    thread_exit_with_return_value(f, -1);\n\n  if (fd == STDIN_FILENO)\n    thread_exit_with_return_value(f, -1);\n\n  if (fd == STDOUT_FILENO)\n    putbuf(buffer, size);\n  else{\n    struct file_handle* t = syscall_get_file_handle(fd);\n    if (t != NULL \u0026\u0026 !inode_isdir(file_get_inode(t-\u003eopened_file))){\n      lock_acquire(\u0026filesys_lock);\n      f-\u003eeax = (uint32_t)file_write(t-\u003eopened_file, (void*)buffer, size);\n      lock_release(\u0026filesys_lock);\n    }\n    else\n      thread_exit_with_return_value(f, -1);\n  }\n}\n```\n\n5.`syscall_exit` ：结束当前的进程，如果是子进程，则将自身的返回值和状态告诉父进程，并设置返回值。调用`thread_exit`让线程结束\n\n```c++\nstatic void\nsyscall_exit(struct intr_frame *f, const int return_value){\n\n\n  struct thread *cur = thread_current ();\n  if (!cur-\u003egrandpa_died)\n  {\n    cur-\u003emessage_to_grandpa-\u003eexited = true;\n    cur-\u003emessage_to_grandpa-\u003ereturn_value = return_value;\n  }\n\n\n  thread_exit_with_return_value(f, return_value);\n}\n```\n\n6.`syscall_create` ：指定要创建的文件名和初始大小，获取文件操作资源的`lock`后调用 `filesys_create` 函数创建文件\n\n```c++\nstatic void\nsyscall_create(struct intr_frame *f, const char * name, unsigned initial_size){\n  if (!syscall_check_user_string(name))\n    thread_exit_with_return_value(f, -1);\n\n  lock_acquire(\u0026filesys_lock);\n  f-\u003eeax = (uint32_t)filesys_create(name, initial_size);\n  lock_release(\u0026filesys_lock);\n}\n```\n\n7.`syscall_open` ：指定文件名，获取文件操作资源的`lock`后调用`filesys_open`打开某个文件。此处需要一个`list`记录开过的文件\n\n```c++\nstatic void\nsyscall_open(struct intr_frame *f, const char* name){\n  if (!syscall_check_user_string(name))\n    thread_exit_with_return_value(f, -1);\n  lock_acquire(\u0026filesys_lock);\n  struct file* tmp_file = filesys_open(name);\n  lock_release(\u0026filesys_lock);\n  if (tmp_file == NULL){\n    f-\u003eeax = (uint32_t)-1;\n    return;\n  }\n}\n```\n\n8.`syscall_close` ：指定文件名，获取文件操作资源的`lock`后调用`filesys_close`关闭某个文件， 并且需要在已打开的文件`list`中移除这个文件句柄\n\n```c++\nstatic void\nsyscall_close(struct intr_frame *f, int fd){\n  struct file_handle* t = syscall_get_file_handle(fd);\n  if(t != NULL){\n    lock_acquire(\u0026filesys_lock);\n\n#ifdef FILESYS\n    if (inode_isdir(file_get_inode(t-\u003eopened_file)))\n      dir_close(t-\u003eopened_dir);\n#endif\n    file_close(t-\u003eopened_file);\n    lock_release(\u0026filesys_lock);\n    list_remove(\u0026t-\u003eelem);\n    free(t);\n  }\n  else\n    thread_exit_with_return_value(f, -1);\n}\n```\n\n9.`syscall_read` ：指定缓冲大小读取输入源中相应大小的内容，获取文件操作资源的`lock`后调用`file_read`读取内容\n\n```c++\nstatic void\nsyscall_read(struct intr_frame *f, int fd, const void* buffer, unsigned size){\n  if (!syscall_check_user_buffer(buffer, size, true))\n    thread_exit_with_return_value(f, -1);\n\n  if (fd == STDOUT_FILENO)\n    thread_exit_with_return_value(f, -1);\n\n  uint8_t * str = buffer;\n  if (fd == STDIN_FILENO){\n    while(size-- != 0)\n      *(char *)str++ = input_getc();\n  }\n  else{\n    struct file_handle* t = syscall_get_file_handle(fd);\n    if (t != NULL \u0026\u0026 !inode_isdir(file_get_inode(t-\u003eopened_file))){\n      lock_acquire(\u0026filesys_lock);\n      f-\u003eeax = (uint32_t)file_read(t-\u003eopened_file, (void*)buffer, size);\n      lock_release(\u0026filesys_lock);\n    }\n    else\n      thread_exit_with_return_value(f, -1);\n  }\n}\n```\n\n10.`syscall_filesize` ：指定文件句柄，获取文件操作资源的`lock`后调用`filesys_length`获得文件的大小\n\n```c++\nstatic void\nsyscall_filesize(struct intr_frame *f, int fd){\n  struct file_handle* t = syscall_get_file_handle(fd);\n  if(t != NULL){\n    lock_acquire(\u0026filesys_lock);\n    f-\u003eeax = (uint32_t)file_length(t-\u003eopened_file);\n    lock_release(\u0026filesys_lock);\n  }\n\n  else\n    thread_exit_with_return_value(f, -1);\n}\n```\n\n11.`syscall_exec` ：指定cmd内容，获取文件操作资源的`lock`后调用`process_execute`实现。但是由于孩子与父亲之间需要进行充分的信息交流，所以借助信号量来保持孩子与父亲之间的同步问题，确保进程之间的正确调度\n\n```c++\nstatic void\nsyscall_exec (struct intr_frame *f, const char *cmd_line)\n{\n  if (!syscall_check_user_string(cmd_line))\n    thread_exit_with_return_value(f, -1);\n  lock_acquire(\u0026filesys_lock);\n  f-\u003eeax = (uint32_t)process_execute (cmd_line);\n  lock_release(\u0026filesys_lock);\n  struct list_elem *e;\n  struct thread *cur = thread_current ();\n  struct child_message *l;\n  for (e = list_begin (\u0026cur-\u003echild_list); e != list_end (\u0026cur-\u003echild_list); e = list_next (e))\n  {\n    l = list_entry (e, struct child_message, elem);\n    if (l-\u003etid == f-\u003eeax)\n    {\n      sema_down (l-\u003esema_started);\n      if (l-\u003eload_failed)\n        f-\u003eeax = (uint32_t)-1;\n      return;\n    }\n  }\n}\n```\n\n12.`syscall_wait`：调用 `process_wait` 函数等子进程执行结束，`process_wait` 中，父进程会先判断子进程是否已返回，如果子进程已返回，父进程就会直接将该进程从它的子进程列表中移除。子进程若未返回，则父进程就会在子进程信号量上等待，直到子进程返回为止\n\n```c++\nstatic void\nsyscall_wait (struct intr_frame *f, pid_t pid)\n{\n  f-\u003eeax = (uint32_t)process_wait (pid);\n}\n```\n\n13.`syscall_seek` ：指定位置，获取文件操作资源的`lock`后调用 `file_seek` 将文件指针跳转到文件中的指定位置\n\n```c++\nstatic void\nsyscall_seek(struct intr_frame *f, int fd, unsigned position){\n  struct file_handle* t = syscall_get_file_handle(fd);\n  if (t != NULL){\n    lock_acquire(\u0026filesys_lock);\n    file_seek(t-\u003eopened_file, position);\n    lock_release(\u0026filesys_lock);\n  }\n\n  else\n    thread_exit_with_return_value(f, -1);\n}\n```\n\n14.`syscall_remove` ：指定文件名，获取文件操作资源的`lock`后调用 `filesys_remove` 删除文件\n\n```c++\nstatic void\nsyscall_remove(struct intr_frame *f, const char* name){\n  if (!syscall_check_user_string(name))\n    thread_exit_with_return_value(f, -1);\n  lock_acquire(\u0026filesys_lock);\n  f-\u003eeax = (uint32_t)filesys_remove(name);\n  lock_release(\u0026filesys_lock);\n}\n```\n\n15.`syscall_tell` ：指定句柄，获取文件操作资源的`lock`后调用 `file_tell`获取文件指针当前所在文件中的位置\n\n```c++\nstatic void\nsyscall_tell(struct intr_frame *f, int fd){\n  struct file_handle* t = syscall_get_file_handle(fd);\n  if (t != NULL \u0026\u0026 !inode_isdir(file_get_inode(t-\u003eopened_file))){\n    lock_acquire(\u0026filesys_lock);\n    f-\u003eeax = (uint32_t)file_tell(t-\u003eopened_file);\n    lock_release(\u0026filesys_lock);\n  }\n  else\n    thread_exit_with_return_value(f, -1);\n}\n```\n\n16.`syscall_halt` ：调用`shutdown_power_off`函数实现关机\n\n```c++\nstatic void\nsyscall_halt(struct intr_frame *f){\n  shutdown_power_off();\n}\n```\n\n#### 4. Denying Writes to Executables\n\n基本思想：\n\n通过加锁控制文件访问\n\n结构设计：\n\n```c++\n/* An open file. */\nstruct file \n  {\n    struct inode *inode;        /* File's inode. */\n    off_t pos;                  /* Current position. */\n    bool deny_write;            /* Has file_deny_write() been called? */\n\n#ifdef FILESYS\n    struct dir* dir; // for process_load\n#endif\n\n  };\n```\n\n对文件通过`deny_write`变量判断是否可写\n\n算法设计：\n\n在 `thread_exec` 中，在`load` 操作时调用 `file_deny_write` 加锁，禁止写入文件。在 `thread_exit` 中，将这个可执行文件调用 `file_allow_write` 解锁，允许写入文件，再调用 `file_close` 关闭文件。\n\n```c++\nbool\nload (const char *file_name, void (**eip) (void), void **esp)\n{\n  ···\n  done:\n  /* We arrive here whether the load is successful or not. */\n  if (success)\n  {\n    t-\u003eexec_file = file;\n\n#ifdef FILESYS\n    t-\u003ecurrent_dir = get_file_dir(file);\n#endif\n\n    file_deny_write(file);\n  }\n  return success;\n}\n\nvoid\nfile_close (struct file *file) \n{\n  if (file != NULL)\n    {\n      file_allow_write (file);\n   \n#ifdef FILESYS\n      dir_close(file-\u003edir);\n#endif\n\n      inode_close (file-\u003einode);\n      free (file);\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flpyleo%2Fpintos","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flpyleo%2Fpintos","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flpyleo%2Fpintos/lists"}