深入理解系统调用

环境搭建:

安装开发工具:

1 sudo apt install build-essential
2 sudo apt install qemu 
3 sudo apt install libncurses5-dev bison flex libssl-dev libelf-dev
4 sudo apt install axel

下载内核源码:

1 axel -n 20 https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/
2 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

配置内核选项:

 1 make defconfig # Default configuration is based on 'x86_64_defconfig'
 2 make menuconfig
 3 # 打开debug相关选项
 4 Kernel hacking --->
 5     Compile-time checks and compiler options --->
 6     [*] Compile the kernel with debug info
 7     [*] Provide GDB scripts for kernel debugging
 8     [*] Kernel debugging
 9 # 关闭KASLR,否则会导致打断点失败
10     Processor type and features ---->
11     [] Randomize the address of the kernel image (KASLR)

编译和运行内核:

1 make -j$(nproc) # nproc gives the number of CPU cores/threads available
2 qemu-system-x86_64 -kernel arch/x86/boot/bzImage# 测试⼀下内核能不能正常加载运⾏,因为没有⽂件系统最终会kernel panic

结果:

 制作内存根⽂件系统 :

 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 #记得要编译成静态链接,不⽤动态链接库。
 6 Settings --->
 7     [*] Build static binary (no shared libs)
 8 #然后编译安装,默认会安装到源码⽬录下的 _install ⽬录中。
 9 make -j$(nproc) 
