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

第五章 系统调用的三层机制(下)

一、知识点概括

  1. 中断向量0x80和system_call中断服务程序入口的关系
    0x80对应着system_call中断服务程序入口,在start_kernel函数中调用了trap_init函数,trap_init函数中调用了set_system_trap_gate函数,其中有系统调用的中断向量0x80和system_call中断服务程序入口的函数指针,system_call被声明为一个函数,通过set_system_trap_gate函数绑定了中断向量0x80和system_call中断服务程序入口之后,一旦执行int 0x80,CPU就直接跳转到system_call这个位置执行。

  2. 系统调用用户态接口和系统调用的内核处理函数是通过系统调用号匹配起来的。

  3. ystem_call中断服务程序执行流程
    从entry(system_call)开始执行,根据系统调用号来查sys_call_table表中的位置,调用系统调用对应的处理函数,在syscall_exit里面判断当前任务是否需要处理syscall_exit_work,进入syscall_exit_work,这是最常见的进程调度时机点。
    其中sys_call_table(,%eax,4)可以理解为,分派表中每个表项占4个字节,所以先把系统调用号(eax)乘以4,再加上sys_call_table分派表的起始地址,得到系统调用号对应的系统调用内核处理函数的指针。
    system_call的执行流程图如下图所示:

其中,cmpl部分是检查系统调用号(应小于nr_syscalls),不合法即跳入syscall_badsys异常处理,movl部分是保存返回值到栈中,syscall_exit检查是否有任务需要处理,有则进入syscall_exit_work,无则恢复现场。

二、实验:分析system_call中断处理过程

删除旧的的menu目录,重新下载新的版本。操作如下:

$ cd /home/shiyanlou/LinuxKernel
$ rm -rf menu
$ git clone http://github.com/mengning/menu.git
$ cd menu

修改menu目录下的test.c文件,把上节课自己写的系统调用代码加进去,做成两个菜单命令。test.c文件新增代码如下:

#include <sys/stat.h>
#include <sys/types.h>
...
int MakeDir() {
    int ret = 0;
    ret = mkdir("./testdir", 0777);
    printf("ret is: %d.\n", ret);
    return 0;
}

int MakeDirAsm() {
    int ret = 0;
    //ret = mkdir("./testdir", 0777);
    char *dir = "./testdir";
    int mode = 0777;
    asm volatile(
        "movl %1, %%ebx\n\t"
        "movl %2, %%ecx\n\t"
        "movl $39, %%eax\n\t"
        "int $0x80\n\t"
        "movl %%eax, %0\n\t"
        : "=m"(ret)
        : "m"(dir), "m"(mode)
    );
    printf("ret is: %d.\n", ret);
    return 0;
}

int main()
{
    ...
    MenuConfig("make-dir","Make Directory",MakeDir);
    MenuConfig("make-dir-asm","Make Directory(asm)",MakeDirAsm);
    ...
}

重新编译并启动程序:

$ make rootfs

在界面中输入help命令,可以看到make-dir和make-dir-asm这两个命令已经成功地加到这个系统里。执行make-dir命令也可以正常返回结果0,表示命令执行成功。如下图所示:

使用qumu命令重新启动内核并使用-s和-S参数“冻结”系统执行,命令如下:

$ cd /home/shiyanlou/LinuxKernel
$ qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

程序“冻结”的效果如下,启动窗口中提示程序“[stopped]”

此时为了使用gdb进行调试,需要水平分割一个窗口,输入如下命令:

$ gdb
(gdb) file linux-3.18.6/vmlinux
Reading symbols from linux-3.18.6/vmlinux...done.
(gdb) target remote:1234
Remote debugging using :1234
0x0000fff0 in ?? ()
(gdb) continue

其中,file命令用于加载linux内核代码的符号表,target用于将gdb调试工具连接到已经启动的程序上。完成之后,执行continue继续程序的执行。
待系统启动完成后,按 < C + c > 中断系统后给程序打上断点,然后执行continue继续运行程序,如下:

(gdb) break sys_mkdir
Breakpoint 1 at 0xc1139220: file fs/namei.c, line 3525.
(gdb) continue

设置一个sys_time断点,启动menuOS,执行time命令,进入系统调用,会停在在sys_time这个函数。

系统调用过程

系统调用是如何工作的呢?在上面的例子中,它是怎么能够调用到sys_mkdir内核函数的呢?

当程序执行到 int 0x80 这个指令时就会调转到system_call处执行,这个对应关系是由中断向量设计的时候就固定的。

time_asm发现还是停在了sys_time的位置,在sys_call并不能停下。

三、总结

  • system_call这一段代码就是系统调用的处理过程,系统调用是一个特殊一点的中断,可推广到一般,都有保护现场SAVE_ALL和恢复现场restore_all的过程。
  • 系统调用过程:从系统调用处理过程的入口进入,先保存现场SAVE_ALL,然后检查系统调用号是否合法,合法就调用内核处理函数,之后restore_all和iret恢复现场,返回用户态,这个过程期间可能会执行进程切换的任务。

猜你喜欢

转载自www.cnblogs.com/liangxu111/p/11747070.html