惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cisco Talos Blog
Cisco Talos Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
爱范儿
爱范儿
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Jina AI
Jina AI
雷峰网
雷峰网
The Register - Security
The Register - Security
The Cloudflare Blog
博客园 - 【当耐特】
M
MIT News - Artificial intelligence
I
InfoQ
博客园 - 三生石上(FineUI控件)
H
Help Net Security
博客园 - 司徒正美
Vercel News
Vercel News
WordPress大学
WordPress大学
S
SegmentFault 最新的问题
云风的 BLOG
云风的 BLOG
B
Blog
Google DeepMind News
Google DeepMind News
B
Blog RSS Feed
L
LangChain Blog
人人都是产品经理
人人都是产品经理
GbyAI
GbyAI
T
The Blog of Author Tim Ferriss
T
Tailwind CSS Blog
aimingoo的专栏
aimingoo的专栏
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Recorded Future
Recorded Future
小众软件
小众软件
Martin Fowler
Martin Fowler
罗磊的独立博客
Stack Overflow Blog
Stack Overflow Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
酷 壳 – CoolShell
酷 壳 – CoolShell
腾讯CDC
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Apple Machine Learning Research
Apple Machine Learning Research
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - Franky
Hugging Face - Blog
Hugging Face - Blog
Y
Y Combinator Blog
V
Visual Studio Blog
F
Fortinet All Blogs
Microsoft Azure Blog
Microsoft Azure Blog
大猫的无限游戏
大猫的无限游戏
Engineering at Meta
Engineering at Meta
N
Netflix TechBlog - Medium
V
V2EX
Blog — PlanetScale
Blog — PlanetScale

Posts on Changkun's Blog

Why High-Output Systems Are Often the First to Stop Growing Dark Forest Theory: A Formal Derivation Agents (or Humans) in Goal-Directed and Goalless Environments: On Pipelines, Priors, and the Rhythm Between Exploration and Exploitation At the Boundary of Self-Reference: From Stable Structures in Artificial Intelligence to the Self as a Recursive Model in an Open Dissipative System 2023 Reading List 2022 Reading List 2021 Reading List Are PSS/USS and RSS Actually the Same Thing? Performance Differences from Page Faults vs. Prefetching 2020 Year-End Review Migration with Zero Downtime 2020 Reading List The All in Go Stack Pointers Might Not Be Ideal for Parameters Eliminating A Source of Measurement Errors in Benchmarks Setup Wordpress in 10 Minutes 我为什么不再写博客了? 2019 年终总结 2018-2019 读书清单 Ten years of blogging Rethinking the Reflections on Communications and Trusts 2018 年终总结 Go source code study is open source Go source study: unsafe Pattern Go source study: sync.Pool Go runtime programming A Million WebSocket and Go Designing Asynchronous RESTful APIs 分布式杂谈01:CAP 理论的误解 Issues of Human-Bot Interaction 压缩法与深度网络的泛化性 Go in 1 Hour UMSLT04: The Past and Present of SGD UMSLT03: A Gentle Start of Learning Theory UMSLT02: A Breif History of Neural Networks UMSLT01: A Breif History of Regularization 不笑不足以为道 论文笔记:Generalization in Deep Learning 2017 年终总结 2017 读书清单 深度学习的泛化理论简介 删除 GitHub 上已经提交的敏感信息 硕士生涯的第一年就这样告一段落了 人肉计算(10): 系统参与激励 人肉计算(9): 陷阱的解法 别聊,一聊你就暴露 人肉计算(8): 人肉计算与数据科学中的陷阱 人肉计算(7): 社会行为分析 Hexo + GitHub + Travis CI + VPS 自动部署 人肉计算(6): 预测市场 人肉计算(5): 信用风险评级模型 读书与回报 瞎扯: 对现代企业理论与当下IT企业的商业模式和信息产业链的规律性的思考 人肉计算(4): 输入数据聚合与PageRank 又一次打整了一下博客 人肉计算(3): 输入数据聚合与链路预测 人肉计算(2): 意图博弈 GWAPs 人肉计算(1): 众包与群众智慧 对后辈同学在计算机专业上的答疑与解惑 在德国的医疗及住院体验 这可能不是一个技术博客了 实验楼楼赛第3期-Python-题解 迅速更换了 DISQUS Electron 深度实践总结 良好的编码体验的三个方面 2016 年终总结 2016 读书清单 最近在着手写的文章 微信小程序文档极致总结 谈谈过去三个月在实验楼的实习经历 Built a Desktop Client for My Blog Guacamole 源码分析与 VNC 中 RFB 协议的坑 《高速上手 C++11/14》正式发布 Docker 极速入门教程02 - 镜像与容器管理 Docker 极速入门教程01 - 基本概念和操作 阶段性沉默 ELK+Redis 最佳实践 终于全面启用了 HTTPS 苹果开源了LZFSE无损压缩 Hash 碰撞的一种思路 记一次完整的 Kaldi-TIMIT 示例运行 Kaldi 上的 TIMIT 例子 Kaldi 安装与部署 从科研写作谈起 Swift API 设计指南 有趣的人类 所以其实论文并没有什么鬼用 Githug 通关记录及指南 小结一下这学期的收获 2015 读书清单 2015 年终总结 负能量爆表 转眼就快两个月了 博客迁移记录 大三总结 这个世界,终究不会是我们的。 Linux 内核分析 之六:Linux 内核创建进程的过程 小说「泽缘」 大创项目的标题真是每年都在考验同学们的想象力啊 Linux 内核分析 之四:使用库函数API和嵌入汇编两种方式使用同一个系统调用
Linux 内核分析 之五:system_call中断处理过程的简要分析
Changkun Ou · 2015-04-05 · via Posts on Changkun's Blog

