2019-2020-6 20199317《Linux内核原理与分析》第六周作业
第5章 系统调用的三层机制(下)
1 给MenuOS增加命令
首先进入LinuxKernel目录下,用rm -rf menu强制删除当前的menu目录,然后用git clone重新克隆一个新版本的menu,如下图所示:

新版本的menu中已经将上一章做的两个系统调用添加进去了,在test.c里我们看到添加的两个系统调用,如下图所示:



可以看到在test.c里的main()函数增加了增加了两行代码,一个是MenuConfig("time"),另一个是MenuConfig("time-asm"),从这里看出如果要给MenuOS增加新的命令,只需要使用MenuConfig命令,并增加对应的函数即可。
接下来进入menu目录下,运行make rootfs脚本就可以自动编译并自动生成根文件系统,这时打开了menu镜像,如下图所示:


在MenuOS菜单中输入help命令可以看到新增了 两条命令,输入time命令和time-asm命令可以看到运行结果,如下图所示:

2 使用gdb跟踪系统调用内核函数sys_time
用“qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S”命令先把内核启动一下,可以看到被冻结起来了,代码没有被运行,如下图所示:

再另外打开一个shell窗口,用Ctrl+Shift+O实现水平分割

启动gdb,把内核加载进来,建立连接。这里用到的代码为:
file linux-3.18./vmlinux
target remote:

在这个地方可以看到出现了一个问题:在输入target remote:1234命令时,显示连接超时,这是因为我关掉了另一个Shell打开的MenuOS菜单,重新输入qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S命令打开之后,显示的是连接成功。
接下来就可以设置断点了,在这里用break start_kernel设置一个断点,在此之前内核一直是stop状态,如果按“c”则继续执行,系统开始启动,并启动到start_kernel函数的位置停在断点处,如下图所示:

time系统调用是13号系统调用对应的内核处理函数,即sys_time。接下来在这里用break sys_time设置一个断点,按“c”继续执行,启动MenuOS后执行time命令,程序会停到sys_time这个函数的位置,time命令执行到一半将卡在那里,如下图所示:


通过list命令列出sys_time对应的代码如下图所示:

使用s命令单步执行进入get_seconds(),然后用gdb的finish命令把这个函数全部执行完,再单步执行,一直到return i,获得的就是当前系统时间time的数值。
当执行int 0x80时,CPU会自动跳转到system_call函数,所以我们把断点设置到system_call,并继续执行,如下图所示:

可以看到在MenuOS中执行time-asm命令时,还是停在了原先设定的sys_time这个位置,在system_call这个位置并不能停下。sys_call是一段特殊的汇编代码,只能调试系统调用的内核函数和其他内核函数的处理过程,且system_call还有一个函数原型声明,但它并不是一个普通的函数,,只是一段汇编代码的起点,且内部没有严格遵守函数调用堆栈机制,所以gdb不能完成跟踪执行过程的任务。
3 系统调用在内核代码中的处理过程
3.1 中断向量0x80和system_call中断服务程序入口的关系

