zoukankan      html  css  js  c++  java
  • 第五周 扒开系统调用的三层皮(下)

    • 给MenuOS增加time和time-asm命令

    更新menu代码到最新版

    在main函数中增加MenuConfig

    增加对应的Time函数和TimeAsm函数

    make rootfs

    • 使用gdb跟踪系统调用内核函数sys_time

    一直按n单步执行会进入schedule函数

    sys_time返回后进入汇编代码处理gdb无法继续跟踪

    执行int 0x80之后执行system_call对应的代码

    • 系统调用在内核代码中的处理过程

    1.系统调用在内核代码中的工作机制和初始化

    2.系统调用机制的初始化

    3.中断上下文的切换和进程上下文的切换

    • 实验:使用gdb跟踪分析一个系统调用内核函数

    1.运行MenuOS系统

    在实验楼的虚拟机环境里,打击打开shell,使用命令

    cd LinuxKernel/  

    qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img  

    即可启动这个用于实验的Linux系统MenuOS,实际上就是一个在Linux内核的基础上,再运行一个简单菜单命令行程序。我们可在MenuOS>的提示符下输入help,看到其全部支持的命令.

    2.调试运行

    1)使用带参数命令启动MenuOS

    qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S # 关于-s和-S选项的说明:

    -S freeze CPU at startup (use ’c’ to start execution)

    -s shorthand for -gdb tcp::1234 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项

    也就是在启动MenuOS系统的时候,添加了-S和-s这两个参数,这样可以使得系统在刚刚启动的时候,暂停执行。以便等待我们调试器跟踪执行。



    2)启动gdb,设置断点,运行

    在shell窗口上,右键单击,选择水平分割,在分割出的新的窗口中,输入gdb,在出现gdb提示符后,加载符号文件

    (gdb)file linux-3.18.6/vmlinux  

    建立和被调试程序的连接

    (gdb)target remote:1234   

    在start_kernel函数入口处设置断点

    (gdb)break start_kernel  

    继续输入c,使得系统运行到start_kernel处停住

    (gdb)c



    在gdb下,使用list,列出断点附近的源代码,使用n,单步执行等命令,从而可以详细的跟踪到Linux系统启动的过程。

    gdb调试常用参数

    • r(run)            :    开始运行程序;
    • c(continue)  :    继续运行一直到断点停止
    • b(break)       :   设置程序断点;
    • p(print)         :    打印出变量值;如 p var,会把var变量的值输出
    • s(step)         :    单步跟踪,会进入函数内部
    • n(next)         :    单步跟踪,不进入函数
    • finish           :     跳出函数调试,并打印返回时的信息
    • u(until)        :     跳出循环体
    • q(quit)         :     退出gdb
    • l(list)            :     显示当前行后面的源程序
    • bt (backtrace)   :     查看堆栈信息
    • info              :      查看各类gdb信息以及环境信息,比如:info break 可以查看断点信息
    • clear            :       清除全部已定义的断点
    • delete         :       删除指点的断点号,后面接断点号

    3. 总结

    start_kernel()是内核的汇编与C语言的交接点,在该函数以前,内核的代码都是用汇编写的,完成一些最基本的初始化与环境设置工作。start_kernel就像是c代码中的main函数。不管你关注Linux的内核模块,总是离不开start_kernel函数的,因为大部分模块的初始化工作都是在start_kernel中完成的。在start_kernel()中Linux将完成整个系统的内核初始化,因此start_kernel函数也比较复杂,好在我们只需要关注自己感兴趣的部分即可。内核初始化的最后一步就是启动init进程这个所有进程的祖先。

    在start_kernel的最后,是调用rest_init函数,在rest_init函数,内核将使用下面的代码产生第一个真正的进程,即pid=1的1号进程,而init_task是静态制造出来的,pid=0,我们可以在start_kernel函数的开始处,看到其被初始化的代码 。

    它试图将从最早的汇编代码一直到start_kernel的执行都纳入到init_task进程上下文中,在其初始化工作完成后,就会成为系统的idle进程。事实上在更早前的sched_init函数中,通过init_idle(current, smp_processor_id())函数的调用就已经把init_task初始化成了一个idle task,init_idle函数的第一个参数current就是&init_task,在init_idle中将会把init_task加入到cpu的运行队列中,这样当运行队列中没有别的就绪进程时,init_task(也就是idle task)将会被调用,它的核心是一个while(1)循环,在循环中它将会调用schedule函数以便在运行队列中有新进程加入时切换到该新进程上。
    • 流程图

    • 实验总结

    系统通过 int 0x80从用户态进入内核态,在这个过程中系统先保存了中断环境,然后执行系统调用函数。system_call() 函数通过系统调用号查找系统调用表 sys_cal_table 来查找到具体的系统调用服务进程。在执行完系统调用后在执行 iret 之前,内核做了一系列检查,用于检查是否有新的中断产生。如果没有新的中断,则通过已保存的系统中断环境返回用户态。这样就完成了一个系统调用过程。

  • 相关阅读:
    zoj 3599 Game 博弈论
    hdu 2486/2580 / poj 3922 A simple stone game 博弈论
    hdu 1517 A Multiplication Game 博弈论
    hdu 4407 Sum 容斥原理
    hdu 4686 Arc of Dream
    hdu 4588 Count The Carries
    hdu 4586 Play the Dice
    C#学习(5)
    C#学习(4)
    C#学习(3)
  • 原文地址:https://www.cnblogs.com/20135305yg/p/5303056.html
Copyright © 2011-2022 走看看