第六周 进程的描述和进程的创建

一、进程描述符task_struct数据结构

1.操作系统三大功能

  • 进程管理
  • 内存管理
  • 文件系统

2.进程控制块PCB——task_struct

也叫进程描述符,为了管理进程,内核需要对每个进程进行描述,它就提供了内核所需了解的进程信息。

struct task_struct数据结构很庞大,1235行~1644行

3.Linux进程状态

Linux进程的状态与操作系统原理中的描述的进程状态有所不同

操作系统状态:

  • 就绪态
  • 运行态
  • 阻塞态

linux进程状态:

4.理解task_struct数据结构

二、进程的创建

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

(1)进程起源再回顾

start_kernel的最后rest_init里面创建了两个kernel_thread(内核线程)

  • kernel_init用户态进程
  • kthreadd所有内核线程的祖先

以上创建过程与我们在shell命令行下启动一个进程,创建过程本质上是一样的。

(2)复制一份进程描述符

其实0号进程是手工写入它的进程描述符数据,1号进程的创建是复制了0号进程的PCB,根据1号进程的需要,修改PID,加载一个init可执行程序。

进程是怎么创建起来的?

fork一个子进程的代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char * argv[])
{
int pid;
/* fork another process */
pid = fork();  //用于在用户态创建一个子进程的系统调用
if (pid < )   //出错处理,之后两个都不会执行
{
/* error occurred */
fprintf(stderr,"Fork Failed!");
exit(-);
}
else if (pid == )   //这里pid=0,下面两个都会执行。因为fork系统调用在父进程和子进程各返回一次
{
/* child process */
printf("This is Child Process!\n");
}
else
{
/* parent process */
printf("This is Parent Process!\n");
/* parent will wait for the child to complete*/
wait(NULL);
printf("Child Complete!\n");
}
}

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

(1)系统调用再回顾

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

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
    $ *childregs = *current_pt_regs(); //复制内核堆栈
$ childregs->ax = ; //为什么子进程的fork返回0,这里就是原因 $ p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶
$ p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址

三、实验

1.删除menu,clone一份新的menu,把test_fork.c和test.c覆盖掉,重新执行make rootfs

我们发现多了fork功能

2.与之前相同,启动gdb

3.因为fork实际执行的是clone,所以在sys_clone这里设置断点,以及其他关键地方

4.继续执行,停在了do_fork的位置

5.next……接下来是一些出错处理,直到copy_process

6.继续单步执行,程序停在了dup_task_struct函数处,现在已经把父进程的PCB,也就是task_struct数据结构复制过来了,也就是由p所指向的子进程的PCB

进去,发现

7.在copy_thread函数,next,从这里可以看到,从子进程的pid,也就是内核堆栈的位置,找到了栈空间,SAVE_ALL的一些内容,SAVE_ALL的地址

找到其压栈地址

8.当前进程的内核堆栈寄存器中的值复制到子进程中

9.设置子进程被调度的起点

10.对ret_from_fork继续执行单步调试,当前系统执行的是汇编代码。

.

11.当程序跳转到syscall_exit处后,就不能再继续gdb跟踪调试了

四、总结

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

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

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

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

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

  • 不论是使用 fork 还是 vfork 来创建进程,最终都是通过 do_fork() 方法来实现的
  • 调用copy_process,复制一个PCB——task_struct,给新进程分配一个新的内核堆栈
  • 修改复制过来的进程数据,比如pid、进程链表等等都要改改
  • fork()函数在父子进程中各返回一次,

Linux内核分析——第六周学习笔记20135308的更多相关文章

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

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

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

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

  3. Linux内核分析——第七周学习笔记20135308

    第七周 可执行程序的装载 一.预处理.编译.链接和目标文件的格式 1.可执行程序是怎么来的 C代码—>预处理—>汇编代码—>目标代码—>可执行文件 .asm汇编代码 .o目标码 ...

  4. Linux内核分析——第三周学习笔记20135308

    第三周 构造一个简单的Linux系统MenuOS 计算机三个法宝: 1.存储程序计算机 2.函数调用堆栈 3.中断 操作系统两把宝剑: 1.中断上下文的切换:保存现场和恢复现场 2.进程上下文的切换 ...

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

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

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

    第八周 进程的切换和系统的一般执行过程 一.进程切换的关键代码switch_to分析 1.进程调度与进程调度的时机分析 (1)进程分类 第一种分类 I/O-bound:等待I/O CPU-bound: ...

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

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

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

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

  9. 《Linux内核分析》第一周学习笔记

    <Linux内核分析>第一周学习笔记 计算机是如何工作的 郭垚 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/c ...

随机推荐

  1. HTML DOM item() 方法

    一直不知道javascript还有类似jQ里面eq()的函数,原来原生javascript的item()有类似功能: 由于是原生javascript,先补习下children和childNodes的区 ...

  2. Oracle修改表空间为自动扩展

    https://gqsunrise.iteye.com/blog/2015692 1.数据文件自动扩展的好处1)不会出现因为没有剩余空间可以利用到数据无法写入2)尽量减少人为的维护3)可以用于重要级别 ...

  3. Bank项目

    项目概述: 技术涵盖:由 8 组由浅入深的模块构成,应用如下技术:面向对象的封装性.构造器.引用类型的成员变量.异构数组.继承.多态.方法的重载.方法的重写.包装类.单子模式.异常.集合. 实现功能: ...

  4. Apache Commons Fileupload 反序列化漏洞分析

    下面是k8脚本. # -*- coding: utf-8 -*- # Oracle Weblogic Server (10.3.6.0, 12.1.3.0, 12.2.1.2, 12.2.1.3) D ...

  5. Docker技术入门与实战 第二版-学习笔记-9-Docker Compose 项目-1-举例说明

    Docker Compose 是 Docker 官方编排(Orchestration)项目之一,负责快速在集群中部署分布式应用 Compose 通过一个配置文件来管理多个Docker容器,在配置文件中 ...

  6. webservice 客户端调用

    /** * 通过webserevice下发工单 * @param url * @param method * @param requestMap * @return * @throws Service ...

  7. shiro实战系列(十五)之Spring集成Shiro

    Shiro 的 JavaBean 兼容性使得它非常适合通过 Spring XML 或其他基于 Spring 的配置机制.Shiro 应用程序需要一个具 有单例 SecurityManager 实例的应 ...

  8. Python 函数(一)

    Python3 函数(基本概念) 1.概念: 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段:通过函数,可以对特定功能的代码进行封装,实现代码的复用. 2. 5.参数传递 (1) P ...

  9. C++之内联函数

    C++语言新增关键字 inline,用于将一个函数声明为内联函数.在程序编译时,编译器会将内联函数调用处用函数体替换,这一点类似于C语言中的宏扩展. 采用内联函数可以有效避免函数调用的开销,程序执行效 ...

  10. Altium 拼板方法以及 注意的 地方

    1.修改软件设置, 即工具→优先选项→覆铜重建 对号去掉,如下,否则 拼板复制 覆铜 会变形导致拼板错误!!! 2.拼板方法,Ctrl+A全部复制(不要漏掉),选择特殊粘贴的方式,快捷键 E→A  去 ...