libco 源码剖析(1): 协程上下文切换之 32 位
libco 源码剖析(1): 协程上下文切换之 32 位
相关背景资料
- 关于汇编语言及内存布局相关基础,参看 参考文献[0], 参考文献[1]
- 32 位协程上下文结构如下:
// coctx.h
 struct coctx_t
 {
 void *regs[ 8 ];
 size_t ss_size;
 char *ss_sp;
 };
 
- 32 位协程上下文中的寄存器信息注释如下:
// coctx.cpp
 // low | regs[0]: ret |
 // | regs[1]: ebx |
 // | regs[2]: ecx |
 // | regs[3]: edx |
 // | regs[4]: edi |
 // | regs[5]: esi |
 // | regs[6]: ebp |
 // high | regs[7]: eax | = esp
 
- 协程上下文切换函数声明如下:
extern "C"
 {
 extern void coctx_swap( coctx_t *,coctx_t* ) asm("coctx_swap");
 };
 
- 协程上下文切换汇编源码:参考文献[2]
源码解析
- 根据协程上下文结构及上下文切换函数的定义,可以画出进入上下文切换汇编时的内存布局: 
  - To pass parameters to the subroutine, push them onto the stack before the call. The parameters should be pushed in inverted order. —— 参考文献[7] 
- 如上图,进入 - coctx_swap函数后, ESP 寄存器指向 返回地址(return address) 。 第一句汇编指令将- coctx_swap函数的第一个参数的地址存入 EAX 寄存器中:- leal 4(%esp), %eax //sp
 - 然后将 - coctx_swap函数的第一个参数的地址(即 返回地址(return address) 的地址 +- sizeof(void*))存入 ESP 寄存器。- movl 4(%esp), %esp
 - 最后将 ESP 寄存器的值增加 32( - 8*sizeof(void*) = 32。即,将栈顶设置为- ®s[7] + sizeof(void*)。后续向栈顶压入上下文时,即是在将数据存入- coctx_t::regs中)。- leal 32(%esp), %esp //parm a : ®s[7] + sizeof(void*)
 - 上述一系列操作后内存布局如下: 
  
- 接下来就是按照约定,依次将 EAX, EBP, ESI, EDI, EDX, ECX, EBX 保存的数据以及**返回地址( - %eax-4)**压入栈内。- pushl %eax //esp ->parm a pushl %ebp
 pushl %esi
 pushl %edi
 pushl %edx
 pushl %ecx
 pushl %ebx
 pushl -4(%eax)
 - 由于当前栈顶指针 ESP 保存的是 - ®s[7] + sizeof(void*),因此将寄存器信息压入栈的过程实际上就是将数据保存在- coctx_swap函数的第一个参数指向的- coctx_t结构的- reg数组中。
 移入寄存器后的内存布局如下:
  
- 接下来将第二个参数的值(即 切换的新上下文信息的结构的地址 )存入栈顶寄存器 ESP, 作为栈顶指针。 - movl 4(%eax), %esp //parm b -> ®s[0]
 - 操作后的内存布局如下: 
  
- 将 返回地址(return address) 的值弹出到 EAX 寄存器中: - popl %eax //ret func addr
 - 然后,依次弹出接下来的几个寄存器的值: - popl %ebx
 popl %ecx
 popl %edx
 popl %edi
 popl %esi
 popl %ebp
 - 操作后的内存布局如下: 
  
- 接下来是恢复之前的栈数据。根据前面的分析,我们可以知道当前栈顶 - reg[7]保存的是上下文切换前的第一个参数的地址,即 实际栈顶地址+4 。- 而现在的 EAX 保存的是上下文切换前的 返回地址(return address) 。因此要恢复上下文切换之前的状态,只需要将 - reg[7]弹出到 ESP 寄存器,然后将 EAX 寄存器的值压入栈。- popl %esp
 pushl %eax //set ret func addr
 - 最后将 EAX 寄存器清空: - xorl %eax, %eax
 
其他
64位汇编与32位类似,就不赘述。主要差别在于 64 位通过寄存器传递参数。
leaq 112(%rdi),%rsp
... ...
movq %rsi, %rsp
To pass parameters to the subroutine, we put up to six of them into registers (in order: rdi, rsi,
rdx, rcx, r8, r9). If there are more than six parameters to the subroutine, then push the rest onto
the stack in reverse order —— 参考文献 [8]
参考文献
[ 0 ] 内存布局与栈
 [ 1 ] Lecture 4: x86_64 Assembly Language
 [ 2 ] coctx_swap.S
 [ 3 ] coctx.h
 [ 4 ] coctx.cpp
 [ 5 ] Calling Functions and Passing Parameters in Assembly
 [ 6 ] Mixing Assembly and C
 [ 7 ] The 32 bit x86 C Calling Convention
 [ 8 ] The 64 bit x86 C Calling Convention
libco 源码剖析(1): 协程上下文切换之 32 位的更多相关文章
- socketserver源码解析和协程版socketserver
		来,贴上一段代码让你仰慕一下欧socketserver的魅力,看欧怎么完美实现多并发的魅力 client import socket ip_port = ('127.0.0.1',8009) sk = ... 
