一、配置实验环境
1、安装实验工具
1 sudo apt install build-essential 2 sudo apt install qemu 3 sudo apt install libncurses5-dev bison flex libssl-dev libelf-dev
2、下载并解压内核
1 sudo apt install axel 2 axel -n 20 https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/ linux-5.4.34.tar.xz 3 xz -d linux-5.4.34.tar.xz 4 tar -xvf linux-5.4.34.tar 5 cd linux-5.4.34
3、配置内核环境
1 make defconfig 2 make menuconfig 3 Kernel hacking ---> 4 Compile-time checks and compiler options ---> 5 [*] Compile the kernel with debug info 6 [*] Provide GDB scripts for kernel debugging 7 [*] Kernel debugging 8 Processor type and features ----> 9 [] Randomize the address of the kernel image (KASLR)
部分步骤如下图所示:
4、编译运行内核
1 make -j$(nproc) 2 qemu-system-x86_64 -kernel arch/x86/boot/bzImage
5、制作根文件系统
1)下载安装并编译busybox
1 axel -n 20 https://busybox.net/downloads/busybox-1.31.1.tar.bz2 2 tar -jxvf busybox-1.31.1.tar.bz2 3 cd busybox-1.31.1 4 make menuconfig 5 Settings ---> 6 [*] Build static binary (no shared libs) 7 make -j$(nproc) && make install
部分操作如下图所示:
2)制作根文件系统镜像
1 mkdir rootfs 2 cd rootfs 3 cp ../busybox-1.31.1/_install/* ./ -rf 4 mkdir dev proc sys home 5 sudo cp -a /dev/{null,console,tty,tty1,tty2,tty3,tty4} dev/
3)新建init脚本⽂件放在根⽂件系统根⽬录下(rootfs/init)
1 touch init 2 vim init
编写如下内容:
4)给init添加可执行权限并打包成内存根⽂件系统镜像,之后测试挂载根⽂件系统,看内核启动完成后是否执⾏init脚本。
1 chmod +x init 2 find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../ rootfs.cpio.gz 3 qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz
执行结果如下:
二、编写系统调用程序
1、查找系统调用
我的学号后三位为391,故寻找91号系统调用,查找方法如下:
1 cd ~/linux-5.4.34/arch/x86/entry/syscalls 2 vim syscall_64.tbl
查找结果如下:
2、fchmod函数
Fchmod函数出错信息:
EBADF:非法的文件描述符。
EIO:发生I/O错误。
EPERM:有效用户ID与文件拥有者不同,进程无权访问、修改文件。
EROFS:文件位于只读文件系统。
3、编写测试程序
1)编写fchmod.c测试文件
2)编译并执行该程序
1 gcc -o fchmod fchmod.c -static 2 ./fchmod fchmod.c
将测试文件本身作为测试对象,测试结果如下:
3)反汇编该文件
1 objdump -S fchmod > fchmod.S
部分汇编代码如下所示:
4)移动fchmod、fchmod.c文件至根目录的home文件夹下后,重新打包镜像文件
1 find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../ rootfs.cpio.gz
4、使用gdb跟踪该系统调用
1)启动qemu
1 qemu-system-x86_64 -kernel ~/linux-5.4.34/arch/x86/ boot/bzImage -initrd ~/rootfs.cpio.gz -S -s -nographic -append "console=ttyS0"
此时启动不会弹出QEMU虚拟机窗⼝,终端界面会显示停止状态;
2)这时,重新打开一个终端,设置断点并调试
1 cd linux-5.4.34/ 2 gdb vmlinux 3 (gdb) target remote:1234 4 (gdb) b start_kernel 5 (gdb) n
结果如下:
三、总结
linux内核中设置了一组用于实现系统功能的子程序,称为系统调用。系统调用和普通库函数调用非常相似,只是系统调用由操作系统核心提供,运行于内核态,而普通的函数调用由函数库或用户自己提供,运行于用户态。一般的,进程是不能访问内核的。它不能访问内核所占内存空间也不能调用内核函数。为了和用户空间上运行的进程进行交互,内核提供了一组接口。透过该接口,应用程序可以访问硬件设备和其他操作系统资源。
系统调用在用户空间进程和硬件设备之间添加了一个中间层。该层主要作用有三个:
-
它为用户空间提供了一种统一的硬件的抽象接口。比如当需要读些文件的时候,应用程序就可以不去管磁盘类型和介质,甚至不用去管文件所在的文件系统到底是哪种类型。
-
系统调用保证了系统的稳定和安全。作为硬件设备和应用程序之间的中间人,内核可以基于权限和其他一些规则对需要进行的访问进行裁决。举例来说,这样可以避免应用程序不正确地使用硬件设备,窃取其他进程的资源,或做出其他什么危害系统的事情。
-
每个进程都运行在虚拟系统中,而在用户空间和系统的其余部分提供这样一层公共接口,也是出于这种考虑。如果应用程序可以随意访问硬件而内核又对此一无所知的话,几乎就没法实现多任务和虚拟内存,当然也不可能实现良好的稳定性和安全性。在Linux中,系统调用是用户空间访问内核的惟一手段;除异常和中断外,它们是内核惟一的合法入口。