10 make install
11 mkdir rootfs
12 cd rootfs
13 cp ../busybox-1.31.1/_install/* ./ -rf
14 mkdir dev proc sys home
15 sudo cp -a /dev/{null,console,tty,tty1,tty2,tty3,tty4} dev/
 1 #准备init脚本⽂件放在根⽂件系统跟⽬录下(rootfs/init),添加如下内容到init⽂件。
 2 #!/bin/sh
 3 mount -t proc none /proc
 4 mount -t sysfs none /sys
 5 echo "Wellcome MengningOS!"
 6 echo "--------------------"
 7 cd home
 8 /bin/sh
 9 #给init脚本添加可执⾏权限
10 chmod +x init
1 #打包成内存根⽂件系统镜像
2 find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz
3 #测试挂载根⽂件系统,看内核启动完成后是否执⾏init脚本
4 qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz

结果:

 跟踪调试Linux内核的基本⽅法 :

1 qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz -S -s
2 # 纯命令⾏下启动虚拟机
3 qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz -S -s -nographic -append "console=ttyS0"
4 #⽤以上命令先启动,然后可以看到虚拟机⼀启动就暂停了。加-nographic -append "console=ttyS0"参数启动不会弹出QEMU虚拟机窗⼝,可以在纯命令⾏下启动虚拟机,此时可以通过“killall qemu-system-x86_64”命令强⾏关闭虚拟机。
1 #再打开⼀个窗⼝,启动gdb,把内核符号表加载进来,建⽴连接:
2 cd linux-5.4.34/
3 gdb vmlinux
4 (gdb) target remote:1234
5 (gdb) b start_kernel
6 c、 bt、 list、 next、 step....

结果:

 

 

 系统调用:

什么是系统调用:

简单来说,系统调用就是用户程序和硬件设备之间的桥梁。

用户程序在需要的时候,通过系统调用来使用硬件设备。

系统调用的存在,有以下重要的意义:

1)用户程序通过系统调用来使用硬件,而不用关心具体的硬件设备,这样大大简化了用户程序的开发。

    比如:用户程序通过write()系统调用就可以将数据写入文件,而不必关心文件是在磁盘上还是软盘上,或者其他存储上。

2)系统调用使得用户程序有更好的可移植性。

    只要操作系统提供的系统调用接口相同,用户程序就可在不用修改的情况下,从一个系统迁移到另一个操作系统。

3)系统调用使得内核能更好的管理用户程序,增强了系统的稳定性。

    因为系统调用是内核实现的,内核通过系统调用来控制开放什么功能及什么权限给用户程序。

    这样可以避免用户程序不正确的使用硬件设备,从而破坏了其他程序。

4)系统调用有效的分离了用户程序和内核的开发。

    用户程序只需关心系统调用API,通过这些API来开发自己的应用,不用关心API的具体实现。

    内核则只要关心系统调用API的实现,而不必管它们是被如何调用的。

12号系统调用:

brk:

 

 reference:brk()  and  sbrk() change the location of the program break, which defines the end of the process's data segment

(i.e., the program break is the first location after the end of the uninitialized data segment). 

汇编调用:

 汇编结果:

 重新生成文件系统:

 

debug:

 

 

 

 结果:

 1 (gdb) b __x64_sys_brk
 2 Breakpoint 1 at 0xffffffff81199a40: file mm/mmap.c, line 187.
 3 (gdb) c
 4 Continuing.
 5 
 6 Breakpoint 1, __x64_sys_brk (regs=0xffffc900001b7f58) at mm/mmap.c:187
 7 187     SYSCALL_DEFINE1(brk, unsigned long, brk)
 8 (gdb) n
 9 do_syscall_64 (nr=18446612682188181624, regs=0xffffc900001b7f58) at arch/x86/entry/common.c:300
10 300             syscall_return_slowpath(regs);
11 (gdb) n
12 301     }
13 (gdb) n
14 entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:184
15 184             movq    RCX(%rsp), %rcx
16 (gdb) n
17 185             movq    RIP(%rsp), %r11
18 (gdb) n
19 187             cmpq    %rcx, %r11      /* SYSRET requires RCX == RIP */
20 (gdb) n
21 188             jne     swapgs_restore_regs_and_return_to_usermode
22 (gdb) n
23 205             shl     $(64 - (__VIRTUAL_MASK_SHIFT+1)), %rcx
24 (gdb) n
25 206             sar     $(64 - (__VIRTUAL_MASK_SHIFT+1)), %rcx
26 (gdb) n
27 210             cmpq    %rcx, %r11
28 (gdb) n
29 211             jne     swapgs_restore_regs_and_return_to_usermode
30 (gdb) n
31 213             cmpq    $__USER_CS, CS(%rsp)            /* CS must match SYSRET */
32 (gdb) n
33 214             jne     swapgs_restore_regs_and_return_to_usermode
34 (gdb) n
35 216             movq    R11(%rsp), %r11
36 (gdb) n
37 217             cmpq    %r11, EFLAGS(%rsp)              /* R11 == RFLAGS */
38 (gdb) n
39 218             jne     swapgs_restore_regs_and_return_to_usermode
40 (gdb) n
41 238             testq   $(X86_EFLAGS_RF|X86_EFLAGS_TF), %r11
42 (gdb) n
43 239             jnz     swapgs_restore_regs_and_return_to_usermode
44 (gdb) n
45 243             cmpq    $__USER_DS, SS(%rsp)            /* SS must match SYSRET */
46 (gdb) n
47 244             jne     swapgs_restore_regs_and_return_to_usermode
48 (gdb) n
49 253             POP_REGS pop_rdi=0 skip_r11rcx=1
50 (gdb) n
51 entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:259
52 259             movq    %rsp, %rdi
53 (gdb) n
54 260             movq    PER_CPU_VAR(cpu_tss_rw + TSS_sp0), %rsp
55 (gdb) n
56 entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:262
57 262             pushq   RSP-RDI(%rdi)   /* RSP */
58 (gdb) n
59 entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:263
60 263             pushq   (%rdi)          /* RDI */
61 (gdb) n
62 entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:271
63 271             SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi
64 (gdb) n
65 273             popq    %rdi
66 (gdb) n
67 entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:274
68 274             popq    %rsp
69 (gdb) n
70 entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:275
71 275             USERGS_SYSRET64
72 (gdb) n
73 0x0000000000475349 in ?? ()

 

posted @ 2020-05-26 10:06  Atsuko  阅读(319)  评论(0编辑  收藏  举报