《Linux内核分析》第五周 扒开系统调用的三层皮(下)

时间:2023-12-12 09:08:44

【刘蔚然 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

WEEK FIVE(3.21——3.27)扒开系统调用的“三层皮”(下)

SECTION 1 给MenusOS增加time和time-asm命令

1.操作步骤

进入实验楼

  1. 首先,强制(即 -rf命令)删除当前的menu
  2. 克隆一个新的menu(其中已经含有time了)。但是通过实践以及查询,实验楼中对连入外网进行了限制,所以在实验楼中输入下列语句会报错(也就是无法解析https://github.com的网址)

    git clone https://github.com/mengning/menu.git
  3. 进入menu之后,输入make rootfs,就可以自动编译

  4. 输入help,可以发现系统支持更多的命令:
    • help
    • version
    • quit
    • time
    • time-asm
  5. 那么,time和time-asm是如何实现的呢?
    • 进入test.c之后,查看main函数。与之相关的只有两条语句:
      • menuconfig("time","Show system Time",Time);
      • menuconfig("time-asm","Show system Time",Time(asm));
    • 此外,添加time函数和timeasm函数
  6. 附:关于网址解析解决过程(无法修改resolv.conf文件)后在实验楼的“问答”中查到实验环境限制连入外网
    • 《Linux内核分析》第五周 扒开系统调用的三层皮(下)
    • 《Linux内核分析》第五周 扒开系统调用的三层皮(下)
  7. 附:虚拟机中的实验过程(与20135211共同完成)
      • 按照第三周《构建一个简单的内核MenuOS》中的配置步骤,对kali虚拟机进行配置;然后更新内核版本到最新版
      • 《Linux内核分析》第五周 扒开系统调用的三层皮(下)
      • 启动init进程(由makefile中内容可以看出,init可执行文件是linktable.c,menu.c,test.c多个文件编译得到的);并且,此时的MenuOS内核已经添加了time以及time_asm系统调用
      • 《Linux内核分析》第五周 扒开系统调用的三层皮(下)
      • 《Linux内核分析》第五周 扒开系统调用的三层皮(下)
      • 再进行内核调试的时候,由于kali是64位环境,与实验楼中的调试方法略有不同

        • 启动内核到调试状态

          root@KaliYL:/usr/src/linux-source-4.4/arch/x86_64/boot# qemu-system-x86_64 -kernel bzImage -initrd /home/YL/rootfs.img -S -s//这里要加上-system-x86_64
        • 进行调试

          gdb /usr/src/linux-source-4.4/vmlinux
          (gbd) set arch i386:x86-64  
          (gdb)target remote:1234
          (gdb)b rest_init //(不能break 到 start_kernel,会报错)

2.小结

由此可见,给MenuOS增加time和time-asm命令需要:

  1. 更新menu代码
  2. 在main函数中增加menuconfig
  3. 增加对应的time和timeasm函数
  4. make rootfs

SECTION 2 使用gdb跟踪系统调用内核函数sys_time

1.操作步骤

  1. 进入内核,冻结启动

    qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
  2. 启动gdb
    • 《Linux内核分析》第五周 扒开系统调用的三层皮(下)
  3. 加载debug版本内核并连接到target地址
    • 《Linux内核分析》第五周 扒开系统调用的三层皮(下)
  4. 为处理time函数的系统调用systime设置断点之后,在menuOS中执行time。发现系统停在systime处。继续按n单步执行,会进入schedule函数。
  5. sys_time返回之后进入汇编代码处理,gdb无法继续跟踪
  6. 如果在syscall设置断点(entry32.S),然后输入c之后,发现是不会在sys_call处停下来的(因为这里是一处系统调用函数而不是正常函数)

系统调用在内核代码中的工作机制和初始化

1.int 0x80指令与systemcall是通过中断向量联系起来的,而API和对应的sys[函数]是通过系统调用号联系起来的

  • 《Linux内核分析》第五周 扒开系统调用的三层皮(下)
  • 这也印证了上周的学习内容:软中断触发系统调用,系统调用处理函数负责指派API对应的系统调用函数

2.系统调用机制的初始化

  1. trapgate函数中,涉及到了系统调用的中断向量和systemcall的汇编代码入口;一旦执行int 0x80,CPU直接跳转到system_call
  2. 《Linux内核分析》第五周 扒开系统调用的三层皮(下)

3.简化后便于理解的system_call伪代码

  1. systemcall的位置就在ENTRY(systemcall)处;(其他中断的处理过程与此类似)
    • 《Linux内核分析》第五周 扒开系统调用的三层皮(下)
    • 501行的syscalltable是系统调用表(比如,对于time函数而言,在此处调用的就应该是systime)
    • 503行after_call之后,保存返回值
    • 505行要exit的时候,会有一个syscallexitwork,否则直接返回用户态
    • 进入syscallexitwork之后,简化为下图所示的伪代码
      • 《Linux内核分析》第五周 扒开系统调用的三层皮(下)
      • 19行,判断当前的任务是否需要处理syscallexitwork;一般来说,系统调用都需要处理一些系统调度,也就是需要“work”
      • 从第30行开始,跳转到workpending。有worknotifying也就是处理信号;work_reschedle也就是重新调度(调度完之后再restore all)

4.简单浏览system_call到iret之间的主要代码

  1. SAVE_ALL:保存现场
  2. syscall_call:调用了系统调用处理函数
  3. restore all:恢复现场(因为系统调用处理函数也算是一种特殊的“中断”)
  4. syscallexitwork:如3.中所述
  5. INTERRUPT RETURN:也就是iret,系统调用到此结束

思考总结

1.理解makefile

1 #
2 # Makefile for Menu Program
3 #
4
//1.以下类似于变量声明,就是将右侧的变量在代码中用左侧替代
5 CC_PTHREAD_FLAGS = -lpthread
6 CC_FLAGS = -c
7 CC_OUTPUT_FLAGS = -o
8 CC = gcc //2.CC 是一个全局变量,它指定你的Makefile所用的编译器,一般默认是gcc
9 RM = rm
10 RM_FLAGS = -f
11 12 TARGET = test
13 OBJS= linktable.o menu.o test.o //3.Define a macro for the object files;.o文件是unix下的中间代码目标文件
14 15 all: $(OBJS)
16 $(CC) $(CC_OUTPUT_FLAGS) $(TARGET) $(OBJS) //4.即 gcc -o XXX
17 rootfs: //5.挂载根文件系统(root files system),见下方解释
18 gcc -o init linktable.c menu.c test.c -m32 -static -lpthread //该句以下是编译顺序
19 gcc -o hello hello.c -m32 -static
20 find init hello | cpio -o -Hnewc |gzip -9 > ../rootfs.img
21 qemu -kernel ../linux-3.18.6/arch/x86/boot/bzImage -initrd ../rootfs.img
22 .c.o:
23 $(CC) $(CC_FLAGS) $<
24 25 clean:
26 $(RM) $(RM_FLAGS) $(OBJS) $(TARGET) *.bak

参考:

分析:

  • makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作

  • 此外,在不同的系统中启动内核的起点有着不同的定义。在实际操作的kali虚拟机中,第21行即可更改为:

    qemu-system-x86-64 -kernel 4.3.0-kali-amd64 -initrd ../rootfs.img//kali是64位虚拟机

2.画出从systemcall到iret的流程图(参考entry32.S)