探索Golang协程实现——从v1.0开始
李乐
<h2>问题引入</h2>提起协程,你可能会说,不就go func吗,我分分钟就能创建上万个协程。可是协程到底是什么呢?都说协程是用户态线程,这里的用户态是什么意思?都说协程比线程更轻量,协程轻量在哪里呢?
本文主要为读者介绍这些内容:
<ul><li>Golang v1.0协程并发模型——MG模型,协程创建,协程切换,协程退出,以及g0协程,重在理解协程栈切换逻辑;</li><li>为了理解协程栈,还需要简单了解下虚拟内存,函数栈帧以及简单的汇编语言;</li><li>Golang v1.0协程调度逻辑;</li><li>defer,panic以及recover底层实现原理。</li></ul>通过本篇文章,你将从根本上了解Golang协程。
注:为什么选择v1.0版本呢?因为他足够的简单,不过,麻雀虽小五脏俱全;而且你会发现,即使到了现在,Golang协程实现原理,也就那么回事。v1.0版本代码可以从github上下载,分支为release-branch.go1。
<h2>基础补充</h2>在讲解Golang协程实现之前,还需要补充一些基础知识。理解协程,就需要理解函数栈帧,以及虚拟内存。而函数栈帧的管理,需要从汇编层次去解读。
PS:不要怕,汇编其实很简单,不过几条指令,几个寄存器而已。
<h3>虚拟内存</h3>linux将内存组织为一些区域(段)的集合,如代码段,数据段,运行时堆,共享库段,以及用户栈都是不同的区域。如下图所示:
<span class="img-wrap"></span>
用户栈,自上而下增长,寄存器%rsp指向用户栈的栈顶位置;通过malloc分配的内存通常是在运行时堆。
想想函数调用过程,比如func1调用func2,待func2执行完毕后,还会回归道func1继续执行。该过程非常类似于栈结构,先入后出。大多数语言的函数调用都采用栈结构实现(基于用户栈),函数的调用与返回即对应一系列的入栈与出栈操作,而我们平常遇到的栈溢出就是因为函数调用层级过深,不断入栈导致的。函数栈帧如下图所示:
<span class="img-wrap"></span>
寄存器%rbp指向函数栈帧底部位置,寄存器%rsp指向函数栈帧顶部位置。可以看到,在函数栈帧入栈时候,还会将调用方函数栈帧的%rbp寄存器入栈,以及实现多个函数栈帧的链接关系。否则,当前函数执行完毕后,如何恢复其调用方的函数栈帧?
谁为我维护着函数栈帧结构呢?当然是我的代码了,可是我都没关注过这些啊。可以看看编译器生成的汇编代码,我们简单写一个c程序:
<pre><code class="lang-go hljs">int add(int x, int y) { return x y; } int main() { int sum = add(111,222); } </code></code></pre>查看编译结果:
<pre><code class="lang-go hljs">main: pushq %rbp movq %rsp, %rbp subq $16, %rsp movl $222, %esi movl $111,您可能感兴趣的文章:
探索Golang协程实现——从v1.0开始
更改MySQL数据库名实例代码
专家教你如何有效的学习Drupal - Drupal问答
asp.net常用http状态码表
css中position相对定位和绝对定位(relative,absolute)详解
配置mysql主从复制的一点心得体会
php实现简单用户登录功能程序代码
php获取http状态码程序代码
mysql导入导出数据时中文乱码的解决办法
PHP中截取中文乱码解决办法