Linux 0.11源码阅读笔记-中断过程
Linux 0.11源码阅读笔记-中断过程
是什么中断
中断发生时,计算机会停止当前运行的程序,转而执行中断处理程序,然后再返回原被中断的程序继续运行。中断包括硬件中断和软件中断,硬中断是由外设自动产生的,软中断是程序通过int指令主动调用。中断产生时,会有一个中断号,根据中断号可在中断向量表中选择对应的中断处理程序执行。
中断在linux当中非常重要,是用户态代码与和心态代码相互切换运行的桥梁。进程调度依赖于时钟中断进入内核,系统调用也是依赖int 80软中断进入内核执行。
中断处理过程
以int 80中断为例。system_call过程代码是 int 80中断处理程序,是所有系统调用的入口。位于linux-0.11/kernel/system_call.s文件中。
sytem_call执行过程
- 保存运行环境:保存被中断程序的运行环境,包括指令地址(PC)、段等寄存器的值。
- 执行系统调用:根据系统调用号,查找系统调用函数地址表,并执行系统调用函数
- 执行调度程序:判断进程时间片是否处于不可运行状态(时间片用完或者处于阻塞状态),若不可运行,则执行调度程序。
- 进行信号处理:若被中断程序为用户态进程,会先判断信号是否产生,并执行相应的信号处理程序。
- 恢复运行环境:恢复被中断程序的运行环境。
system_call代码及注释
### int 0x80 - linux系统调用入口点(调用中断int 0x80,eax 中是调用号)
system_call:
cmpl $nr_system_calls-1,%eax # 调用号如果超出范围的话就在eax中置-1并退出
ja bad_sys_call
# 保存原段寄存器值
push %ds
push %es
push %fs
# edx、ecx、ebx作为系统调用的参数
pushl %edx
pushl %ecx # push %ebx,%ecx,%edx as parameters
pushl %ebx # to the system call
# 设置内核段
movl $0x10,%edx # set up ds,es to kernel space
mov %dx,%ds
mov %dx,%es
# fs指向用户程序的数据段。
movl $0x17,%edx # fs points to local data space
mov %dx,%fs
# 调用系统调用号对应的系统调用函数
call sys_call_table(,%eax,4) # 间接调用指定功能C函数
pushl %eax # 把系统调用返回值入栈
# 如果进程时间片(counter)用完或者状态(state)非就绪,则执行调度程序
movl current,%eax # 取当前任务(进程)数据结构地址→eax
cmpl $0,state(%eax) # state
jne reschedule
cmpl $0,counter(%eax) # counter
je reschedule
# 中断处理程序后半段,返回被中断程序继续执行。
ret_from_sys_call:
# 若被中断程序为0号进程,直接返回。
movl current,%eax # task[0] cannot have signals
cmpl task,%eax
je 3f # 向前(forward)跳转到标号3处退出中断处理
# 若被中断程序运行在内核态(例如其它可被中断中断程序),则直接返回
cmpw $0x0f,CS(%esp) # was old code segment supervisor ?
jne 3f
cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ?
jne 3f
# 若被中断程序为用户进程,则先处理进程的信号
# 通过信号位图,判断产生的信号,然后调用do_signal执行对应的信号处理程序
movl signal(%eax),%ebx # 取信号位图→ebx,每1位代表1种信号,共32个信号
movl blocked(%eax),%ecx # 取阻塞(屏蔽)信号位图→ecx
notl %ecx # 每位取反
andl %ebx,%ecx # 获得许可信号位图
bsfl %ecx,%ecx # 从低位(位0)开始扫描位图,看是否有1的位,若有,则ecx保留该位的偏移值
je 3f # 如果没有信号则向前跳转退出
btrl %ecx,%ebx # 复位该信号(ebx含有原signal位图)
movl %ebx,signal(%eax) # 重新保存signal位图信息→current->signal.
incl %ecx # 将信号调整为从1开始的数(1-32)
pushl %ecx # 信号值入栈作为调用do_signal的参数之一
call do_signal # 调用C函数信号处理程序(kernel/signal.c)
popl %eax # 弹出入栈的信号值
# 返回被中断程序
3: popl %eax # eax中含有上面入栈系统调用的返回值
popl %ebx
popl %ecx
popl %edx
pop %fs
pop %es
pop %ds
iret # 特权级中断返回指令
用户栈与内核栈之间的切换
特权级发生变化时,会涉及到内核栈和用户栈的切换。linux具有两个特权级,内核态(0)和用户态(3)。int指令可以从用户态转入内核态,iret指令可以从内核态返回用户态。