在用户态中有一个系统调用xyz(),xyz()系统调用库函数里面用了SYSCALL(在这里即为int 0x80)来触发系统调用,其中中断向量0x80对应system_call中断服务程序入口。
在start_kernel函数里调用的trap_init函数中有一段代码如下:
#ifdef CONFIG_x86_32
set_system_trap_gate(SYSCALL_VECTOR, &system_call);
set_bit(SYSCALL_VECTOR, used_vectors);
#endif
这里通过set_system_trap_gate函数绑定了中断向量0x80(这里的SYSCALL_VECTOR是系统调用的中断向量0x80)和system_call中断服务程序入口后,一旦执行int 0x80,CPU就直接跳转到system_call这个位置来执行,即系统调用的工作机制在start_kernel里初始化好之后,CPU一旦执行到Int 0x80指令就会立即跳转到system_call的位置。
3.2 在system_call汇编代码中的系统调用内核处理函数
system_call这一段代码就是系统调用的处理过程,系统调用是一个特殊一点的中断(或称之为软中断),这一段代码中也有保存现场SAVE_ALL和恢复现场restore_all的过程。同时,system_call_table是一个系统调用的表,EAX寄存器传递的系统调用号,使用者在调用它时会根据EAX寄存器来调用对应的系统调用内核处理函数。
简化后的system_call代码为:
ENTRY(system_call)
RING0_INT_FRAME
ASM_CLAC
pushl_cfi %eax #保存系统调用号
SAVE_ALL #保存现场,将用到的所有CPU寄存器保存到栈中
GET_THREAD_INFO(%ebp) #ebp用于存放当前进程thread_info结构的地址
testl $_TIF_WORK_SYSCALL_ENTRY, TI_flags(%ebp)
jnz syscall_trace_entry
cmpl $(nr_syscalls), %eax #检查系统调用号(系统调用号应小于NR_syscalls)
jae syscall_badsys #不合法,跳入异常处理
syscall_call:
call *sys_call_table(,%eax,) #通过系统调用号在系统调用表中找到相应的系统调用内核处理函数,比如sys_time
movl %eax, PT_EAX(%esp) #保存返回值到栈中
syscall_exit:
testl $_TIF_ALLWORK_MASK, %ecx #检查是否有任务需要处理
jne syscall_exit_work #需要,进入syscall_exit_work,这里是最常见的进程调度时机
restore_all:
TRACE_IRQS_TRET #恢复现场
irq_return:
INTERRUPT_RETURN #iret
从entry(system_call)开始看这段代码,根据系统调用号来查sys_call_table表中的位置,调用系统调用对应的处理函数,在syscall_exit里面判断当前的任务是否需要处理syscall_exit_work,进入syscall_exit_work,这是最常见的进度调度时机点。
sys_call_table(,%eax,4)中每个表项占4个字节,所以先把系统调用号(EAX寄存器)乘以4,再加上sys_call_table分派表的起始地址,即得到系统调用号对应的系统调用内核处理函数的指针。sys_call_table分派表是由一段脚本根据linux-3.18.6/arch/x86/syscalls/syscall_32.tbl来自动生成的。
3.3 整体上理解系统调用的内核处理过程
system_call流程如下图所示:

