深入分析任务切换与堆栈 by Liu Wanli 下面可以直接链接文章出处:
http://www.linuxforum.net/forum/showflat.php?Cat=&Board=linuxK&Number=301272&fpart=all
关键字:时间中断、任务切换、堆栈、LINUX0.01
引言:
任务切换与堆栈的关系怎样?很多朋友可能不知道她们之间有什么关系,还有一些朋友可能认为他们之间不会有太大的关系(文献4)。而我认为:任务切换跟堆栈有着密切的关系!下面是我对它们之间关系进行的探讨,这里的任务切换我指的是发生时间中断时进行强制调度发生的任务切换,所以下面考虑堆栈时我是从中断开始探讨的。当然,我在进行这方面分析的时候,也愈感它们的复杂性,错误之处在所难免,望各位朋友多多指正。建议读者水平:* * *
一、时间中断。
假设一个进程在用户空间执行时(这时CPL=3),发生了时间中断。这时的中断处理过程为(文献1:P438):
1、根据中断向量号找到中断门描述符;
2、从描述符中分解出选择子、偏移量、属性字段并进行相应的特权检查;
3、根据描述符类型转入相应中断处理程序中去执行。
好象太肤浅了一些?再看看(文献1:P439图10.20):
1、选择子为空?no继续;
2、取得对应描述符;(描述符中DPL属性应该为0,文献3中断向量初始化部分)
3、存储段描述符?yes继续;
4、非一致代码段且DPL<CPL且段存在?yes继续;根据假设CPL=3,DPL=0,所以到5!
5、切换成内层堆栈!
如何切换??因为一个进程有用户空间堆栈和系统空间(也叫内核空间)堆栈,用户空间堆栈在哪儿我不管,它应该是由该进程的任务状态段TSS中SS2指定,SS0指定系统空间堆栈,它和该进程任务结构task_struct共占一页空间(见文献3:sched.c)。所以这里的切换成内层堆栈应该是将该进程的TSS中SS0的值赋给SS寄存器。
6、使RPL=0;
7、把描述符装入CS;
8、入口偏移越界?no继续;
9、EFLAG、CS、EIP入栈;呵,开始栈的改变了哟!
10、TF=0、NT=0、IF=0;这里考虑的是中断门。
11、转入处理程序。
别急,先看看现在的堆栈情况:
| 外层EIP |
| 外层CS |
| EFLAG |
| 外层ESP |
| 外层SS |
-----------
这个栈在什么地方呢?这相当重要!这是在当初切换至内层堆栈时进行的,即已经到了当前进程的系统空间堆栈,也就是跟task_struct共占一页的那个堆栈。而这里保存的就是该进程在用户空间的堆栈和代码信息,以便中断完成后恢复进程执行。
二、中断处理程序。
这里指的是时间中断。(文献3:system_call.c: timer_interrupt:)
timer_interrupt:
1. push %ds
2. push %es
3. push %fs
4. pushl %edx
5. pushl %ecx
6. pushl %ebx
7. pushl %eax
8. movl $0x10,%eax
9. mov %ax,%ds
10. mov %ax,%es
11. movl $0x17,%eax
12. mov %ax,%fs
13. incl jiffies
14. movb $0x20,%al
15. outb %al,$0x20
16. movl CS(%esp),%eax
17. andl $3,%eax
18. pushl %eax
19. call do_timer
20. andl $4,%esp
21. jmp ret_from_sys_call
1-7行为压栈操作,这是我们所关心的!16-18即是将CPL(CPL=CS&3)压栈,目的是用于do_tiemr(long cpl)函数。那么在执行到do_timer里面时的堆栈怎么样呢?看看:
|返回地址 |
-----------
| CPL |
| eax |
| ebx |
| ecx |
| edx |
| fs |
| es |
| ds |
-----------
| 外层EIP |
| 外层CS |
| EFLAG |
| 外层ESP |
| 外层SS |
-----------
上面的返回地址当然就是调用do_timer后的那条语句,即20行的andl $4,%esp语句。那么是不是do_timer函数执行完就返回到这儿呢,也是,当然要复杂得多,因为在do_timer()函数中调用了schedule()并且发生了任务切换!哎,好麻烦,也不知道什么时候才能返回到这儿来呢,还是一步一步来看吧。
三、do_timer()(文献3:sched.c: do_timer())
void do_timer(long cpl)
{
...
if ((--current->counter)>0) return;
current->counter=0;
if(!cpl)return;
schedule();
}
省略号为无关紧要的两条语句,进行进程的计时。如果时间片没有用完(counter>0)或CPL为0,不发生调度直接返回,当然这里也不是就直接返回到以前执行的进程空间,而是返回到do_timer()中,注意开始的返回地址,然后再通过iret指令从中断处理返回到进程中去。当然,根据我们的假设,这儿CPL应该为3,因为是在用户空间发生中断的。我们要从最复杂的情况来讨论这个问题。好了,就让我们进入到中心点吧,请进schedule()。
四、schedule()。 (文献3:sched.c: schedule())
void schedule(void )
{
int next;
...
switch_to(next);
}
呵,这里我又省略了几句代码,它执行的是调度算法,即从所有状态为‘运行’的进程中找出下一个要执行的进程,然后将编号赋给next。进行切换!
switch_to()是一个宏,它在(文献3: sched.h)中定义:
#define switch_to(n) { /
struct (long a,b;} __tmp; /
__asm__("cmpl %%ecx,current /n/t" /
"je 1f/n/t" /
"xchgl %%ecx, current/n/t" /
"movw %%dx, %1/n/t" /
"ljmp *%0/n/t" /
"cmpl %%ecx, %2/n/t" /
"jne 1f/n/t" /
"clts/n" /
"1:" /
::"m" (*&__tmp.a), "m" (*&__tmp.b), /
"m" (last_task_used_math),"d" _TSS(n), "c" ((long) task[n])); /
}
这是任务切换的关键代码,原理是直接通过TSS来进行任务的切换(文献1:P420)。那我就将这段关键代码逐行解说一下吧。cmpl %%ecx, current,比较任务n是不是当前进程,如果是当然就不用切换了,直接结束schedule()。xchgl %%ecx,current,current指针指向任务n的任务结构,ecx寄存器保存当前进程的任务结构指针。movw %%dx, %1, 使__tmp.b=‘GDT中第n个任务的TSS选择子’,注意_TSS(n)是求选择子的宏!ljmp *%0,这句代码就是真正的任务切换罗, AT&T语法的ljmp相当于INTEL的jmp far SECTION:OFFSET指令格式,它的绝对地址前加*号。这里引用(文献1:P420)一段话:当段间转移指令JMP所含指针的选择子指示一个可用任务状态段TSS描述符时,正常情况下就发生从当前任务到由该可用任务的切换。目标任务的入口点由目标任务TSS内的CS和EIP字段所规定的指针确定,这样的JMP指令内的偏移被丢弃。再具体的任务切换你也许得翻翻(文献1:P421),这里我只讲有关堆栈的处理,那就是把寄存器现场保存到当前任务的TSS。把通用寄存器、段寄存器、EIP及EFLAGS的当前值保存到当前的TSS中。保存的EIP的值是返回地址,指向引起任务切换指令的下一条指令;恢复目标任务的寄存器现场,根据保存在TSS中的内容恢复各通用寄存器、段寄存器、EFLAG、EIP。好了,基本概念就引用这么多,那么,刚才提到的进程马上要被切换出去了,它保存TSS中EIP是什么呢?显然,根据刚才的分析应该是cmpl %%ecx, %2这条指令。这意味着什么呢?这就是说,如果下次这个任务要被切换成运行状态时,它将从cmpl %%ecx, %2这条指令开始执行!那么,由彼任务推到此任务,也就是说我们切换至任务next时,它也是从这条指令开始执行的!于是我们进入到任务next的堆栈空间,并开始执行,但由于任务next和当前的任务有着相同的堆栈路径(这和LINUX中的内核控制路径是不是一回事呢?),所以我们还是引用当前的堆栈来继续分析。
哦,有点糊涂了,好象是。休息一下,再参考一下(文献2:上册P373)。专家也是这样说的;)
要不,我们这么理解,刚才被中断的进程发生了强制调度,且也发生了任务切换,只不过是切换到它自己,实际上不是哟。好吧,JMP成功,开始执行。
五、转折点,从schedule()返回。
cmpl %%ecx, %2;jne 1f; clts;1: 这几句是与协处理器有关,还有TS标志,我们就直接到1:吧,开始从schedule()返回,注意switch_to()是宏,它在schedule()末端。返回到哪儿去了呢?跟踪一下,看看上面的堆栈示意图,返回地址就是调用do_timer后的那条语句,
addl $4, %esp
jmp ret_from_sys_call
这儿esp加4就是把堆栈中的CPL去掉,因为我们不用了,跳转到ret_from_sys_call。哦,剩下的处理与系统调用返回共用代码。
六、ret_from_sys_call (文献3,kernel/system_call.s)
先看看我们的焦点,堆栈怎么样了呢?
| eax |
| ebx |
| ecx |
| edx |
| fs |
| es |
| ds |
-----------
| 外层EIP |
| 外层CS |
| EFLAG |
| 外层ESP |
| 外层SS |
-----------
ret_from_sys_call:
movel current, %eax
cmpl task, %eax
je 3f
movl CS(%esp), %ebx
testl $3, %ebx
je 3f
cmpw $0x17, OLDSS(%esp)
jne 3f
2:
....
3:
popl %eax
popl %ebx
popl %ecx
popl %edx
pop %fs
pop %es
pop %ds
iret
2标号处我省略了一些有关信号及其它一些处理。让我们分析一下,如果当前任务是0号进程,或是任务先前的CPL为3(即用户态),或是任务先前的堆栈段为LDT中指定的堆栈,JMP到3标号处。由先前的假设可知,此任务的CPL为3,那就跳吧。把eax, ebc, ecx, edx, fs, es, ds寄存器从堆栈中恢复出来。
现在堆栈如下:
| 外层EIP |
| 外层CS |
| EFLAG |
| 外层ESP |
| 外层SS |
-----------
记得我们还有最后一条语句哟,iret。这条指令大家想必已经很熟悉了,它恢复EIP、CS、EFLAG、ESP、SS。记得不,这是不是已经恢复到了最初的时间中断时进程被中断的那一刻?恭喜!你终于可以继续做你需要做的事情了!小心,还有下一个时间中断,哦,你不怕?因为它不会影响你的连贯性。
相关推荐
对linux 0.11版本中switch_to()的理解
switch_to的代码在linux-0.11\include\linux中的sched.h。它是一个宏定义,代码如下:
linux内核----- swtich_to 详细分析
linux内核进程切换最重要的一个部分就是宏定义switch_to,下面从几个方面来详细讲解一下:(1)内嵌汇编(2)memory 破坏描述符(编译器优化)(3)进程切换的标志是什么?(4)堆栈切换的标志是什么?(5)为什么switch_to 提供了三个参数?(6)汇编参数的传递? 带着这几个问题,先来大体浏览一下代码 #define switch_to(p
分析Linux 0.11中的任务切换
Linux 0.11中的任务切换分析
Linux 进程调度+Linux系统一般执行过程 笔记
Linux 进程调度 Linux系统一般执行过程 笔记
Linux进程调度——schedule()函数分析
刘森林 原创作品转载请注明出处 《Linux内核分析》MOOC课程 打开终端中输入qemu –kernel linux-3.18.6/arch/x86/boot/bzImage –initrd rootfs.img –S –s 然后打开另一个终端输入gdb (gdb)file linux-3.18.6/vmlinux (gdb)target remote:1234 (gdb)b schedul
switch_to(n)函数
这篇文章我们来分析switch_to()函数, 分析的代码取自linux kernel 0.11 sched.h文件, 我们只为说明原理, 所以尽量简单. 其中代码注释参考了赵炯老师一书, 言归正传,下面来看switch_to()函数. switch_to(n)将切换当前任务到任务n.首先检测任务n是不是当前任务,是则直接退出. 输入: %0 – 指向_tmp
context_switch( )上下文切换
context_switch( )上下文切换: 调用switch_mm(),把虚拟内存从一个进程映射切换到新进程中 调用switch_to(),从上一个进程的处理器状态切换到新进程的处理器状态。这包括保存、恢复栈信息和寄存器信息 The context_swit
Unity Shader变体优化实战:从内存削减到打包加速
本文深入探讨了Unity项目中Shader变体数量爆炸导致的打包缓慢、内存占用过高及包体臃肿等问题。核心解决方案是通过精准的Shader变体裁剪,即收集项目实际用到的变体形成“白名单”,并利用IPreprocessShaders接口在打包时剔除无用变体。实战表明,该方法能显著提升打包速度并大幅降低运行时内存。
Linux进程切换
这篇文章也是阅读《Linux内核情景分析》的读书笔记。这篇文章仅涉及进程切换的一部分内容,具体来说就是switch_to()函数的分析,觉得这个函数确实值得花点心思,而且它确实也是进程切换的一个核心操作。看了一下最新的内核代码,这一函数的内容已经发生了很大变化,但是这里还是以书中的2.4内核为背景分析。 首先列出这个函数的内容 #define switch_to(prev,next,last)
华三模拟器实战:IPSec VPN穿越NAT的完整配置与调试(附常见问题排查)
本文详细解析了华三模拟器中IPSec VPN穿越NAT的完整配置与调试方法,包括基础环境准备、分步配置、ACL协调策略及高级故障排查。通过实战案例和关键参数解析,帮助网络工程师解决IPSec与NAT协同工作的典型问题,提升企业网络安全性。
DSPACE仿真平台的使用
DSPACE仿真平台的使用 文章目录DSPACE仿真平台的使用1.ModelDesk新建项目2.MotionDesk引入项目3.ControlDesk引入项目4.项目整合运行 我们使用DSPACE仿真平台软件来模拟小汽车的自动驾驶。 总共需要用到DSPACE大类下的三个软件:ModelDesk、ControlDesk、MotionDesk。 1.ModelDesk新建项目 在桌面上点击ModelDesk图标,打开DSPACE—ModelDesk 选择 File 标签,侧边栏选择 New,点击 New A
linux进程调度时机原因,理解进程调度时机跟踪分析进程调度与进程切换的过程...
一.进程调度现代的操作系统是多道的,这必然涉及到进程的调度,调度需要许多的调度算法。1.需要多种调度算法的理由:不同的进程对于计算机的资源(CPU、IO等)的需求是不同的,比如有些需要频繁的IO,有些需要一直占CPU。那么他们之间的调度成为提升计算机效率的关键。不同进程还可以分类成批处理进程,例如编译程序、科学计算等,这类进程特点是不需要和用户交互,而是直接在后台埋头苦干,还有一点就是不需要实时。...
淘特App x-sign参数逆向实战:从抓包到算法定位
本文详细解析了淘特App的x-sign参数逆向过程,从抓包分析到算法定位,涵盖了使用jadx-gui进行静态分析、Frida动态Hook验证以及Native层算法还原等关键技术。文章重点介绍了x-sign参数的生成机制及其在阿里系应用中的安全防护策略,为逆向工程师提供了实用的调试技巧和对抗策略。
7.switch_to到底干了啥?
第六讲(传送门:fork + execve:一个进程的诞生)我们介绍了进程的诞生,可是操作系统中有很多进程,进程之间怎么切换的,又有哪些奥秘?我们回到源码,细细阅读。 操作系统原理中介绍了大量进程调度算法,这些算法从实现的角度看仅仅是从运行队列中选择一个新进程,选择的过程中运用了不同的策略而已。 对于理解操作系统的工作机制,反而是进程的调度时机与进程的切换机制更为关键。 进程调度的时机:
MT6826SGT-STD-R绝对值编码器深度解析:精准控制+灵活编程
本文详细介绍MT品牌的高性能绝对值编码器——MT6826SGT-STD-R。:区别于传统增量式编码器,绝对值输出无需掉电归零,开机即获准确位置数据。的核心优势,结合应用场景与选型指南,为工程师提供一站式解决方案参考。MT6826SGT-STD-R是一款专为工业级精密控制设计的。该设备广泛应用于伺服系统、机器人关节校准、自动化产线等领域。:15bit分辨率实现细腻的位置反馈;:通过专用编写器可自定义多项关键参数;:优化电路设计,适应复杂电磁环境。
8.DJI-PSDK:一站式项目功能开发总结(空中气象站项目/激光甲烷检测项目)
本文将以一篇文章的幅度快速完成: 项目一:空中气象站项目; 项目二:激光甲烷项目; 项目所使用的PSDK来源于第0章节末尾的百度网盘;解压:Payload-SDK-3.8.zip代码所在目录:双击打开: 修改打印串口和相关波特率,我们修改如下: 进行相关编译选项配置如下: :
linux0.11中switch_to理解
最近看linux0.11源码时,看到任务切换函数switch_to,感觉很晦涩,于是在网上查了一些资料,现在终于有些眉目,特记录于此,以方便大家参考,有什么错误或不足之处,还请大家指出~ switch_to源码 /* * switch_to(n) should sw
怎样让你的论文看起来“像人写的”?只用三招直接去除AI味(附提示词)
当然如果你在实际使用中还有其他需求,比如降重润色、改写翻译等,也可以结合七哥的专业写作平台来辅助,这个高级学术平台已经内置了ChatGPT4o、ChatGPT4.1、Claude3.7、满血版DeepSeek-R1等AI大模型,帮你写文献综述、干课题申报、写论文等学术任务绝对每一点问题。比如我自己的一套学术写作平台,其中的“降重与润色”模块整合了多种GPTs应用,除了能帮助调整语言风格、避免雷同表达,还可以顺带降个重,对于提升学术文本的整体质量还是非常有用的。一、为什么用AI写出来的文章会有AI味?
Linux0.11内核--进程的调度schedule和switch_to解析
前言 在我学习进程调度的时候,是通过这三篇文章把这个问题搞明白的。所以这里贴 在一起,方便查看。第一篇主要是将进程的调度,可认为是个总体讲述,里面共 涉及两个函数schedule()和switch_to()这篇把schedule()注释的比较清楚,但 switch_to()我没太明白;第二篇和第三篇都是详解switch_to()的,其实第三篇是 讲的最清楚,我是看到这篇明白的,但是第二篇里面的几个图可以帮助理解几个 重要的概念,所以也贴了下来。 第一篇 进程的调度 进程的调度
*手把手教你学 Simulink——压缩机用永磁同步电机无位置传感器 I‑F 启动(Sensorless I/F Starting)控制仿真
dtdi^αzαe^αω^θ^=Ls1(−Rsi^α+usα−zα)=K⋅sign(i^α−iα)=zα+Kpll⋅(i^α−iα)同理 β=PLL(e^α,e^β)=∫ω^dt。给定 Id=Iset, Iq=f(θopen)按设定 fstart(t)Iq=k⋅sin(θopen)投影 → 恒幅 Is=Id2+Iq2。 将 Id,Iq→ Iα,Iβ→ SVPWM。Id=Ialign(弱对齐 → 防抖动)
温度PID控制PLC程序
温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值
进程的切换——switch_to函数
1. 前言 在前文中,分析了进程的创建过程,顺带着介绍了进程相关的一些概念,如task_struct,进程的结构等。如今各种大型网站都有着高流量高并发,高并发的基础是操作系统对于多进程多线程机制的实现,操作系统用非常优异的进程调度机制,本文将基于Linux kernel最原始的源代码来分析进程是如何实现调度的。 2. 进程的切换——switch_to函数 ...
Probability and Statistics - 4th edition
Probability and Statistics - 4th edition
Linux进程(之)进程切换函数switch_to()解析
进程切换过程是由一段汇编代码switch_to完成,主要功能是保存当前进程的esp和eip,恢复切换到的进程的esp和eip。 保证进程被切换回来后,prev是一个正确值也是switch_to需要考虑的问题。
Devexpress16.2.6破解工具
Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具
Camera2Demo
在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作
switch_to() in schedule()
Reading ULK about switch_to(prev, next, last), it's quite hard to understand why the need for the 3rd argument. It turns out it's not hard when read it with schedule(). The schedule() perform a proce...
context_switch()
Linux内核的context_switch()函数是进程调度的核心执行环节,负责完成当前进程(prev)和目标进程(next)的软硬件上下文切换。其主要功能包括:地址空间切换(通过switch_mm()更新页表)、运行队列状态更新、底层寄存器与栈切换(通过架构相关的switch_to()汇编实现)。该函数区分进程和线程,线程切换时无需更新页表从而降低开销。切换过程中会关闭中断以确保安全性,并通过TLB_ASID等技术优化性能。作为schedule()的最后执行步骤,context_switch()与pic
2986



被折叠的 条评论
为什么被折叠?



