原创作品转载请注明出处https://github.com/mengning/linuxkernel/
一、实验环境
虚拟机为VMware Workstation,系统为Ubuntu 18.04
编译5.0内核
mkdir LinuxKernel
cd ~/Linuxkernel/
wget https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.0.1.tar.xz
xz -d linux-5.0.1.tar.xz
tar -xvf linux-5.0.1.tar
cd linux-5.0.1
make i386_defconfig
make -j8
可能的问题
/bin/sh: 1: bison: not found
scripts/Makefile.lib:217: recipe for target ‘scripts/kconfig/zconf.tab.c’ failed
make[2]: *** [scripts/kconfig/zconf.tab.c] Error 127
Makefile:514: recipe for target ‘silentoldconfig’ failed
/bin/sh: 1: flex: not found
scripts/Makefile.lib:202: recipe for target ‘scripts/kconfig/zconf.lex.c’ failed
make[2]: *** [scripts/kconfig/zconf.lex.c] Error 127
Makefile:514: recipe for target ‘silentoldconfig’ failed
error : openssl/bio.h :No such file or folder
解决方案
sudo apt-get install bison
sudo apt-get install flex
sudo apt-get install libssl-dev
制作根文件系统
cd ~/LinuxKernel/
mkdir rootfs
git clone https://github.com/mengning/menu.git
cd menu
gcc -pthread -o init linktable.c menu.c test.c -m32 -static
cd ../rootfs
cp ../menu/init ./
find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
可能的问题
/usr/bin/ld: 当搜索用于 /usr/lib/gcc/x86_64-linux-gnu/5/libgcc.a 时跳过不兼容的 -lgcc
/usr/bin/ld: 找不到 -lgcc
/usr/bin/ld: 当搜索用于 /usr/lib/gcc/x86_64-linux-gnu/5/libgcc_s.so 时跳过不兼容的 -lgcc_s
/usr/bin/ld: 找不到 -lgcc_s
解决方案
sudo apt install gcc-4.8 gcc-4.8-multilib g++-4.8 g++-4.8-multilib
启动MenuOS
qemu-system-i386 -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img
一切顺利的话,可以看见如下界面
二、实验过程
跟踪内核启动
qemu -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img -S -s -append nokaslr
打开另一个终端
cd LinuxKernel/linux-5.0.1
gdb vmlinux
(gdb) target remote:1234
实现系统调用
查找系统调用表可知,标号36的系统调用为sync
函数原型 | void sync(void) |
---|---|
头文件 | unistd.h |
函数说明 | sync负责将系统缓冲区的数据“写入”磁盘,以确保数据的一致性和同步性 |
注意 | sync函数仅仅是将全部改动过的块缓冲区排入写队列,然后就返回,并不等待实际I/O操作结束 |
在menu目录下的test.c中增加sync的系统调用:
int Sync(int argc,char *argv[])
{
sync();
printf("Sync has done!\n");
return 0;
}
int SyncAsm(int argc,char *argv[])
{
asm volatile(
"mov $0x24,%%eax\n\t"
"int $0x80\n\t"
"mov %%eax,%0\n\t"
: "=m"
);
printf("SyncAsm has done!\n");
return 0;
}
int main()
{
...
...
MenuConfig("sync","Make all changes done",Sync);
MenuConfig("sync-asm","Make all changes done(asm)",SyncAsm);
}
重新编译制作rootfs.img后启动MenuOS
可以进行如下的互动:
跟踪系统调用
三、实验总结
系统调用
- 当调用一个系统调用时,CPU从用户态切换到内核态并开始执行一个system_call和系统调用内核函数。在Linux中通过执行int
- 0x80来触发系统调用,内核为每个系统调用分配一个系统调用号,用户态进程必须明确指明系统调用号,需要使用EAX寄存器来传递。
- 系统调用可能需要参数,但是不能通过像用户态进程函数中将参数压栈的方式传递,因为用户态和内核态有不同的堆栈,必须通过寄存器的方式传递参数。
- EAX用来传递系统调用号,EBX、ECX、EDX、ESI、EDI、EBP用来传递参数,若参数较多,则把指向内存的指针存入寄存器