原创作品转载请注明出处 《Linux内核分析》MOOC课程
http://mooc.study.163.com/course/USTC-1000029000

  1. 向MenuOS系统中加入自己写的系统调用函数:
    1.1我们首先下载最新的MenuOS代码,此代码中将time加入了MenuOS中,而且修改了Makefile,输入make rootfs即可制作根文件系统,使用qemu启动内核加载rootfs.img文件系统。
    我们输入命令:
git clone  https://github.com/mengning/menu.git
cd menu
make rootfs

即可完成环境的配置。
1.2然后我们打开test.c文件加入上次实验写的系统调用函数
加入两个函数

int newdir(int argc, char *argv[])
{
    if(mkdir("new dir", S_IRWXU) == 0)
        printf("new dir create success\n");
    else
        printf("new dir create failed\n");
    return 0;
    
}
int newdirAsm(int argc, char *argv[])
{
        char *dirName = "new dirAsm";
        mode_t mode = S_IRWXU;
        int returnValue = 0;
        asm
        (
                "movl $39, %%eax\r\n"
                "movl %1, %%ebx\r\n"
                "movl %2, %%ecx\r\n"
                "int $0x80\r\n"
                "movl %%eax,  %0"
                :"=m"(returnValue)
                :"d"(dirName),"D"(mode)
        );
        if(returnValue == 0)
            printf("new dirAsm create success\n");
        else
            printf("new dirAsm create failed\n");       
        return 0;
}

并且在main函数里面加入

MenuConfig("newdir","create a new directory",newdir);
MenuConfig("newdir-asm","create a new directory(asm)",newdirAsm);

完成之后我们重新编译,make rootfs编译并且进入内核。
输入newdir和newdir-asm就会发现命令生效了

这里写图片描述
这里写图片描述
2. 跟踪中断处理的代码
我们使用gdb来调试跟踪一下MenuOS中mkdir的系统调用过程。
首先在终端输入

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

并且在另一个终端打开gdb
输入

gdbtui
(gdb)file linux-3.18.6/vmlinux #加载符号表
(gdb)target remote:1234 #建立gdb和gdbserver之间的连接
(gdb)b sys_mkdir #在sys_mkdir建立断点
(gdb)c

然后我们在qemu中输入newdir,会触发断点,然后我们进入gdb跟踪调试
这里写图片描述
断点停到SYSCALL_DEFINE2.
这个SYSCALL_DEFINE2在这里其实就是

asmlinkage long sys_mkdir(const char __user *pathname, umode_t mode)

因为:我们打开源码中/linux-3.18.6/include/linux/syscalls.h文件,会看到一堆宏定义。
我们拿刚才那个定义为例:、

#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)

这是把SYSCALL_DEFINE2定义为SYSCALL_DEFINEx然后把name与下划线连接(##表示前后两个参数当做串连接)
然后看下SYSCALL_DEFINEx的定义

#define SYSCALL_DEFINEx(x, sname, ...)				\
	SYSCALL_METADATA(sname, x, __VA_ARGS__)			\
	__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

我们继续找到__SYSCALL_DEFINEx的定义。

#define __SYSCALL_DEFINEx(x, name, ...)					\
asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))
…

剩余部分省略
__这里把SYSCALL_DEFINEx定义为asmlinkage long sys##name…
而name就是前面的_name,在这里也就是_mkdir。
所以SYSCALL2_DEFINE2定义为了asmlinkage long sys_mkdir,正式我们要找的系统调用。
弄清楚了之后,接下来我们继续跟踪。
我们在此处x/s pathname打印一下参数,看到参数正是我们要创建的文件夹名字
这里写图片描述
我们按s步入执行,发现进入了一个函数SYSCALL_DEFINE3,根据上面的理解,这里应该是sys_mkdirat系统调用
这里写图片描述
我们来看下这个函数里面的操作

user_path_create 
security_path_mkdir
vfs_mkdir
done_path_create

我们步入执行进入user_path_create
这里写图片描述
里面会调用kern_path_create
然后进入kern_path_create,里面还会调用do_path_lookup
然后我们返回到SYSCALL_DEFINE3中执行完毕,会跳入到schedule中

这里写图片描述
如果继续执行的话就会进入到不可调试的部分只能通过阅读代码来跟踪了。
这里写图片描述
上面的调用过程我们来用一个流程图来描述一下:
这里写图片描述
3. 分析系统调用过程的汇编代码
/linux-3.18.6/arch/x86/kernel/entry_32.S中ENTRY(system_call)与ENDPROC(system_call)之间的内容就是完成系统调用的汇编代码。
提取出主要的处理过程就是:

ENTRY(system_call)
SAVE_ALL
system_call:
	call *sys_call_table(,%eax,4)#根据系统调用号来调用系统调用的处理函数
…
system_exit:
	testl $_TIF_ALLWORK_MASK, %ecx #判断当前任务是否需要处理syscall_exit_work
	jne syscall_exit_work #如果需要处理则跳转到syscall_exit_work
restore_all:
	RESTORE_INT_REGS #不需要处理的话进行RESTORE_ALL
irq_return:
	INTERRUPT_RETURN #返回
ENDPROC(system_call)

整个中断处理的过程我们可以用一个流程图画出来
这里写图片描述

Logo

更多推荐