陈康 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
本次实验的环境为实验楼虚拟环境。
1 给MenuOS增加菜单命令
1.1 获取最新menu克隆并编译执行
操作过程如下:
cd LinuxKernel
rm menu -rf
git clone http://github.com/mengning/menu.git
cd menu
make rootfs
值得注意的是make过程,rootfs是事先写好的一个脚本,自动编译自动生成根文件系统,同时自动启动MenuOS,在Makefile中对应脚本如下:
rootfs:
gcc -o init linktable.c menu.c test.c -m32 -static -lpthread
gcc -o hello hello.c -m32 -static
find init hello | cpio -o -Hnewc |gzip -9 > ../rootfs.img
qemu -kernel ../linux-3.18.6/arch/x86/boot/bzImage -initrd ../rootfs.img
1.2 添加菜单命令
增加两个获得pid并打印出来的菜单命令,这里有两种实现方式,一种是直接调用系统API函数,另外一种是通过汇编触发软中断并设置中断向量的值的方式调用系统接口来获得pid的值。通过修改test.c文件实现。
main函数中增加:
MenuConfig("getpid","Show Pid",Getpid);
MenuConfig("getpid_asm","Show Pid(asm)",GetpidAsm);
新增菜单处理函数如下:
int Getpid(int argc, char *argv[])
{
pid_t pid;
pid = getpid();
printf("pid = %d\n",pid);
return 0;
}
int GetpidAsm(int argc, char *argv[])
{
pid_t pid;
asm volatile(
"mov $0,%%ebx\n\t"
"mov $0x14,%%eax\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
: "=m" (pid)
);
printf("pid = %d\n",pid);
return 0;
}
可知获取pid的中断向量号为0x14。
make rootfs
得到更改后的运行效果,如下图所示:
2 使用gdb跟踪系统调用内核函数sys_time
先在shell下执行以下命令:
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
使用qemu运行Linux操作系统镜像,并在起始的地方暂停住。
然后另起一个shell,执行以下操作:
cd LinuxKernel
gdb
file linux-3.18.6/vmlinux
target remote:1234 //连接到需要调试的MenuOS
b start_kernel //设置断点
c //执行,可见程序在start_kernel处停下
list //可查看start_kernel的代码
b sys_time //sys_time是13号系统调用对应的内核处理函数,在该函数处设置断点
c
此时会进入schedule函数。sys_time返回后进入汇编代码处理,gdb无法继续进行追踪
执行int 0x80后执行system call对应的代码(system call不是函数,是一段特殊的汇编代码,gdb还不能进行跟踪)。
3 系统调用在内核代码中的处理过程
现在来分析system_call中断处理过程,了解system_call中断处理过程,分析系统调用的过程。
3.1 系统调用伪代码
系统调用处理的相关代码在linux-3.18.6/arch/x86/kernel/entry_32.S中,但相关实现较为复杂,涉及大量细节,因此此处使用课程提供的伪代码进行分析,伪代码如下图所示:
3.2 系统调用流程图
总结
linux系统屏蔽了底层细节,把状态分为内核态和用户态。用户程序只能在用户态下执行,只有内核态能够访问硬件,此时需要通过一个系统调用向操作系统请求,内核对请求做评估,如果符合系统调用的话,就会满足请求,进入内核态访问硬件。
系统调用一般是通过int 0x80中断进入内核态,整个过程是:首先是保存现场,然后通过eax寄存器查找对应哪一个系统调用,然后查找系统调用表调用对应的系统调用。等完成调用之后检查是否有信号发生或者需要进程调度,如果有就进入相应的处理程序,如果没有就恢复现场完成了整个系统调用,用户程序在完成系统调用之后,又会返回到程序继续执行。