总结一下:从系统调用处理过程的入口开始,可以看到SAVE_ALL保存现场,然后找到syscall_call和sys_call_table。call *sys_call_table(,%eax,4)就是调用了系统调用的内核处理函数,之后restore_all和最后有一个INTERRUPT_RETURN(iret)用于恢复现场并返回系统调用到用户态结束。在这个过程当中可能会执行syscall_exit_work,里面有work_pending,其中的work_notifysig是处理信号的。work_pending里还有可能调用schedule,这是一个非常关键的部分。
4 总结
在第四章的基础上,这一章进一步深入内核系统调用处理过程:在用户态下的xyz()就是一个API函数,是系统调用对应的API,其中封装了一个系统调用,这个系统调用 SYSCALL(即为int 0x80汇编语句)来触发中断,对应system_call内核代码的起点,即中断向量0x80对应的中断服务程序入口。从system_call内核代码的起点开始,先是SAVE_ALL保存现场,再是检查EAX寄存器中保存的系统调用号的正确性,接着根据系统调用号在sys_call_table这个分派表查找相对应的系统调用内核处理函数sys_xyz()的入口,得到这个函数的返回值保存到栈中,然后在syscall_exit里面判断当前的任务是否需要处理syscall_exit_work,进入sys_exit_work,进行进程调度,调度完之后就会跳转到restore_all,恢复现场返回系统调用到用户态。
2019-2020-6 20199317《Linux内核原理与分析》第六周作业的更多相关文章
- 2019-2020-1 20199329《Linux内核原理与分析》第九周作业
<Linux内核原理与分析>第九周作业 一.本周内容概述: 阐释linux操作系统的整体构架 理解linux系统的一般执行过程和进程调度的时机 理解linux系统的中断和进程上下文切换 二 ...
- 2019-2020-1 20199329《Linux内核原理与分析》第二周作业
<Linux内核原理与分析>第二周作业 一.上周问题总结: 未能及时整理笔记 Linux还需要多用 markdown格式不熟练 发布博客时间超过规定期限 二.本周学习内容: <庖丁解 ...
- 20169212《Linux内核原理与分析》第二周作业
<Linux内核原理与分析>第二周作业 这一周学习了MOOCLinux内核分析的第一讲,计算机是如何工作的?由于本科对相关知识的不熟悉,所以感觉有的知识理解起来了有一定的难度,不过多查查资 ...
- 20169210《Linux内核原理与分析》第二周作业
<Linux内核原理与分析>第二周作业 本周作业分为两部分:第一部分为观看学习视频并完成实验楼实验一:第二部分为看<Linux内核设计与实现>1.2.18章并安装配置内核. 第 ...
- 2018-2019-1 20189221 《Linux内核原理与分析》第九周作业
2018-2019-1 20189221 <Linux内核原理与分析>第九周作业 实验八 理理解进程调度时机跟踪分析进程调度与进程切换的过程 进程调度 进度调度时机: 1.中断处理过程(包 ...
- 2017-2018-1 20179215《Linux内核原理与分析》第二周作业
20179215<Linux内核原理与分析>第二周作业 这一周主要了解了计算机是如何工作的,包括现在存储程序计算机的工作模型.X86汇编指令包括几种内存地址的寻址方式和push.pop.c ...
- 2019-2020-1 20209313《Linux内核原理与分析》第二周作业
2019-2020-1 20209313<Linux内核原理与分析>第二周作业 零.总结 阐明自己对"计算机是如何工作的"理解. 一.myod 步骤 复习c文件处理内容 ...
- 2018-2019-1 20189221《Linux内核原理与分析》第一周作业
Linux内核原理与分析 - 第一周作业 实验1 Linux系统简介 Linux历史 1991 年 10 月,Linus Torvalds想在自己的电脑上运行UNIX,可是 UNIX 的商业版本非常昂 ...
- 《Linux内核原理与分析》第一周作业 20189210
实验一 Linux系统简介 这一节主要学习了Linux的历史,Linux有关的重要人物以及学习Linux的方法,Linux和Windows的区别.其中学到了LInux中的应用程序大都为开源自由的软件, ...
- 2018-2019-1 20189221《Linux内核原理与分析》第二周作业
读书报告 <庖丁解牛Linux内核分析> 第 1 章 计算工作原理 1.1 存储程序计算机工作模型 1.2 x86-32汇编基础 1.3汇编一个简单的C语言程序并分析其汇编指令执行过程 因 ...
随机推荐
- Android 这 13 道 ContentProvider 面试题,你都会了吗?
前言 作为 Android 的四大组件之一,ContentProvider 可以说是无处不在了. 但是对于我而言,开发过程中看似 ContentProvider 用得很娴熟,却一直没能形成一个完整的体 ...
- 基础练习1——ls的实现与递归
学习贵在坚持,兜兜转转,发现还是从基础做起吧,打好基础,才会长期的坚持下去... 第一个练习:shell命令 “ls"的实现与递归 1.简介:ls 的作用是列举当前目录下所有的目录和文件. ...
- [考试反思]NOIP模拟测试19:洗礼
[]260 []230[]210 []200[8]170[9]160 这套题一般,数据很弱,T1T2暴力都能A,而且都是一些思维题,想不到就爆0. 原因不明,很多一直很强的人在这一次滑铁卢了,于是我个 ...
- 2019 .NET China Conf:路一直都在,社区会更好
这个周末,我从成都飞到了上海参加了首届由社区组织而非官方(比如Microsoft)组织的.NET开发者峰会(.NET Conf).为此,我特意请了两天的假(周五+周六,对,我们是大小周,这周六要上班) ...
- JS面试题-<变量和类型>-JavaScript浅拷贝与深拷贝
前言 最开始了解到深浅拷贝是因为准备面试,但那个时候因为在学校做的项目比较少需求也比较简单,所以没有在项目中遇到这类问题,所以对这个问题就属于知道这个知识点,看过相关内容,却没有自己的总结,也没有深入 ...
- html5 textarea 写入换行的方法
html5 textarea 写入换行的方法<pre> <textarea id="fwe" class="selmiao" cols=&qu ...
- vue之注册自定义的全局js方法
前端开发的时候,总会需要写一些js方法,在vue框架中为了方便使用,可以考虑注册一个全局的js方法,下面是注册步骤: 1.0 可以在assets文件中的js文件下面新建一个js文件,如:yun.js- ...
- 利用SSH隧道技术穿越内网访问远程设备
本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/11899478.html 通常,我们用于调试的计算机无法远程访问位于局域网中的待调试设备. ...
- yum 配置文件 以及 语法
yum的配置文件 #vi /etc/yum.conf [main] cachedir=/var/cache/yum/$basearch/$releasever keepcache= debugleve ...
- 通过myclipse建立一个简单的Hibernate项目(PS:在单元测试中实现数据的向表的插入)
Hibernate的主要功能及用法: Ⅰ.Hibernate封装了JDBC,使Java程序员能够以面向对象的思想对数据库进行操作 Ⅱ.Hibernate可以应用于EJB的J2EE架构,完成数据的持久化 ...