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


    进程的描述和进程的创建

    1.进程描述

    1.1操作系统的三大管理功能以及对应的抽象概念:

    • 进程管理
    • 内存管理
    • 文件系统
      1.2Linux进程的状态:
      (1)Linux中进程的状态细分可以分为七种:
    • R运行状态(runing):并不意味着进程一定在运行中,也可以在运行队列里;
    • S睡眠状态(sleeping):进程在等待事件完成;(浅度睡眠,可以被唤醒)
    • D磁盘睡眠状态(Disk sleep):不可中断睡眠(深度睡眠,不可以被唤醒,通常在磁盘写入时发生)
    • T停止状态(stopped):可以通过发送SIGSTOP信号给进程来停止进程,可以发送SIGCONT信号让进程继续运行
    • X死亡状态(dead):该状态是返回状态,在任务列表中看不到;
    • Z僵尸状态(zombie):子进程退出,父进程还在运行,但是父进程没有读到子进程的退出状态,子进程进入僵尸状态;
    • t追踪停止状态(trancing stop)
      (2)在linux下,进程三种主要状态:
    • 就绪态
    • 运行态
    • 阻塞状态
      每种状态将会依据外部条件不断的切换。 刚开始创建的进程叫做就绪态,叫做task_running 当被调用之后,开始运行叫做运行态,也叫做task_running 这两个状态都用了task_runing同一个标志,可理解为可运行状态,是否运行要看时间片等信息。 如果进程终止后进入僵尸状态,最终被回收。 如果等待某件事情将进入阻塞状态。如果被唤醒重新进入就绪态。

    2.进程的创建

    2.1Linux中创建进程一共有三个函数:

    • fork,创建子进程。
    • vfork,与fork类似,但是父子进程共享地址空间,而且子进程先于父进程运行。
    • clone,主要用于创建线程。

    注:Linux中所有的进程创建都是基于复制的方式,Linux通过复制父进程来创建一个新进程,通过调用do_ fork来实现。然后对子进程做一些特殊的处理。而Linux中的线程,又是一种特殊的进程。根据代码的分析,do_ fork中,copy_ process管子进程运行的准备,wake_ up_ new_ task作为子进程forking的完成。fork()函数最大的特点就是被调用一次,返回两次。

    2.2 do_fork处理内容:

    • 调用copy_process,将当期进程复制一份出来为子进程,并且为子进程设置相应地上下文信息。
    • 在vfork调用时,初始化vfork的完成处理信息
    • 调用wake_up_new_task,将子进程放入调度器的队列中,此时的子进程就可以被调度进程选中,得以运行。
    • 如果是vfork调用,需要阻塞父进程,直到子进程执行exec。

    2.3 copy_process的大体流程:

    • 检查各种标志位
    • 调用dup_task_struct复制一份task_struct结构体,作为子进程的进程描述符。
    • 检查进程的数量限制。
    • 初始化定时器、信号和自旋锁。
    • 初始化与调度有关的数据结构,调用了sched_fork,这里将子进程的state设置为TASK_RUNNING。
    • 复制所有的进程信息,包括fs、信号处理函数、信号、内存空间(包括写时复制)等。
    • 调用copy_thread,这又是关键的一步,这里设置了子进程的堆栈信息。
    • 为子进程分配一个pid
    • 设置子进程与其他进程的关系,以及pid、tgid等。这里主要是对线程做一些区分。
    • 在copy_process中,copy_thread函数为子进程准备了上下文堆栈信息

    2.4 copy_thread的流程如下:

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

    实验内容

    1.删除之前的menu,再通过git克隆一份新的,使用mv把test.c覆盖掉,然后执行 make roofts。

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

    2.执行make rootfs,使用help可以看到列表中增加了fork。

    make rootfs
    MenuOS>>help
    

    3.使用水平分割,开启新的shell窗口,启动gdb,把内核加载进来,连接到target remote 1234

    (gdb)file linux-3.18.6/vmlinux
    (gdb)target remote:1234
    

    4.使用b在sys_clone、do_fork、dup_task_struct、copy_process、copy_thread、ret_from_fork处各设置断点。

    (gdb)b sys_clone
    (gdb)b do_fork
    (gdb)b dup_task_struct
    (gdb)b copy_process
    (gdb)b copy_thread
    (gdb)b ret_from_fork
    

    5.使用c继续执行,停到了 do_fork 位置,然后使用命令n

    (gdb)c
    (gdb)n
    


    6.到 copy_process() 函数,继续执行。

    7.使用c继续执行,到断点copy_thread

    总结

    通过对本周的课程的学习,我的理解如下:
    新的进程通过克隆旧的程序(当前进程)而建立。
    fork() 和 clone()(对于线程)系统调用可用来建立新的进程。
    这两个系统调用结束时,内核在系统的物理内存中为新的进程分配新的 task_struct 结构,同时为新进程要使用的堆栈分配物理页。
    Linux 还会为新的进程分配新的进程标识符。
    然后,新 task_struct 结构的地址保存在链表中,而旧进程的 task_struct 结构内容被复制到新进程的 task_struct 结构中。

  • 相关阅读:
    window.location.href的使用方法
    hdu 2850 Load Balancing (优先队列 + 贪心)
    几种常见模式识别算法整理和总结
    【DateStructure】 Charnming usages of Map collection in Java
    编写你自己的单点登录(SSO)服务
    微软历史最高市值是多少?
    Tomcat配置一个ip绑定多个域名
    递归算法:求序列的全排列
    SMTP协议分析
    platform_device与platform_driver
  • 原文地址:https://www.cnblogs.com/20199304lbs/p/11785212.html
Copyright © 2020-2023  润新知