用户栈到内核栈
中断引起CPU特权级从3级到0级的变化,此时CPU会进行用户栈到内核栈的切换操作。
- 获取内核栈内存地址信息。CPU从当前任务状态段TSS(PCB)中取得新栈的段选择符和偏移值,内核栈指针从TSS的ss0(选择符)和esp0字段中获得。
- 将用户栈地址信息保存在内核栈中。将用户态栈指针和代码地址信息ss、esp、cs、eip压入内核栈中。
- 将ss、esp、cs、eip指向内核栈和代码地址。
内核栈到用户栈
iret指令引起CPU特权级从0级到3级的变化,此时CPU会进行内核栈到用户栈的切换操作。
iret指令将先前压入内核栈的用户进程栈和代码地址信息cs、esp、ss、esp信息从栈里弹出,加载到相应的寄存器中,重新执行用户进程。
进程调度的实现机制
每个用户进程有自己的内核栈,进程调度时,将栈寄存器指向需要运行的进程的内核栈,执行iret指令即可继续运行该进程。
内核栈的切换
进程调度的通过内核栈之间的切换,实现进程之间的切换运行。为什么每个进程都需要一个内核栈?进程需要保存运行状态信息(各种寄存器信息),等待被调度程序选中运行。

进程切换
进程切换的实质在于进程状态(上下文)的切换,即将CPU的当前进程状态替换成新进程的状态。被替换进程的进程状态会保存在其对应的tss数据结构中,等待恢复运行。任务寄存器TR会保存当前任务的TSS指针,TSS数据结构总保存进程的运行状态。
具体过程为:
- 当前进程通过中断进入内核,用户态ss、esp、cs、ip寄存器信息保存在内核栈中,切换到内核态运行,使用内核堆栈
- schedule调度程序时,将当前进程内核态运行信息保存在当前任务的tss数据结构中,然后切换到新进程的内核运行状态,在内核中运行新的进程。
- 新运行的进程通过iret指令从内核态转到用户态运行,用户态的运行状态信息在其内核栈中。

