分析system_call中断处理过程

上周我们使用gcc内嵌汇编调用系统调用,这次我们具体分析下过程。

将getpid嵌入menuos

代码从github下载,步骤如下:

1. 增加一个函数,getpid
2. 在main中添加MenuConfig("getpid","Show Pid", Getpid);
3. 重新编译 make roofs
4. 此时启动 执行getpid就可以看到打印出pid为1

见下面两张图:



menuos的原理

其实这个很简单,在上上周我们分析过linux内核的启动过程,1号进程,就是init,它的执行逻辑是/sbin/bin,所以这里的menuos就是编写的init。

这里注意,linux内核源码不包含/sbin/bin,一般的发行版使用的是FreeBSD的版本。

这里的menuos就是自己制作init,然后嵌入linux内核编译出的镜像。

所以在menuos中执行getpid,得到的pid为1,见上图。

中断处理

中断分为两种,一种是外中断,由外部的IO设备产生,另一种是内中断,也称为异常,由CPU内部产生。

这里涉及到一个很重要的概念,叫做中断上下文。

CPU的运行状态,分为以下三种:

1. 内核态,运行于进程上下文,内核代表进程运行于内核空间。
2. 内核态,运行于中断上下文,内核代表硬件运行于内核空间。
3. 用户态,运行于用户空间。

从上面,我们可以看出,中断上下文不与任何的进程相关联,仅仅运行在内核空间,而且一般只访问内核数据。

这里顺便总结下进程上下文:

1. 用户级上下文: 正文、数据、用户堆栈以及共享存储区;
2. 寄存器上下文: 通用寄存器、程序寄存器(IP)、处理器状态寄存器(EFLAGS)、栈指针(ESP);
3. 系统级上下文: 进程控制块task_struct、内存管理信息(mm_struct、vm_area_struct、pgd、pte)、内核栈。

其实就是包含三个内容:用户数据、硬件状态(主要是寄存器)、内核数据。

所以,当发生进程调度时,要将三个上下文全部进行切换。

当进行系统调用时,仅仅需要切换寄存器上下文。

相比进程上下文,中断上下文仅仅包含一些寄存器的信息。发生中断时,所谓的保护现场和恢复现场,指的就是这些寄存器信息。

分析system_call

代码如下:

# system call handler stub
ENTRY(system_call)
RING0_INT_FRAME # can't unwind into user space anyway
ASM_CLAC
pushl_cfi %eax # save orig_eax
SAVE_ALL # 保存系统寄存器信息
GET_THREAD_INFO(%ebp) # 获取thread_info结构的信息
# system call tracing in operation / emulation
testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp) # 测试是否有系统跟踪
jnz syscall_trace_entry # 如果有系统跟踪,先执行,然后再回来
cmpl $(NR_syscalls), %eax # 比较eax中的系统调用号和最大syscall,超过则无效
jae syscall_badsys # 无效的系统调用 直接返回
syscall_call:
call *sys_call_table(,%eax,4) # 调用实际的系统调用程序
syscall_after_call:
movl %eax,PT_EAX(%esp) # 将系统调用的返回值eax存储在栈中
syscall_exit:
LOCKDEP_SYS_EXIT
DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt
# setting need_resched or sigpending
# between sampling and the iret
TRACE_IRQS_OFF
movl TI_flags(%ebp), %ecx
testl $_TIF_ALLWORK_MASK, %ecx # 检测是否所有工作已完成
jne syscall_exit_work # 未完成,则去执行这些任务 restore_all:
TRACE_IRQS_IRET # iret 从系统调用返回

这段代码的逻辑主要就是:

1. 保存寄存器上下文,
2. 检查系统调用号是否合法
3. 执行系统调用
4. 检查是否还有别的工作需要完成
5. 退出系统调用,返回到用户态

我们继续跟踪里面的syscall_exit_work,它用来处理系统调用之后,未完成的工作

syscall_exit_work:
testl $_TIF_WORK_SYSCALL_EXIT, %ecx # 测试syscall的工作完成
jz work_pending
TRACE_IRQS_ON
ENABLE_INTERRUPTS(CLBR_ANY) # could let syscall_trace_leave() call
# schedule() instead
movl %esp, %eax
call syscall_trace_leave
jmp resume_userspace
END(syscall_exit_work)

这一段的主要作用还是进入work_pending

work_pending代码:

work_pending:
testb $_TIF_NEED_RESCHED, %cl # 判断是否需要调度
jz work_notifysig # 不需要则跳转到work_notifysig
work_resched:
call schedule # 调度进程
LOCKDEP_SYS_EXIT
DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt
# setting need_resched or sigpending
# between sampling and the iret
TRACE_IRQS_OFF
movl TI_flags(%ebp), %ecx
andl $_TIF_WORK_MASK, %ecx # 是否所有工作都已经做完
jz restore_all # 是则退出
testb $_TIF_NEED_RESCHED, %cl # 测试是否需要调度
jnz work_resched # 重新执行调度代码

这段的逻辑很清楚

1. 先检查是否需要调度,
2. 如果是,则进行进程调度,之后再次判断。
3. 如果不需要调度,那么去执行work_notifysig,处理信号

work_notifysig代码:

