韩玉琪 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

一、进程的描述

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

1. 进程控制块PCB——task_struct

  • 操作系统的三大管理功能

      - 进程管理
    - 内存管理
    - 文件系统
  • PCB task_struct中包含

      - 进程状态
    - 进程打开的文件
    - 进程优先级信息
  • struct task_struct数据结构很庞大

2. Linux进程的状态

  • 不同于操作系统(就绪、运行、阻塞)

    图片来源:《Linux内核设计与实现》

  • 定义的进程状态

3. 我们关心的字段

  • 查看task_struct

  • 进程的标识pid

  • 所有进程链表struct list_head tasks

  • 双向循环链表

  • 继承关系

  • CPU相关状态

4. 分析内核处理过程

(1)do_fork

  • 调用copy_process,将当前进程复制一份出来给子进程,并且为子进程设置相应地上下文信息。
  • 调用wake_up_new_task,将子进程放入调度器的队列中,此时的子进程就可以被调度进程选中运行。

(2)copy_process

  • 创建进程描述符以及子进程所需要的其他所有数据结构,为子进程准备运行环境
  • 调用dup_task_struct复制一份task_struct结构体,作为子进程的进程描述符。
  • 复制所有的进程信息
  • 调用copy_thread,设置子进程的堆栈信息,为子进程分配一个pid。

(3)dup_ task_ struct

  • 先调用alloc_task_struct_node分配一个task_struct结构体。

  • 调用alloc_thread_info_node,分配了一个union。这里分配了一个thread_info结构体,还分配了一个stack数组。返回值为ti,实际上就是栈底。

  • tsk->stack = ti将栈底的地址赋给task的stack变量。

  • 最后为子进程分配了内核栈空间。

  • 执行完dup_task_struct之后,子进程和父进程的task结构体,除了stack指针之外,完全相同。

(4)copy_thread

  • 获取子进程寄存器信息的存放位置
  • 对子进程的thread.sp赋值,将来子进程运行,这就是子进程的esp寄存器的值。
  • 如果是创建内核线程,那么它的运行位置是ret_from_kernel_thread,将这段代码的地址赋给thread.ip,之后准备其他寄存器信息,退出
  • 将父进程的寄存器信息复制给子进程。
  • 将子进程的eax寄存器值设置为0,所以fork调用在子进程中的返回值为0.
  • 子进程从ret_from_fork开始执行,所以它的地址赋给thread.ip,也就是将来的eip寄存器。

(5)运行新进程

从ret_from_fork处开始执行
  • dup_task_struct中为其分配了新的堆栈
  • copy_process中调用了sched_fork,将其置为TASK_RUNNING
  • copy_thread中将父进程的寄存器上下文复制给子进程,这是非常关键的一步,这里保证了父子进程的堆栈信息是一致的。
  • ret_from_fork的地址设置为eip寄存器的值,这是子进程的第一条指令。

二、进程的创建

1. fork

  • fork系统调用在父进程和子进程各返回一次
  • 子进程中返回的是0,父进程中返回值是子进程的pid。

2. 创建一个新进程在内核中的执行过程

fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建。
  • 创建新进程是通过复制当前进程实现的。

do_fork主要是复制了父进程的task_struct,然后修改必要的信息,从而得到子进程的task_struct

  • 复制一个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、进程链表等。

3. 子进程系统调用处理过程

*childregs = *current_pt_regs(); //复制内核堆栈
childregs->ax = 0; //子进程的fork返回0的原因
p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶
p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址
  • 刚fork出来的子进程是从ret_from_fork开始执行的,然后跳转到syscall_exit,从系统调用中返回。

三、实践:分析Linux内核创建一个新进程的过程

1. 启动MenuOS

cd LinuxKernel
rm menu -rf
git clone https://github.com/mengning/menu.git
cd menu
mv test_fork.c test.c
make rootfs

2. gdb调试fork命令

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
  • 在新窗口中启动调试:

      $ gdb
    $ file linux-3.18.6/vmlinux
    $ target remote:1234
  • 设置断点:

      - b sys_clone
    - b do_fork
    - b dup_task_struct
    - b copy_process
    - b copy_thread
    - b ret_from_fork
  • 在Menu系统中输入fork指令,可以看到只输出了fork功能的描述,在断点处sys_clone处停止了。

  • 继续调试,停在do_ fork 和copy_ process

  • 程序停在dup_task_struct函数处

  • 在copy_thread函数中,继续单步执行,可以看到,内核空间压栈地址被初始化了。

  • 程序停止在了ret_ from_ fork处,当前系统执行的是汇编代码。(同时打印出进程信息)

四、总结

Linux通过复制父进程来创建一个新进程,通过调用do_ fork来实现并为每个新创建的进程动态地分配一个task_ struct结构。

