LINUX内核分析第六周学习总结——进程的描述和进程的创建

Posted cindy_忻

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LINUX内核分析第六周学习总结——进程的描述和进程的创建相关的知识,希望对你有一定的参考价值。

LINUX内核分析第六周学习总结——进程的描述和进程的创建

张忻(原创作品转载请注明出处)

《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

一、知识概要

进程的描述

  1. 进程描述符task_struct数据结构(一)
  2. 进程描述符task_struct数据结构(二)

进程的创建

  1. 进程的创建概览及fork一个进程的用户态代码
  2. 理解进程创建过程复杂代码的方法
  3. 浏览进程创建过程相关的关键代码
  4. 创建的新进程是从哪里开始执行的?
  5. 使用gdb跟踪创建新进程的过程

二、学习笔记

进程的描述

1.进程描述符task_struct数据结构(一)

为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息。

  • struct task_struct数据结构很庞大
  • Linux进程的状态与操作系统原理中的描述的进程状态似乎有所不同,比如就绪状态和运行状态都是TASK_RUNNING,为什么呢?
  • 进程的标示pid
  • 所有进程链表struct list_head tasks;     内核的双向循环链表的实现方法 - 一个更简略的双向循环链表
  • 程序创建的进程具有父子关系,在编程时往往需要引用这样的父子关系。进程描述符中有几个域用来表示这样的关系
  • Linux为每个进程分配一个8KB大小的内存区域,用于存放该进程两个不同的数据结构:Thread_info和进程的内核堆栈               

      进程处于内核态时使用,不同于用户态堆栈,即PCB中指定了内核栈,那为什么PCB中没有用户态堆栈?用户态堆栈是怎么设定的?

      内核控制路径所用的堆栈很少,因此对栈和Thread_info来说,8KB足够了

  •  struct thread_struct thread; //CPU-specific state of this task
  • 文件系统和文件描述符
  • 内存管理——进程的地址空间

Linux内核状态转换图:

技术分享

2.进程描述符task_struct数据结构(二)

技术分享

技术分享

双向循环链表图如下:

技术分享

技术分享

技术分享

进程的父子关系直观图:

技术分享

技术分享

技术分享

技术分享

进程的创建

1.进程的创建概览及fork一个进程的用户态代码

(1)进程的起源再回顾

  • 道生一(start_kernel...cpu_idle)
  • 一生二(kernel_init和kthreadd)
  • 二生三(即前面的0、1、2三个进程)
  • 三生万物(1号进程是所有用户态进程的祖先,2号进程是所有内核线程的祖先)

(2)0号进程手工写,1号进程复制、加载init程序

(3)shell命令行是如何启动进程的

fork一个子进程的代码:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 int main(int argc, char * argv[])
 5 {
 6     int pid;
 7     /* fork another process */
 8     pid = fork();
 9     if (pid < 0)   出错处理
10     { 
11         /* error occurred */
12         fprintf(stderr,"Fork Failed!");
13         exit(-1);
14     } 
15     else if (pid == 0) 
16     {
17         /* child process */  子进程   pid=0时 if和else都会执行  fork系统调用在父进程和子进程各返回一次
18         printf("This is Child Process!\n");
19     } 
20     else 
21     {  
22         /* parent process  */
23         printf("This is Parent Process!\n");
24         /* parent will wait for the child to complete*/
25         wait(NULL);
26         printf("Child Complete!\n");
27     }
28 }

2.理解进程创建过程复杂代码的方法

(1)系统调用再回顾

技术分享

技术分享

(2)fork的子进程是从哪里开始执行的?

与基于mykernel写的精简内核对照起来。

(3)创建一个新进程在内核中的执行过程

  • fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建;
  • Linux通过复制父进程来创建一个新进程,那么这就给我们理解这一个过程提供一个想象的框架:
  • 复制一个PCB——task_struct
    err = arch_dup_task_struct(tsk, orig);
  • 要给新进程分配一个新的内核堆栈
ti = alloc_thread_info_node(tsk, node);
tsk->stack = ti;
setup_thread_stack(tsk, orig); //这里只是复制thread_info,而非复制内核堆栈
  • 要修改复制过来的进程数据,比如pid、进程链表等等都要改改吧,见copy_process内部。
  • 从用户态的代码看fork();函数返回了两次,即在父子进程中各返回一次,父进程从系统调用中返回比较容易理解,子进程从系统调用中返回,那它在系统调用处理过程中的哪里开始执行的呢?这就涉及子进程的内核堆栈数据状态和task_struct中thread记录的sp和ip的一致性问题,这是在哪里设定的?copy_thread in copy_process
1 *childregs = *current_pt_regs(); //复制内核堆栈
2 childregs->ax = 0; //为什么子进程的fork返回0,这里就是原因!
3  
4 p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶
5 p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址

(4)理解复杂事物要预设一个大致的框架。

(5)创建新进程是通过复制当前进程来实现的。

(6)设想创建新进程过程中需要做哪些事

3.浏览进程创建过程相关的关键代码

(1)系统调用内核处理函数sys_fork、sys_clone、sys_vfork

技术分享

最终都是执行do_fork()。

技术分享

do_fork()里的复制进程的函数:

 

 

技术分享

具体:

技术分享

打开复制PCB的具体函数:

技术分享

打开alloc_thread_info():

技术分享

 技术分享

拷贝内核堆栈数据和指定新进程的第一条指令地址。

4.创建的新进程是从哪里开始执行的?

(1)复制内核堆栈时

技术分享

打开pt_regs:

技术分享

int指令和SAVE_ALL压到内核栈的内容。

下面分析entry_32.S,也就是总控程序。

技术分享

技术分享

5.使用gdb跟踪创建新进程的过程

三、课后作业

1.理解task_struct数据结构http://codelab.shiyanlou.com/xref/linux-3.18.6/include/linux/sched.h#1235

2.分析fork函数对应的内核处理过程sys_clone,理解创建一个新进程如何创建和修改task_struct数据结构;

3.使用gdb跟踪分析一个fork系统调用内核处理函数sys_clone ,验证您对Linux系统创建一个新进程的理解,推荐在实验楼Linux虚拟机环境下完成实验。

4.特别关注新进程是从哪里开始执行的?为什么从哪里能顺利执行下去?即执行起点与内核堆栈如何保证一致。

5.根据本周所学知识分析fork函数对应的系统调用处理过程,撰写一篇署名博客,并在博客文章中注明“真实姓名(与最后申请证书的姓名务必一致) + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ”,博客内容的具体要求如下:

(1)题目自拟,内容围绕对Linux系统如何创建一个新进程进行;

(2)可以结合实验截图、绘制堆栈状态执行流程图等;

(3)博客内容中需要仔细分析新进程的执行起点及对应的堆栈状态等。

(4)总结部分需要阐明自己对“Linux系统创建一个新进程”的理解

 

 

以上是关于LINUX内核分析第六周学习总结——进程的描述和进程的创建的主要内容,如果未能解决你的问题,请参考以下文章

《Linux内核分析》第六周学习总结

Linux内核设计第六周学习总结 分析Linux内核创建一个新进程的过程

Linux内核分析——第六周学习笔记

《Linux内核分析》第六周学习笔记

Linux内核分析——第六周学习笔记20135308

《Linux内核分析》第六周学习小结