327
原创作品转载请注明出处,https://github.com/mengning/linuxkernel/
编译内核
使用 wget 命令下载 Linux 5.0 版本内核
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.0.1.tar.xz
解压内核代码包
tar xvJf linux-5.0.1.tar.xz
进入解压出的文件夹,安装编译所需依赖
sudo apt-get install bison flex gcc-4.8 gcc-4.8-multilib g++-4.8 g++-4.8-multilib libssl-dev libc6-dev-i386 libncurses-dev
配置编译
make defconfig make menuconfig
在 menuconfig 中依次选中 Kernel_hacking > compile-time checks and compiler options > comoile the kernel with debug info
·最后开始编译
make -j4
编译出的内核占用很大空间,我第一次编译时 20G 的虚拟机不够用,所以最好把虚拟机硬盘拉满
制作文件系统
根据课件内容,下载 github 的 menu,并制作文件系统
mkdir rootfs git clone https://github.com/mengning/menu.git cd menu gcc -lpthread -o init linktable.c menu.c test.c -m32 -static cd ../rootfs cp ../menu/init ./ find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
运行 qemu
输入
qemu-system-x86_64 -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img
运行结果如下
跟踪内核启动
首先在一个终端中一个启动停止在开始的 menu
qemu-system-x86_64 -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img -S -s -append nokaslr
另外打开一个终端,使用 gdb 调试与其通信
gdb > file linux-5.0.1/vmlinux > target remote:1234 > b start_kernel > c
结果如下
几乎所有的内核模块均会在start_kernel进行初始化
在 start_kernel 中,会对各项硬件设备进行初始化,包括一些 page_address、tick 等等,直到最后需要执行的 rest_init 中,会开始让系统跑起来。
在 rest_init 这个过程中,会调用 kernel_thread() 来创建内核线程 kernel_init,它创建用户的init进程,初始化内核,并设置成1号进程,这个进程会继
续做相关的系统初始化
然后 start_kernel 会调用 kernel_thread 并创建 kthreadd,负责管理内核中得所有线程,然后进程 ID 被设置为 2
最后,创建idle进程(0号进程),不能被调度,并利用循环来不断调号空闲的CPU时间片
跟踪系统调用
我的学号后三位是 327 ,所以我选择跟踪 27 号系统调用 alarm
alarm 系统调用可以设置信号传送闹钟,即用来设置信号 SIGALRM 在经过参数 seconds 秒数后发送给目前的进程。如果未设置信号 SIGALARM 的处理函数,那么 alarm() 默认处理终止进程
unsigned int alarm(unsigned int seconds);
修改 test.c 在其中添加测试函数
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h>
void sig_alarm() {
exit(0);
} int Alarm_test(int argc, char *argv[]) { signal(SIGALRM, sig_alarm);
alarm(10);
return 0; } int main() { PrintMenuOS(); SetPrompt("MenuOS>>"); MenuConfig("version","MenuOS V1.0(Based on Linux 3.18.6)",NULL); MenuConfig("quit","Quit from MenuOS",Quit); MenuConfig("time","Show System Time",Time); MenuConfig("time-asm","Show System Time(asm)",TimeAsm); MenuConfig("alarm","Show alarm",Alarm_test); ExecuteMenu(); }
重新制作根文件系统,重新运行 qemu 和 gdb 调试,并将断点打在 alarm 上
如图所示,
call *%gs:0x10 是 32 位系统下的系统调用函数,最后将执行 __kernel_vsyscall 函数
eax 寄存器中存储的是系统调用号 27
ebx 寄存器中存储的是参数 10
总结
系统调用是一个用户态->内核态->用户态进行切换的一个过程
系统中断的调用有三层分别是API、system_call、system_service。
系统调用是Linux内核中设置的一组用于实现各种系统功能的子程序。
系统调用和普通的函数调用区别在于,系统调用由操作系统核心提供,运行于核心态;而普通的函数调用由函数库或用户自己提供,运行于用户态。
系统调用号是内核为每个系统调用分配标识,用户态进程必须明确的指定使用的系统调用号,来使内核确定使用的服务。
所有评论(0)