参考
- Linux 内核完全注释 内核版本0.11 - 赵炯
PS:错漏指出,请大佬指正,在评论区可交流学习。
Linux 0.11源码阅读笔记-中断过程的更多相关文章
- Linux 0.11源码阅读笔记-文件管理
Linux 0.11源码阅读笔记-文件管理 文件系统 生磁盘 未安装文件系统的磁盘称之为生磁盘,生磁盘也可以作为文件读写,linux中一切皆文件. 磁盘分区 生磁盘可以被分区,分区中可以安装文件系统, ...
- Linux 0.11源码阅读笔记-总览
Linux 0.11源码阅读笔记-总览 阅读源码的目的 加深对Linux操作系统的了解,了解Linux操作系统基本架构,熟悉进程管理.内存管理等主要模块知识. 通过阅读教复杂的代码,锻炼自己复杂项目代 ...
- Linux 0.11源码阅读笔记-总结
总结 Linux 0.11主要包含文件管理和进程管理两个部分.进程管理包括内存管理.进程管理.进程间通信模块.文件管理包含磁盘文件系统,打开文件内存数据.磁盘文件系统包括空闲磁盘块管理,文件数据块的管 ...
- Linux 0.11源码阅读笔记-块设备驱动程序
块设备驱动程序 块设备驱动程序负责实现对块设备数据的读写功能.内核代码统一使用缓冲块间接和块设备(如磁盘)交换数据,缓冲区数据通过块设备驱动程序和块设备交换数据. 块设备的管理 块设备表 内核通过一张 ...
- Linux 0.11源码阅读笔记-内存管理
内存管理 Linux内核使用段页式内存管理方式. 内存池 物理页:物理空闲内存被划分为固定大小(4k)的页 内存池:所有空闲物理页组成内存池,以页为单位进行分配回收.并通过位图记录了每个物理页是否空闲 ...
- Linux 0.11源码阅读笔记-文件IO流程
文件IO流程 用户进程read.write在高速缓冲块上读写数据,高速缓冲块和块设备交换数据. 什么时机将磁盘块数据读到缓冲块? 什么时机将缓冲块数据刷到磁盘块? 函数调用关系 read/write( ...
- Linux 0.11源码阅读笔记-高速缓冲
高速缓冲 概念 高速缓冲区是内存中的一块内存,在块设备与内核其它程序之间起着一个桥梁作用.内核程序如果需要访问块设备中的数据,都需要经过高速缓冲区来间接的操作. 高速缓冲区结构 高速缓冲区被划分为1k ...
- 【从头到脚品读 Linux 0.11 源码】第一回 最开始的两行代码
从这一篇开始,您就将跟着我一起进入这操作系统的梦幻之旅! 别担心,每一章的内容会非常的少,而且你也不要抱着很大的负担去学习,只需要像读小说一样,跟着我一章一章读下去就好. 话不多说,直奔主题.当你按下 ...
- linux 0.11 源码学习+ IO模型
http://www.cnblogs.com/Fredric-2013/category/696688.html
随机推荐
- LeetCode-059-螺旋矩阵 II
螺旋矩阵 II 题目描述:给你一个正整数 n ,生成一个包含 1 到 \(n^{2}\) 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix . 示例说明请见LeetCode ...
- Java IDE的历史变迁及idea的使用
Java开发工具的历史变迁 JCreator Jcreator是荷兰的Xinox Software公司开发的一个用于Java程序设计的集成开发环境(IDE),该公司成立于2001年: 官方网站:htt ...
- Linux服务器 I/O 原理和流程
计算机I/OI/O在计算机中指Input/Output,IOPS (Input/Output Per Second)即每秒的输入输出量(或读写次数),是衡量磁盘性能的主要指标之一.IOPS是指单位时间 ...
- WPF关于绑定与更新修改
看到一些资料与教程视频,在这里记录一下, 首先 我们先做好一个公共的INotifyPropertyChanged事件,也就是通知更新 public class ViewModelBase : INot ...
- 软件工程homework-001
一. 回顾你过去将近3年的学习经历 1.当初你报考的时候,是真正喜欢计算机这个专业吗? 答:不喜欢,高中时候就已经对计算机比较抵触了,家里小时候比较富裕,九几年就在日本买了第一批家用台式机 ...
- Github又悄悄升级了,这次的变化是大文件的存储方式
目录 简介 LFS和它的安装 LFS的使用 从LFS中删除文件 从LFS中拉取代码 转换历史数据到LFS 总结 简介 github是大家常用的代码管理工具,也被戏称为世界上最大的程序员交友网站,它的每 ...
- 6月6日 python学习总结 jQuery (三)
1. 常用事件 1. hover #鼠标悬停监听 2. keydown和keyup #键盘按键 按下/抬起 3. change #监听值的改变 全部输入完失去焦点后 4. focus和blur # 获 ...
- pthread_once函数
http://blog.csdn.net/lmh12506/article/details/8452659 pthread_once()函数详解 在多线程环境中,有些事仅需要执行一次.通常当初始化应用 ...
- Mysql等保部分加固
一.等保要求:操作系统和数据库系统管理用户身份标识应具有不易被冒用的特点,口令应有复杂度要求并定期更换. 实施步骤: 启用口令复杂审核(5.6.6版本以上,才有此模块功能,需要重启mysql). 查看 ...
- 生产环境频繁内存溢出,原来就是因为这个“String类”
摘要:如果在程序中创建了比较大的对象,并且我们基于这个大对象生成了一些其他的信息,此时,一定要释放和这个大对象的引用关系,否则,就会埋下内存溢出的隐患. 本文分享自华为云社区<[高并发]你敢信? ...