Published at发布于:   |   PV/UV: /   |   Reading阅读: 2 min

说明

欧长坤 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 这学期学校恰好有操作系统的课程,上个学习就开始寻思研究研究Linux内核代码,恰好MOOC有这个课程,遂选了此课。

一、准备工作

本周的实验是第四周实验的自然延伸。同样的,它也并不难。 我们可以在http://codelab.shiyanlou.com/xref/linux-3.18.6/arch/x86/syscalls/syscall_32.tbl中查看系统调用号。

如果你上周没有看过我的文章,你可以看看这里。我使用了systeminfo这样一个系统调用。

二、实验过程:跟踪系统调用

我们使用了sysinfo这个库函数API。 下面的三张实验图显示了我们已经成功的使用gdb调试跟踪到了sysinfo这个系统调用(函数名为sys_sysinfo),但是随后使用next命令继续调试时,显然无法再继续调试了,因为不能直接使用gdb来对使用汇编代码编写的system_call进行调试和追踪。这里有一篇文章介绍了如何使用gdb来调试汇编代码,我就不继续展开了:http://www.doc88.com/p-0781911176267.html

三、system_call过程分析

不说废话,老师上课对system_call的分析过程非常清楚,伪代码抓住了重点,我们很容易就能够画出下面的流程图:

接下来我们逐行分析系统调用处理过程的汇编伪代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
.macro INTERRUPT_RETURN  ; 中断返回
    iret
.endm
.macro SAVE_ALL          ; 保护现场
    ...
.macro RESTORE_INT_REGS
    ...
.endm

ENTRY(system_call)
    SAVE_ALL
syscall_call:
    call *sys_call_table(,%eax,4)
    movl %eax, PT_EAX(%esp)  ; store the return value
syscall exit:
    testl $_TIF_ALLWORK_MASK, %ecx # current->work
    jne syscall_exit_work
restore_all:
    RESTORE_INT_REGS
irq_return:
    INTERRUPT_RETURN      ; 到这里就算执行完了
ENDPROC(system_call)

syscall_exit_work:
    testl  $_TIF_WORK_SYSCALL_EXIT, %ecx
    jz work_pending
END(syscall_exit_work)

work_pending:
    testb $_TIF_NEED_RESCHED, %cl
    jz work_notifysig
work_resched:
    call schedule
    jz restore_all
work_notifysig:
    ...                  ; deal with pending signals
END(work_pending)

可以看到无论是中断返回(ret_from_intr) ,还是系统调用返回,都使用了 work_pendingresume_userspace

