Stanford Pintos Project2源代码分析&实现思路

0. 引言

本文是对以下仓库中Pintos Project2源代码的思路分析,截取了重要的核心函数,以方便读者理解整个的实现思路。仅供参考,完成作业记得自己深入理解噢~

https://github.com/NicoleMayer/pintos_project2

1. 打印进程终结信息

1.1 process_exit


void
process_exit (void)
{
   
  struct thread *cur = thread_current ();
  uint32_t *pd;
  int exit_status = current_thread->exit_status;//exit_status变量为退出状态
  if (exit_status == INIT_EXIT_STAT)
    exit_process(-1);

  printf("%s: exit(%d)\n",current_thread->name,exit_status);//打印当前线程名和退出状态

  /* 销毁当前线程的页目录,切换回内核目录 */
  pd = cur->pagedir;
  if (pd != NULL)
  {
   
    cur->pagedir = NULL;//当前线程页面设置为空,保证timer的中断就不会切换回进程页面目录
    pagedir_activate (NULL);//激活线程页表
    pagedir_destroy (pd);//销毁线程页面之前的目录
  }
}

每当一个用户进程因为该进程调用exit或其它原因而结束时,需要打印该进程的进程名和退出码(exit code)。

同时,在结束一个进程时,我们要释放该进程占用的所有资源。


2. 参数传递

2.1 process_execute()


tid_t
process_execute (const char *file_name)
{
   
  tid_t tid;
  char *fn_copy = malloc(strlen(file_name)+1);
  char *fn_copy2 = malloc(strlen(file_name)+1);
  strlcpy (fn_copy, file_name, strlen(file_name)+1);
  strlcpy (fn_copy2, file_name, strlen(file_name)+1);//file_name的两份拷贝,避免caller和load的冲突
  
  char *save_ptr;
  fn_copy2 = strtok_r (fn_copy2, " ", &save_ptr);//用strtok_r函数分离字符串,获得thread_name(存放在fn_copy2),为实现参数传递做准备
    
  /* 创建以file_name为名字的新线程,新的子进程执行start_process函数. */
  tid = thread_create (fn_copy2, PRI_DEFAULT, start_process, fn_copy);
  free(fn_copy2);   //手动释放fn_copy2
    
  if (tid == TID_ERROR){
   
    free (fn_copy);
    return tid;
  }
  sema_down(&thread_current()->sema);//降低父进程的信号量,等待子进程结束
  if (!thread_current()->success) return TID_ERROR;//子进程加载可执行文件失败报错  
  return tid;
}

把传入的参数file_namestrtok_r函数分隔开,获得线程名,为接下来的参数传递(之后会传递给start_process,load , setup_stack)做准备。并以此为线程名创建一个新线程,然后新线程转去执行start_process函数。若子进程加载可执行文件的过程没有问题,则返回新建线程的tid.在这之前,父进程无法返回。这样的同步操作是依靠struct thread里的success变量以及信号量的增减实现的。success记录了线程是否成功执行,而通过创建子进程时父进程信号量减少、子进程结束时父进程信号量增加来实现父进程等待子进程的效果,并保证子进程结束唤醒父进程。


2.2 push_argument()


void
push_argument (void **esp, int argc, int argv[]){
   
  *esp = (int)*esp & 0xfffffffc;
  *esp -= 4;
  *(int *) *esp = 0;
  /*下面这个for循环的意义是:按照argc的大小,循环压入argv数组,这也符合argc和argv之间的关系*/
  for (int i = argc - 1; i >= 0; i--)
  {
   
    *esp -= 4;//每次入栈后栈指针减4
    *(int *) *esp = argv[i];
  }
  *esp -= 4;
  *(int *) *esp = (int) *esp + 4;
  *esp -= 4;
  *(int *) *esp = argc;
  *esp -= 4;
  *(int *) *esp = 0;
}

简单来说,这个函数完成了根据argc的大小将argv数组压入栈的操作。在2.3start_process函数中被调用。压入栈顶的过程中,依次存放入参数argv数组(靠参数分离得到)、argv的地址和argc的地址。


2.3 start_process()


static void
start_process (void *file_name_)
{
   
  char *file_name = file_name_;
  struct intr_frame if_;
  bool success;
    
  char *fn_copy=malloc(strlen(file_name)+1);
  strlcpy(fn_copy,file_name,strlen(file_name)+1);//file_name的一份拷贝

  
  memset (&if_, 0, sizeof if_);
  if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG;
  if_.cs = SEL_UCSEG;
  if_.eflags = FLAG_IF | FLAG_MBS;
  
  char *token, *save_ptr;
  file_name = strtok_r (file_name, " ", &save_ptr);//字符串分离,得到线程名,为了传入接下来load函数的参数
  success = load (file_name, &if_.eip, &if_.esp);
    //调用load函数,判断其是否成功load

  if (success){
   
    int argc = 0;
    //限制命令行长度不得超过50
    int
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值