work_notifysig:				# 投递信号
#ifdef CONFIG_VM86
testl $X86_EFLAGS_VM, PT_EFLAGS(%esp) # 判断8086虚模式,也就是保护模式
movl %esp, %eax
jne work_notifysig_v86 # 返回到内核空间
1:
#else
movl %esp, %eax
#endif
TRACE_IRQS_ON
ENABLE_INTERRUPTS(CLBR_NONE)
movb PT_CS(%esp), %bl
andb $SEGMENT_RPL_MASK, %bl
cmpb $USER_RPL, %bl
jb resume_kernel
xorl %edx, %edx
call do_notify_resume # 将信号投递到进程
jmp resume_userspace # 恢复用户空间 #ifdef CONFIG_VM86 # 如果是VM86模式,需要保存状态信息
ALIGN
work_notifysig_v86:
pushl_cfi %ecx # save ti_flags for do_notify_resume
call save_v86_state # 保存虚模式下的状态
popl_cfi %ecx
movl %eax, %esp
jmp 1b # 跳转到上面的代码,执行do_notify_resume
#endif
END(work_pending)

这段代码主要是处理信号:

1. 先检查是否是8086保护模式
2. 如果是,那么需要先保存虚模式下的状态信息
3. 然后跳转到之前的代码继续执行
4. 将信号投递到进程
5. 恢复用户空间

之后就是返回系统调用

流程图总结

如图:

总结

系统调用中断,本质上也是一个保存状态、进行处理、返回并恢复状态的过程。

署名信息

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

分析system_call中断处理过程的更多相关文章

  1. 20135202闫佳歆--week5 分析system_call中断处理过程--实验及总结

    week 5 实验:分析system_call中断处理过程 一.使用gdb跟踪分析一个系统调用内核函数(上周选择那一个系统调用)--getpid 复习视频: 如何实现? - 更新menu代码到最新版 ...

  2. 作业五:分析system_call中断处理过程

    分析system_call中断处理过程 一.MesuSO增加getpid和getpid-asm 二.使用GDB跟踪系统调用内核函数sys_getpid 分析system_call中断处理过程 使用gd ...

  3. 《Linux内核分析》第五周:分析system_call中断处理过程

    实验 分析system_call中断处理过程 使用gdb跟踪分析一个系统调用内核函数(您上周选择那一个系统调用),系统调用列表参见http://codelab.shiyanlou.com/xref/l ...

  4. Linux内核分析-分析system_call中断处理过程

    姓名:江军 ID:fuchen1994 分析system_call中断处理过程 使用gdb跟踪分析一个系统调用内核函数(您上周选择那一个系统调用),系统调用列表参见http://codelab.shi ...

  5. Linux内核分析第五周学习总结——分析system_call中断处理过程

    Linux内核分析第五周学习总结--分析system_call中断处理过程 zl + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/U ...

  6. 通过分析system_call中断处理过程来深入理解系统调用

    通过分析system_call中断处理过程来深入理解系统调用 前言说明 本篇为网易云课堂Linux内核分析课程的第五周作业,上一次作业中我以2个系统调用(getpid, open)作为分析实例来分析系 ...

  7. 实验五:分析system_call中断处理过程

    原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 如果我写的不好或者有误的地方请留言 ...

  8. 实验五分析system_call中断处理过程

    一.实验要求: 1.使用gdb跟踪分析一个系统调用内核函数 2.根据本周所学知识分析系统调用的过程,从system_call开始到iret结束之间的整个过程,并画出简要准确的流程图 二.实验步骤: 1 ...

  9. Linux内核设计第五周学习总结 分析system_call中断处理过程

    陈巧然原创作品 转载请注明出处   <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 使用gdb跟踪分析一 ...

随机推荐

  1. EXCEL工作表保护密码忘记了,如何撤消工作表保护?

    按下面步骤操作,如果不会发邮件给我吧 SamRichard@live.cn 1\打开文件 2\工具---宏----录制新宏---输入名字如:aa 3\停止录制(这样得到一个空宏) 4\工具---宏-- ...

  2. (引用 )自动化测试报告HTMLtestrunner

    1>下载HTMLTestRunner.py文件,地址为: http://tungwaiyip.info/software/HTMLTestRunner.html   Windows平台: 将下载 ...

  3. 简单的jquery插件写法之一

    http://jsfiddle.net/kyu0hdmx/embedded/#HTML

  4. CentOS7安装mysql5.7.11

    开始安装 yum update yum install wget wget http://repo.mysql.com/mysql57-community-release-el7-7.noarch.r ...

  5. 【实(dou)力(bi)首(mai)发(meng)】第四次CCF软件能力认证题解

    这次的题总体上相对前三次偏简单.由于实力有限,就分析前四题.     试题编号:    201503-1 试题名称:    图像旋转 时间限制:    5.0s 内存限制:    256.0MB 问题 ...

  6. shell 脚本注意事项

    设脚本名为test.sh 第一行应该为#! /bin/bash 1.运行和调试的结果是不一样的 调试 sh -x test.sh  这时在计算两个数的和sum=$[$a+$b]时得到sum=3+4,而 ...

  7. git注意

    .ssh生成的公钥全部复制然后粘贴到gitHub上,公钥是放在.ssh文件夹里面的,复制前先找到这个文件夹 提示出错信息:fatal: remote origin already exists. 解决 ...

  8. C++调用动态库中的虚基类成员函数时总是进错函数

    原创文章,转载请注明作者与本文原始URL. 问题描述:最近遇到这样一个问题,在调用C++的一个成员函数时,总是进错函数.在调用 pMsg->GetMsgContent() 的时候,总是进入到 p ...

  9. Android Studio安装genymotion模拟器

    1.Genymotion的安装: Genymotion无疑是目前最快最好用的模拟器.官网下载地址:https://www.genymotion.com/ 先注册,然后下载,安装VirtualBox最简 ...

  10. redis-cli 命令总结

    redis-cli 命令总结 Redis提供了丰富的命令(command)对数据库和各种数据类型进行操作,这些command可以在Linux终端使用.在编程时,比如使用Redis 的Java语言包,这 ...