1. 新进程的开始

  • copy_thread()中:

      p->thread.ip = (unsigned long) ret _from _fork;
  • 将子进程的ip设置为ret_ form _ fork的首地址,因此子进程是从ret_ from_ fork开始执行的。

2. 执行起点与内核堆栈保证一致

  • 在设置子进程的ip之前:

      *childregs = *current_ pt_ regs();
  • 将父进程的regs参数赋值到子进程的内核堆栈,*childregs的类型为pt_regs,其中存放了SAVE ALL中压入栈的参数。

Linux内核分析第六周学习总结:进程的描述和进程的创建的更多相关文章

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

    LINUX内核分析第六周学习总结--进程的描述与创建 标签(空格分隔): 20135321余佳源 余佳源 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc ...

  2. linux内核分析第六周学习笔记

    LINUX内核分析第六周学习总结 标签(空格分隔): 20135328陈都 陈都 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.c ...

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

    LINUX内核分析第六周学习总结——进程的描述和进程的创建 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/cours ...

  4. Linux内核分析第六周学习笔记——分析Linux内核创建一个新进程的过程

    Linux内核分析第六周学习笔记--分析Linux内核创建一个新进程的过程 zl + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/U ...

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

    第六周 进程的描述和进程的创建 一.进程描述符task_struct数据结构 1.操作系统三大功能 进程管理 内存管理 文件系统 2.进程控制块PCB——task_struct 也叫进程描述符,为了管 ...

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

    进程的描述和进程的创建 前言:以下笔记除了一些讲解视频中的概念记录,图示.图示中的补充文字.总结.分析.小结部分均是个人理解.如有错误观点,请多指教! PS.实验操作会在提交到MOOC网站的博客中写.

  7. LINUX内核分析第七周学习总结——可执行程序的装载

    LINUX内核分析第六周学习总结——进程的描述和进程的创建 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/cours ...

  8. LINUX内核分析第七周学习总结:可执行程序的装载

    LINUX内核分析第七周学习总结:可执行程序的装载 韩玉琪 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/cours ...

  9. LINUX内核分析第八周学习总结——进程的切换和系统的一般执行过程

    LINUX内核分析第八周学习总结——进程的切换和系统的一般执行过程 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/c ...

随机推荐

  1. windows 下mysql每日定时备份的几种方法

    第一种:新建批处理文件 backup.dat,里面输入以下代码:  代码如下 复制代码 net stop mysql xcopy "C:/Program Files/MySQL/MySQL ...

  2. 使用seajs封装js模块

    //方法一:将函数绑定到原型上 define(function(require, exports, module) { $.fn.tab = function(option, callback) { ...

  3. python 类中staticmethod,classmethod,普通方法

    1.staticmethod:静态方法和全局函数类似,但是通过类和对象调用. 2.classmethod:类方法和类相关的方法,第一个参数是class对象(不是实例对象).在python中class也 ...

  4. 关于Java的软引用及弱引用

    概念介绍   1   Reference      描述一个对象的引用.其内部维持一个queue引用,用于跟踪对象的回收情况,当对象被回收时将当前reference引用入队   2   SoftRef ...

  5. 九度oj 题目1087:约数的个数

    题目链接:http://ac.jobdu.com/problem.php?pid=1087 题目描述: 输入n个整数,依次输出每个数的约数的个数 输入: 输入的第一行为N,即数组的个数(N<=1 ...

  6. dedecms为后台自定义菜单的完整方法

    dedecms为后台自定义菜单的完整方法 品味人生 dedeCMS 围观7330次 18 条评论 编辑日期:2014-06-14 字体:大 中 小   最近在给客户定制一个企业网站,客户要求使用ded ...

  7. MSSQL FOR MXL PATH 运用(转载)

    FOR XML PATH 有的人可能知道有的人可能不知道,其实它就是将查询结果集以XML形式展现,有了它我们可以简化我们的查询语句实现一些以前可能需要借助函数活存储过程来完成的工作.那么以一个实例为主 ...

  8. [转]行者,一念一生,成功的背后!(给所有IT人)

    成功的背后,有着许多不为人知的故事,而正是这些夹杂着泪水和汗水的过去,才成就了一个个走向成功的普通人. 凌晨两点半,早 已习惯了一个人坐在电脑前的我,望着屏幕,任思绪在暗夜的包容下静静流淌,时光仿佛又 ...

  9. Android AChartEngine 去除折线图黑边

    通常使用AChartEngine画出的折线图,如果背景不是黑色,则会在折线图的坐标轴旁边出现黑边,如图所示: 试了好多设置,最后终于发现,去除黑边的设置是: mRenderer.setMarginsC ...

  10. 不使用spring的情况下原生java代码两种方式操作mongodb数据库

    由于更改了mongodb3.0数据库的密码,导致这几天storm组对数据进行处理的时候,一直在报mongodb数据库连接不上的异常.   主要原因实际上是和mongodb本身无关的,因为他们改的是配置 ...