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


    第六周学习——使用gdb调式系统调用

    • 问题描述:

      • 经过上一周的学习,我们进一步学习了计算机操作系统的核心工作机制,构造了一个简单的Linux系统MenuOS,分析运行了这个简单的OS并进行一定的分析,利用了GDB简单分析Start_kernel,我们还学习了使用系统的库函数,并在此基础上,学会了使用系统调用。
    • 本周学习:

      • 本周在上周学习的基础上使用gdb调试系统调用
      • 并观察system_call函数是如何工作的

    一、首先,本周解决了一个疑难杂症

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

    上上周我们进行了如下一个实验:

    下载内核源代码编译内核:

    $ cd./LinuxKernel/
    $ wget https://www.kernel.org/pub/linux/kernel//v3.x/linux-3.18.6.tar.xz
    $ xz -d linux-3.18.6.tar.xz
    $ tar -xvf linux-3.18.6.tar
    

    这里我们成功解压了安装包,写一部将要对其进行初步编译:

    $ cd linux-3.18.6
    $ make i386_defconfig
    $ make
    

    等待系统将其成功编译后即可进行下一步,制作系统根文件:

    cd ./LinuxKernel/
    mkdir rootfs
    git clone https://github.com/mengning/menu.git
    cd menu gcc -o init linktable.c menu.c test.c -m32 -static -lpthread
    cd ../roofts
    cp ../menu/init ./
    find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
    

    完成以上步骤后即完成了安装,接下来就可以像在实验楼上的操作一样,进行运行以及gdb调试。
    但在我的电脑上出现了问题如下:

    解决方法

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

    那么从这一层面上来思考问题,我们就可以更新gcc版本为老版本即可完成修改:

    • 我各方查找资料,发现gcc-4.8是一个很好的版本,并且在现在可以很方便的安装

    首先安装一些依赖包

    sudo apt-get install ncurses-dev
    sudo apt-get install bison
    sudo apt-get install flex
    sudo apt-get install build-essential
    

    然后安装gcc-4.8

    sudo apt-get install gcc-4.8 g++-4.8
    

    在安装完之后可以采用下面的指令来查看当前系统中安装的所有的gcc和g++的版本:

    ls /usr/bin/gcc*
    ls /usr/bin/g++*
    

    然后看表格中我们需要的gcc-4.8加入候选行列:

    sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8  100
    

    同样,我们也可以将我们现在使用的gcc-7.4加入候选行列

    sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7.4  100
    

    完成之后,就可以在候选行列中选择要使用的gcc版本

    sudo update-alternatives --config gcc
    

    却换完成后使用代码gcc -v 即可知道我们的gcc版本是否切换成功。

    二、在MenuOS中添加命令

    在menu文件夹下

    vi test.c
    

    写入如下函数

    就可以添加系统调用

    三、分析system_call的代码

    使用代码:

    vi linux-3.18.6/arch/x86/kernel/entry_32.S
    

    即可查看system_call函数代码,如下:

    ENTRY(system_call)
    
    RING0_INT_FRAME
    
    #cant unwind into user space anyway
    
    pushl %eax
    
    #save orig_eax ,将系统调用号压入栈中
    
    CFI_ADJUST_CFA_OFFSET 4
    
    SAVE_ALL
    
    #将寄存器的值压入堆栈当中,压入堆栈的顺序对应着结构体struct pt_regs ,当出栈的时候,就将这些值传递到结构体struct pt_regs里面的成员,从而实现从汇编代码向C程序传递参数。
    
    #GET_THREAD_INFO宏获得当前进程的thread_info结构的地址,获取当前进程的信息。
    
    GET_THREAD_INFO(%ebp)
    
    #system call tracing in operation / emulation
    
    #thread_inof结构中flag字段的_TIF_SYSCALL_TRACE或_TIF_SYSCALL_AUDIT
    
    #被置1。如果发生被跟踪的情况则转向相应的处理命令处。
    
    testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
    
    jnz syscall_trace_entry #比较结果不为零的时候跳转。
    
    #对用户态进程传递过来的系统调用号的合法性进行检查。
    
    #如果不合法则跳转到syscall_badsys标记的命令处。
    
    cmpl $(nr_syscalls), %eax
    
    jae syscall_badsys  #比较结果大于或者等于最大的系统调用号的时候跳转,不合法
    
    #合法则跳转到相应系统调用号所对应的服务例程当中,
    
    #也就是在sys_call_table表中找到了相应的函数入口点。
    
    #由于sys_call_table表的表项占4字节,因此获得服务例程指针的具体方法
    
    #是将由eax保存的系统调用号乘以4再与sys_call_table表的基址相加。
    
    syscall_call:
    
    call *sys_call_table(,%eax,4)
    
    movl %eax,PT_EAX(%esp)
    
    #store the return value 将保存的结果返回
    
    • 具体执行过程如下:


  • 相关阅读:
    SDUT3926 kmp
    SDUT 3930 线段树
    HDU 3277 最大流+二分
    HDU 3081 最大流+二分
    HDU 3605 最大流+状态压缩
    HDU 3572 最大流
    HDU 4576 DP
    POJ 2289 最大流
    POJ 1698 最大流
    poj 3498 最大流
  • 原文地址:https://www.cnblogs.com/dhr9313/p/11749295.html
Copyright © 2020-2023  润新知