参考文章:
Linux进程地址空间
在32位操作系统中,进程的最大地址空间为4GB。整个进程地址空间从下往上为地址的增长方向。对于系统中的所有进程来说,代码都是从同一固定地址开始。
-
只读代码和数据:该区域是直接按照可执行目标文件中的内容初始化的,在进程开始运行时,大小就固定不变;
-
堆:运行时随着malloc和free等函数相应地扩大或缩小;
-
共享库:在地址空间约中间的区域存放C标准库等共享库代码;
-
栈:用于函数的调用、局部变量的存放,随着进程的运行动态增长或缩小;
-
内核虚拟区:操作系统的一部分,内核总是驻留在内存中,不允许应用程序读写这个区域的代码。该区域包括内核代码和数据、与进程相关的数据结构(如页表、内核栈等)。
协程库的源码分析
云风协程库中的关于程序上下文的操作主要依赖glibc库里"ucontext.h"头文件的几个函数:
getContext() // 获取当前的context
setContext() // 切换到指定的context
makeContext() // 设置函数指针和栈到对应的context保存的sp和pc寄存器
swapContext() // 保存当前的context 并且换到指定的context
挂起协程
保存当前子协程的上下文(包括寄存器值和函数调用栈)
- &dummy 当前协程的最低栈地址
- assert 判断当前协程的运行空间小于等于默认的最大空间(STACK_SIZE)
- 如果当前协程的私有栈空间太小不足以保存上下文,需要重新扩容
- memcpy 将top-&dummy的地址空间保存到自定义的协程结构中,以便之后恢复环境上下文
子协程的上下文是保存于堆中,因为需要恢复的。
/**
* @param(coroutine): 当前需要保存上下文的子协程
* @param(top): 调度器的最高地址(子协程运行于主协程的公共栈空间)
*/
static void _save_stack(struct coroutine *coroutine, char *top) {
char dummy = 0;
assert