- Golang源码探索(二) 协程的实现原理(转)
		Golang最大的特色可以说是协程(goroutine)了, 协程让本来很复杂的异步编程变得简单, 让程序员不再需要面对回调地狱,虽然现在引入了协程的语言越来越多, 但go中的协程仍然是实现的是最彻底 ... 
- Golang源码探索(二) 协程的实现原理
		Golang最大的特色可以说是协程(goroutine)了, 协程让本来很复杂的异步编程变得简单, 让程序员不再需要面对回调地狱, 虽然现在引入了协程的语言越来越多, 但go中的协程仍然是实现的是最彻 ... 
- skynet源码阅读<5>--协程调度模型
		注:为方便理解,本文贴出的代码部分经过了缩减或展开,与实际skynet代码可能会有所出入. 作为一个skynet actor,在启动脚本被加载的过程中,总是要调用skynet.start和sky ... 
- 04 flask源码剖析之LocalStack和Local对象实现栈的管理
		04 LocalStack和Local对象实现栈的管理 目录 04 LocalStack和Local对象实现栈的管理 1.源码入口 1. flask源码关于local的实现 2. flask源码关于l ... 
- Flask核心机制--上下文源码剖析
		一.前言 了解过flask的python开发者想必都知道flask中核心机制莫过于上下文管理,当然学习flask如果不了解其中的处理流程,可能在很多问题上不能得到解决,当然我在写本篇文章之前也看到了很 ... 
- 【Python源码剖析】对象模型概述
		Python 是一门 面向对象 语言,实现了一个完整的面向对象体系,简洁而优雅. 与其他面向对象编程语言相比, Python 有自己独特的一面. 这让很多开发人员在学习 Python 时,多少有些无所 ... 
- 最清晰易懂的 Go WaitGroup 源码剖析
		hi,大家好,我是haohongfan. 本篇主要介绍 WaitGroup 的一些特性,让我们从本质上去了解 WaitGroup.关于 WaitGroup 的基本用法这里就不做过多介绍了.相对于< ... 
- socket_server源码剖析、python作用域、IO多路复用
		本节内容: 课前准备知识: 函数嵌套函数的使用方法: 我们在使用函数嵌套函数的时候,是学习装饰器的时候,出现过,由一个函数返回值是一个函数体情况. 我们在使用函数嵌套函数的时候,最好也这么写. def ... 
- 玩转Android之Picasso使用详详详详详详解,从入门到源码剖析!!!!
		Picasso是Squareup公司出的一款图片加载框架,能够解决我们在Android开发中加载图片时遇到的诸多问题,比如OOM,图片错位等,问题主要集中在加载图片列表时,因为单张图片加载谁都会写.如 ... 
随机推荐
- Opengl ES之YUV数据渲染
			YUV回顾 记得在音视频基础知识介绍中,笔者专门介绍过YUV的相关知识,可以参考: <音视频基础知识-YUV图像> YUV数据量相比RGB较小,因此YUV适用于传输,但是YUV图不能直接用 ... 
- Java lambda表达式基本使用
			代码示例:java.lambda.LambdaExpression 1 本质 lambda表达式本质上是对匿名内部类实例的一种简化写法. 1.1 案例 有以下List<Integer>对象 ... 
- windows环境变量修改器
			软件及源码 前言 我一直再用win7的系统,当更改path环境变量的时候很难受, 就只能看到一段,然后前面有啥后面有啥都看不到,而且来回调整优先级的时候需要剪切粘贴,主要就是来回调节优先级特别麻烦.所 ... 
- 小米mini路由器刷breed不死鸟和潘多拉固件
			前言 开启小米路由器ssh, 这一步浪费我很长时间,因为目前的开发版都对ssh升级进行了md5校验,导致官方升级方法总是失败,所以换成老版本的 路由器固件就行了. 步骤 下载 0.4.36 mini路 ... 
- 为什么标准库的模板变量都是inline的
			最近在看标准库里的type_traits的时候发现了个有趣的地方,几乎所有在标准库里的变量模板都是inline的! 不仅常见的实现上(libstdc++.libc++.ms stl)都是inline的 ... 
- 长度最小子数组-LeetCode209 滑动窗口
			力扣:https://leetcode.cn/problems/minimum-size-subarray-sum/ 题目 给定一个含有 n 个正整数的数组和一个正整数 target .找出该数组中满 ... 
- C++编程笔记(QT)
			目录 入门基础 模态对话框 消息提示框(messagebox) 文件和目录 字体选择框 输入对话框 进度条 工具栏 控件布局 Windows托盘案例 控件 button 下拉菜单按钮 `radioBu ... 
- STM32用寄存器实现电平翻转(一个按键控制LED灯的开关)
			代码 GPIOx -> ODR ^= GPIO_Pin_x 如果加载了标准库的文件: GPIOx中的x可以为(A,B,C,D--) GPIO_Pin_x中的x可以为(1,2,3--) 如果没有加 ... 
- uniapp微信小程序内部跳转其他微信小程序
			uniapp小程序内点击某个按钮跳转另外一个小程序连接,具体实现步骤如下: <view class="home-Item" @click="goNativeinde ... 
- [seaborn] seaborn学习笔记5-小提琴图VIOLINPLOT
			文章目录 5 小提琴图Violinplot 1. 基础小提琴图绘制 Basic violinplot 2. 小提琴图样式自定义 Custom seaborn violinplot 3. 小提琴图颜色自 ... 
