zoukankan      html  css  js  c++  java
  • 20169217 《Linux内核原理与分析》 第八周作业

      此次作业仍然分为两个部分,第一部分为实验。

      实验要求:

       1. 阅读理解task_struct数据结构。
       2. 分析fork函数对应的内核处理过程sys_clone,理解创建一个新进程如何创建和修改task_struct数据结构。
       3.使用gdb跟踪分析一个fork系统调用内核处理函数sys_clone ,验证对Linux系统创建一个新进程的理解。

      实验内容:

       1.理解task_struct数据结构。

      操作系统内核里面有操作系统所谓的三大功能,就是进程管理,内存管理和文件系统。但这里面最核心的就是进程管理,也就是linux里面我们有一个数据结构task_struct。为了管理进程,内核要描述进程的这个东西,我们也称它为进程描述符。它提供了进程所需要了解的信息。

     

      进程状态变化图为:

         我们来分析一下进程的变化,操作系统中的进程状态有就绪态、运行态、阻塞态三大状态,实际的,内核管理的进程状态是不一样的。我们来看一下为什么不一样,你看我们在linux进程状态的转换图,我们使用fork来创建一个新进程的时候,实际它的状态是就绪,但是没有在运行。当调度器选择到新创建的进程的时候它就切换到运行态,也是TASK_RUNNING。两个状态都是相同的,也就是说,当进程是TASK_RUNNING状态的时候,它是可运行的,有没有在运行呢,这个取决于它有没有获得CPU的控制权。也就是说这个进程有没有在CPU上实际的执行。如果在CPU上实际的执行就是运行态,如果被调度出去了,在那等待着就是就绪态。所以这个和在操作系统原理中有点不一样。一个正在运行的进程,我们调用do_exit,也就是终止,就会进入TASK_ZOMBIE,也就是进程的终止状态,那么系统会把进程给处理掉。这是就绪态和运行态。我们看一个正在运行的进程,它在等待特定的事件或资源的时候就会进入阻塞态。

     

       2.分析fork函数对应的内核处理过程sys_clone,理解创建一个新进程如何创建和修改task_struct数据结构。

       fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建,函数在/linux-3.18.6/kernel/fork.c中,代码如下:

    1623long do_fork(unsigned long clone_flags,
    1624          unsigned long stack_start,
    1625          unsigned long stack_size,
    1626          int __user *parent_tidptr,
    1627          int __user *child_tidptr)
    1628{
    1629    struct task_struct *p;
    1630    int trace = 0;
    1631    long nr;
    1632
    1633    /*
    1634     * Determine whether and which event to report to ptracer.  When
    1635     * called from kernel_thread or CLONE_UNTRACED is explicitly
    1636     * requested, no event is reported; otherwise, report if the event
    1637     * for the type of forking is enabled.
    1638     */
    1639    if (!(clone_flags & CLONE_UNTRACED)) {
    1640        if (clone_flags & CLONE_VFORK)
    1641            trace = PTRACE_EVENT_VFORK;
    1642        else if ((clone_flags & CSIGNAL) != SIGCHLD)
    1643            trace = PTRACE_EVENT_CLONE;
    1644        else
    1645            trace = PTRACE_EVENT_FORK;
    1646
    1647        if (likely(!ptrace_event_enabled(current, trace)))
    1648            trace = 0;
    1649    }
    1650
    1651    p = copy_process(clone_flags, stack_start, stack_size,
    1652             child_tidptr, NULL, trace);
    1653    /*
    1654     * Do this prior waking up the new thread - the thread pointer
    1655     * might get invalid after that point, if the thread exits quickly.
    1656     */
    1657    if (!IS_ERR(p)) {
    1658        struct completion vfork;
    1659        struct pid *pid;
    1660
    1661        trace_sched_process_fork(current, p);
    1662
    1663        pid = get_task_pid(p, PIDTYPE_PID);
    1664        nr = pid_vnr(pid);
    1665
    1666        if (clone_flags & CLONE_PARENT_SETTID)
    1667            put_user(nr, parent_tidptr);
    1668
    1669        if (clone_flags & CLONE_VFORK) {
    1670            p->vfork_done = &vfork;
    1671            init_completion(&vfork);
    1672            get_task_struct(p);
    1673        }
    1674
    1675        wake_up_new_task(p);
    1676
    1677        /* forking complete and child started to run, tell ptracer */
    1678        if (unlikely(trace))
    1679            ptrace_event_pid(trace, pid);
    1680
    1681        if (clone_flags & CLONE_VFORK) {
    1682            if (!wait_for_vfork_done(p, &vfork))
    1683                ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);
    1684        }
    1685
    1686        put_pid(pid);
    1687    } else {
    1688        nr = PTR_ERR(p);
    1689    }
    1690    return nr;
    1691}

     先定义一个task_sturct结构指针,再用copy_process函数复制一个进程,故真正的复制过程在copy_process函数中,该函数主要执行内容为:

    struct task_struct *p; retval = security_task_create(clone_flags);
    
    p = dup_task_struct(current); retval = sched_fork(clone_flags, p); 
    retval = copy_files(clone_flags, p); retval = copy_fs(clone_flags, p);
    retval = copy_sighand(clone_flags, p); retval = copy_signal(clone_flags, p);
    retval = copy_mm(clone_flags, p); retval = copy_io(clone_flags, p);
    retval = copy_thread(clone_flags, stack_start, stack_size, p);

    linux-3.18.6/arch/x86/kernel/process_32.c的copy_thread函数中

    p->thread.sp = (unsigned long) childregs;  
    p->thread.ip = (unsigned long) ret_from_fork;   
    childregs->ax = 0;     //fork子进程返回为0

    决定了子进程从系统调用中返回后的执行.ret_from_fork决定了新进程的第一条指令地址。p->thread.ip = (unsigned long) ret_from_fork;将子进程的ip设置为ret_from_fork的首地址,子进程从ret_from_fork开始执行

    三、使用gdb跟踪分析一个fork系统调用内核处理函数sys_clone ,验证您对Linux系统创建一个新进程的理解

    rm menu -rf
    git clone https://github.com/mengning/menu.git
    
    cd menu
    mv test_fork.c test.c
    make rootfs
    
    
    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

     

    总结:

    Linux系统创建一个新进程:fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建。do_fork函数主要是调用copy_process函数来创建进程。copy_process函数调用 dup_task_struct 复制当前的 task_struct;调用sched_fork 初始化进程数据结构,并把进程状态设置为 TASK_RUNNING;复制父进程的所有信息,包括copy_files,copy_fs,copy_mm,copy_io等;调用 copy_thread 初始化子进程内核栈;为新进程分配并设置新的 pid,修改进程链表等。dup_task_struct 函数主要是复制一个PCB——task_struct和给新进程分配一个新的内核堆栈。copy_thread函数主要是调度到子进程时的内核栈顶;将子进程的ip设置为ret_from_fork的首地址,子进程从ret_from_fork开始执行;复制内核堆栈;子进程的fork返回0。

  • 相关阅读:
    新浪微盘又是一个给力的产品啊,
    InfoQ: 百度数据库架构演变与设计
    列式数据库——Sybase IQ
    MapR初体验 淘宝共享数据平台 tbdata.org
    IBM正式发布新一代zEnterprise大型机(组图) 大型机,IBM,BladeCenter,美国,纽约 TechWeb News
    1TB is equal to the number of how many GB? 1PB equal to is equal to the number of TB? 1EB PB? | PCfault.com
    Cassandra vs HBase | WhyNosql
    The Hadoop Community Effect
    雅虎剥离开源软件平台 Hadoop ,与风投新建 Hortonworks 公司 品味雅虎
    RowOriented Database 、ColumnOriented Database 、KeyValue Store Database 、DocumentOriented Database
  • 原文地址:https://www.cnblogs.com/dkyliuhongyi/p/6039353.html
Copyright © 2011-2022 走看看