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替换自己的地址空间。

三、实验的过程

大致的思路:

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


原文地址:https://www.cnblogs.com/dhr9313/p/11789043.html