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


    第七周学习——“进程的描述与进程的创建”

    • 问题描述:

      • 经过上一周的学习,我们进一步学习了计算机操作系统的核心工作机制,构造了一个简单的Linux系统MenuOS,利用了GDB简单分析Start_kernel,我们还学习了使用系统的库函数,并在此基础上,学会了使用系统调用,使用gdb调试系统调用,并观察system_call函数是如何工作的
    • 本周学习:

      • 本周在上周学习的基础上继续使用使用gdb调试系统调用,调试断点,分析系统调用运行过程。
      • 并观察fork函数是如何工作的

    一、首先,本周继续补充解决上周的疑难杂症

    对于最新版本的uabntu的gcc编译版本问题的研究

    解决方法

    经各方学习,发现,问题最终还是出在gcc编译器上,我们所使用的gcc编译器的版本为gcc-7.4,对于我们想要编译的内核版本而言,实在是太高了,所以我们并不能成功的编译内核文件,利用上周的方法我们成功改变了编译器版本,在此进行一定的补充:

    ···
    apt-get install gcc gdb
    apt-get install make
    apt-get install bison flex libssl-dev libncurses5-dev
    apt-get install libncurses5-dev
    sudo apt-get install libncurses5-dev make openssl libssl-dev bison flex
    ···

    需要补充安装许多周边工具包,而且不仅需要更改gcc版本,g++的版本也需要改变,否则会出现库查询不到的情况,在此我已经能成功编译linux-3.16.6版本内核了

    二、进程的描述

    OS的三大管理功能:(1)进程管理(进程)(2)内存管理(虚拟内存)(3)文件系统(文件)

    关于进程的创建,Linux提供了几个系统调用来创建和终止进程,以及执行新程序,(fork,vfork,clone和exec,exit);其中clone用来创建轻量级进程,必须制定要共享的资源,exec系统调用执行一个新程序,exit系统调用终止进程。不论是fork,vfork还是clone,在内核中最终都是调用了do_fork来实现进程的创建。

    以及fork()函数的源码:

    long do_fork(unsigned long clone_flags,
              unsigned long stack_start,
              unsigned long stack_size,
              int __user *parent_tidptr,
              int __user *child_tidptr)
    {
        struct task_struct *p;              //创建进程描述符指针
        int trace = 0;
        long nr;                               //子进程pid
    
    
        //创建子进程的描述符和执行时所需的其他数据结构
    p = copy_process(clone_flags, stack_start, stack_size,
             child_tidptr, NULL, trace);    
    
    if (!IS_ERR(p)) {                              //copy_process执行成功
        struct completion vfork;           //定义完成量(一个执行单元等待另一个执行单元完成) 
    
        trace_sched_process_fork(current, p);
    
        nr = task_pid_vnr(p);           //获取pid
    
    
        //如果clone_flags包含CLONE_VFORK标识,将vfork完成量赋给进程描述符
        if (clone_flags & CLONE_VFORK) {
            p->vfork_done = &vfork;
            init_completion(&vfork);
            get_task_struct(p);
        }
    
        wake_up_new_task(p);         //将子进程添加到调度器的队列
    
    
    
        //这个函数的作用是在进程创建的最后阶段,父进程会将自己设置为不可中断状态,然后睡眠在 等待队列上(init_waitqueue_head()函数 就是将父进程加入到子进程的等待队列),等待子进程的唤醒。
        if (clone_flags & CLONE_VFORK) {
            if (!wait_for_vfork_done(p, &vfork))
                ptrace_event(PTRACE_EVENT_VFORK_DONE, nr);
        }
        } else {
            nr = PTR_ERR(p);   //错误处理
        }
        return nr;                     //返回子进程pid(此处的pid为子进程的pid)
    }
    

    可以看出fork()函数对与do_fork() 在生成进程时很关键。不仅调用copy_process()为子进程复制出一份进程信息,如果是vfork()则初始化完成处理信息。

    然后调用wake_up_new_task将子进程加入调度器,为之分配CPU,如果是vfork(),则父进程等待子进程完成exec替换自己的地址空间。

    三、实验的过程

    大致的思路:

    更为精细的过程需要进一步分析内核源码,需要继续努力!


  • 相关阅读:
    Apache的443端口被占用解决方法
    关于变量初始化问题
    浏览无法加载控件
    关于网络数据传输
    java 对象是在什么时候创建的?
    HTML HTTP
    2020 年计划
    Docker 学习
    [腾讯 TMQ] 接口测试用例设计
    pytest + request
  • 原文地址:https://www.cnblogs.com/dhr9313/p/11789043.html
Copyright © 2020-2023  润新知