对于宏SAVE_ALL来说,这条语句会把将寄存器的值压入堆栈当中,压入堆栈的顺序对应struct pt_regs,出栈时,这些值传递到struct pt_regs的成员,实现从汇编代码向C程序传递参数。struct pt_regs可以在arch/x86/include/asm/ptrace.h中查看。

而接下来为何直接就跳到了sys_call_talbe呢,这里伪代码里面没有说明清楚情况,我们来看真实代码片段:

1
2
3
4
5
6
7
8
9
    SAVE_ALL 
    GET_THREAD_INFO(%ebp)  
                    # system call tracing in operation / emulation  
    testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)  
    jnz syscall_trace_entry  
    cmpl $(NR_syscalls), %eax  
    jae syscall_badsys  
syscall_call:  
    call *sys_call_table(,%eax,4)  

首先,GET_THREAD_INFO 宏可以获得当前进程的thread_info结构的地址,获取当前进程的信息。而jnz syscall_trace_entry比较testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)的结果不为零的时候跳转。如此可对用户态进程传递过来的系统调用号的合法性进行检查。如果不合法则跳转到syscall_badsys标记的命令处。

所以,比较结果大于或者等于最大的系统调用号的时候跳转,合法则跳转到相应系统调用号所对应的服务例程当中,也就是在sys_call_table表中找到了相应的函数入口点。由于sys_call_table表的表项占4字节,因此获得服务例程指针的具体方法是将由eax保存的系统调用号乘以4再与sys_call_table表的基址相加。 然后,进入到系统调用表查找到系统调用服务程序的入口函数的地址,再进行跳转。 这样便完成了对sys_call_table的进入。

接下来伪代码马上就来到了syscall_exit,真实代码情况也是如此,在执行完syscall_call后,已经没有比较在继续处理了,因此马上接触并退出系统调用。 但是这个退出的过程就变得非常的复杂了。

syscall_exit开始到irq_return的真实代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
syscall_exit:  
    LOCKDEP_SYS_EXIT  
    DISABLE_INTERRUPTS(CLBR_ANY)
    TRACE_IRQS_OFF  
    movl TI_flags(%ebp), %ecx  
    testl $_TIF_ALLWORK_MASK, %ecx  # current->work  
    jne syscall_exit_work  
restore_all:  
    TRACE_IRQS_IRET  
restore_all_notrace:  
    movl PT_EFLAGS(%esp), %eax  # mix EFLAGS, SS and CS 
    movb PT_OLDSS(%esp), %ah  
    movb PT_CS(%esp), %al  
    andl $(X86_EFLAGS_VM | (SEGMENT_TI_MASK << 8) | SEGMENT_RPL_MASK), %eax  
    cmpl $((SEGMENT_LDT << 8) | USER_RPL), %eax  
    CFI_REMEMBER_STATE  
    je ldt_ss           # returning to user-space with LDT SS
restore_nocheck:  
    RESTORE_REGS 4          # skip orig_eax/error_code  
irq_return:  
    INTERRUPT_RETURN 

这段处理过程就涉及到内核中断返回时候涉及到得一些非常繁杂的细节了,比如work_pending等其它细节工作,我们想要彻底弄清楚,在这个篇幅里显然是不够的,因此我也不再继续赘述。值得注意的事情是work_pending后回涉及到处理进程信号量的问题,这里涉及到了很多进程调度的内容,我们在以后的关于进程部分的课程作业中再详细谈及它吧。

完成这些其余的工作之后,最终来到了irq_return,在这里使用宏 INTERRUPT_RETURN 实际上就是iret指令,恢复现场,最终完成了系统调用中断处理的返回。

四、总结

好了,我们来总结一下:

1、用户态到内核态需要int 0x80进行中断,只有生成了中断向量后才可以切换状态;

2、中断处理让CPU停止当前工作转为执行系统内核中预设的一些任务,因此必须要对当前CPU执行的任务进行执行现场的保护工作,并对一些其他杂七杂八的工作进行检查,完成调用后,再进行检查,才能执行iret返回。

3、系统内部调用涉及CPU架构等内容,不同的CPU对于系统调用的汇编具体代码是不一样的。