2019-2020-1 20199320《Linux内核原理与分析》第七周作业

一、进程的描述

  1. 操作系统内核实现操作系统的三大管理功能:进程管理,内核管理和文件系统,其中最核心的功能是进程管理。

  2. 在操作系统原理中,我们通过进程控制块PCB描述进程,通常采用一个数据结构struct task_struct来描述进程,该结构体大致分为以下几个部分:

    • 进程状态(State)

    • 进程调度信息(Scheduling Information)

    • 各种标识符(Identifiers)

    • 进程通信有关信息(IPC:Inter_Process Communication)

    • 时间和定时器信息(Times and Timers)

    • 进程链接信息(Links)

    • 文件系统信息(File System)

    • 虚拟内存信息(Virtual Memory)

    • 页面管理信息(page)

    • 对称多处理器(SMP)信息

    • 和处理器相关的环境(上下文)信息(Processor Specific Context)

      下面分析一下其中的进程状态:

      Linux内核管理的进程状态转换如下图所示:

二、实验内容:分析Linux内核创建一个新进程的过程

  1. 在MenuOS中增加fork命令,具体命令和实现效果截图如下图所示:

  2. 打开另一个终端,进行gdb跟踪, 对sys_clone,do_fork,dup_task_struct,copy_process,copy_thread和ret_from_fork设置断点 ,实现效果如图所示:

    • 运行后首先停在sys_clone处 :

    • 然后是do_fork,之后是copy_process:

    • 进入copy_thread:

    • 在copy_thread中,我们可以查看p的值 :

    • 但是回到copy_process后再查看,将得到一个value optimized out的提示,这是因为Linux内核打开gcc的-O2选项优化导致.如果想要关掉,可以参考:这里

    • ret_from_fork按照之前的分析被调用,跟踪到syscall_exit后无法继续.如果想在本机调试system call,那么当你进入system call时,系统已经在挂起状态了。如果想要跟踪调试system_call,可以使用kgdb等

新进程是从哪里开始执行的

之前谈到的copy_process中的copy_thread()函数,正是这个函数决定了子进程从系统调用中返回后的执行.

int copy_thread(unsigned long clone_flags, unsigned long sp,
      unsigned long arg, struct task_struct *p)
      {
          ...
          
          *childregs = *current_pt_regs();
          childregs->ax = 0;
          if (sp)
              childregs->sp = sp;
          
          p->thread.ip = (unsigned long) ret_from_fork;
          
          ...
}

子进程执行ret_from_fork

ENTRY(ret_from_fork)
          CFI_STARTPROC
          pushl_cfi %eax
          call schedule_tail
          GET_THREAD_INFO(%ebp)
          popl_cfi %eax
          pushl_cfi $0x0202       # Reset kernel eflags
          popfl_cfi
          jmp syscall_exit
          CFI_ENDPROC
END(ret_from_fork)

执行起点与内核堆栈如何保证一致?

  • 在ret_from_fork之前,也就是在copy_thread()函数中childregs = current_pt_regs();该句将父进程的regs参数赋值到子进程的内核堆栈,

  • *childregs的类型为pt_regs,里面存放了SAVE ALL中压入栈的参数

  • 故在之后的RESTORE ALL中能顺利执行下去.

三、总结

  • Linux通过复制父进程来创建一个新进程,通过调用do_fork来实现

  • Linux为每个新创建的进程动态地分配一个task_struct结构.

  • 为了把内核中的所有进程组织起来,Linux提供了几种组织方式,其中哈希表和双向循环链表方式是针对系统中的所有进程(包括内核线程),而运行队列和等待队列是把处于同一状态的进程组织起来

  • fork()函数被调用一次,但返回两次.

猜你喜欢

转载自www.cnblogs.com/liangxu111/p/11789611.html