技术控

    今日:41| 主题:49101
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] 谈谈协程

[复制链接]
ζ扯蛋的夏天 发表于 6 天前
46 4

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
什么是协程?
   协程被称为“ 轻量级线程 ”或者“ 用户态线程 ”。最近协程在高并发编程领域大放异彩,如Golang天生就支持协程,Lua和Python也支持协程。但其实协程并不是最近才出现的新技术,恰恰相反,协程是一项古老的技术。早期版本的Linux并不支持线程,这时就出现代替线程的轻量级线程--协程。比较有名的有: GNU PthLibtask (Go语言的作者之一Russ Cox的作品)。 下面我们会以Libtask作为分析案例来解释协程的原理。
   注意
  以下内容会引起部分读者情绪不安,敬请留意。
  基本概念
  要理解协程,首先要知道程序是怎么运行的。所以下面我们先来聊聊程序是怎么跑起来的。
   我们知道CPU的使命就是执行程序中的指令,而且CPU内部有很多用于存放数据的 寄存器 ,其中比较重要的一个寄存器叫 E IP寄存器 ,它用于存储下一条要执行的指令。除了EIP寄存器之外,还有一个比较重要的寄存器叫 ESP寄存器 ,它用于保存程序的栈顶位置。除此之外,CPU还有很多其他用途的寄存器,如:通用寄存器EAX、EDX和段寄存器CS、DS等等。
  当一个程序被执行(称为进程)的时候,这些寄存器的值通常会被修改。所以当要切换进程执行的时候,只需要把这些寄存器的值保存下来,然后把新进程寄存器的值赋值到CPU中,那么就完成进程切换了,通常我们把这个过程称为上下文切换,协程的切换也类似。
  

谈谈协程-1 (寄存器,通用,Linux,协程,程序)

  协程原理
  上面讨论过,只需要切换上下文就可以切换协程。而上下文就是CPU寄存器的值,所以要创建一个协程,首先要创建一个保存CPU寄存器值的对象。在Libtask中,使用mcontext结构体来保存寄存器的值。mcontext结构体定义如下:
   struct   mcontext  {
      int mc_gs ;
      int mc_fs ;
      int mc_es ;
      int mc_ds ;
      int mc_edi ;
      int mc_esi ;
      int mc_ebp ;
      int mc_isp ;
      int mc_ebx ;
      int mc_edx ;
      int mc_ecx ;
      int mc_eax ;
      int mc_trapno ;
      int mc_err ;
      int mc_eip ;
      int mc_cs ;
      int mc_eflags ;
      int mc_esp ;
      int mc_ss ;
  };
   mcontext结构体用来保存寄存器的值, 从mcontext的成员可以看到要保存的寄存器很多,包括CS、DS、EIP、EAX、EBX等等。
  C函数调用原理
   因为协程切换一般是通过调用一个swapcontext()的C函数来进行,这个函数的作用就是保存旧的协程上下文和替换新的协程上下文来进行协程切换,而新旧协程上下文就是通过C函数的参数来传递的,所以 我们先来了解下C函数调用过程的原理 。
  C函数是通过栈空间来传递参数的,通过下图有个感性的认识:
  


谈谈协程-2 (寄存器,通用,Linux,协程,程序)

  在上图中,浅绿色部分是调用函数时把参数入栈的。入栈时,C语言是从右到左开始入栈的。例如我们调用swapcontext(old, new)这个函数时,会先把new参数入栈,然后再把old参数入栈。
   另外,在调用一个函数时,CPU会自动把当前指令的下一条指令入栈。所以,在上图可以看到在参数后面还有返回地址。 在返回地址下面保存的是函数的局部变量。
      注意
   栈空间是从内存的高地址向地址增长的
   协程切换
    现在到了重头戏--协程的切换。协程的切换是通过保存旧协程的上下文和替换新协程的上下文来实现的。
  在Libtask库中,保存协程上下文通过getcontext()实现,而替换协程上下文是通过setcontext()实现。这两个函数都是使用汇编语言实现的。所以要看明白这两个函数就必须有汇编的基础。我们来看看这两个函数的实现:
   1 getcontext()
  gexcontext:
  movl    4(%esp), %eax
  movl    %fs, 8(%eax)
  movl    %es, 12(%eax)
  movl    %ds, 16(%eax)
  movl    %ss, 76(%eax)
  movl    %edi, 20(%eax)
  movl    %esi, 24(%eax)
  movl    %ebp, 28(%eax)
  movl    %ebx, 36(%eax)
  movl    %edx, 40(%eax)
  movl    %ecx, 44(%eax)
  movl    $1, 48(%eax)
  movl    (%esp), %ecx
  movl    %ecx, 60(%eax)
  leal    4(%esp), %ecx
  movl    %ecx, 72(%eax)
  movl    44(%eax), %ecx
  movl    $0, %eax
  ret
  getcontext()函数的原型如下:
   int    getcontext ( struct   mcontext *ctx);
  其作用是把当前寄存器的值保存到参数ctx中。上面这段汇编代码就不详细解说了,有兴趣可以根据C函数参数传递的原理来对照一下就很容易理解。
   需要说明的一点是,“ movl 4(%esp), %eax ”这行汇编代码的作用是把ctx参数放置到EAX寄存器中,后面的操作都是通过mcontext结构体的偏移量来赋值的。
   2 setcontext()
  setcontext:
  movl    4(%esp), %eax
  movl    8(%eax), %fs
  movl    12(%eax), %es
  movl    16(%eax), %ds
  movl    76(%eax), %ss
  movl    20(%eax), %edi
  movl    24(%eax), %esi
  movl    28(%eax), %ebp
  movl    36(%eax), %ebx
  movl    40(%eax), %edx
  movl    44(%eax), %ecx
  movl    72(%eax), %esp
  pushl   60(%eax)
  movl    48(%eax), %eax
  ret
  setcontext()函数是协程切换的切换点,原型如下:
   int    setcontext ( struct   mcontext *ctx);
  其作用是把ctx参数中寄存器的值替换成CPU寄存器的值来实现切换。
  最后,我们就可以通过getcontext()和setcontext()这两个函数来实现swapcontext()函数了,实现很简单:
   int    swapcontext ( struct   mcontext *new, struct   mcontext *old)
  {
  getcontext(old);
  setcontext(new);
      return  0;
  }
  以后我们就可以通过swapcontext()函数来进行协程的切换了。
  总结
  在本文中,我们只要解释了协程的基本原理,但是要真正实现一个可以使用的协程库还需要做很多细节的工作,例如切换协程的栈空间(因为每个协程都需要有自己独立的栈空间才不会影响其协程)。
  另外,一个完善的协程库还应该支持定时器和I/O阻塞自动切换协程等功能。对于怎么实现一个完善的协程库可以参考Libtask的源代码。
友荐云推荐




上一篇:Collective #267
下一篇:Java and Scala's Type Systems are Unsound
酷辣虫提示酷辣虫禁止发表任何与中华人民共和国法律有抵触的内容!所有内容由用户发布,并不代表酷辣虫的观点,酷辣虫无法对用户发布内容真实性提供任何的保证,请自行验证并承担风险与后果。如您有版权、违规等问题,请通过"联系我们"或"违规举报"告知我们处理。

让我们一起来次爽的...顶!
回复 支持 反对

使用道具 举报

xundoucha 发表于 6 天前
不回帖,臣妾做不到啊!
回复 支持 反对

使用道具 举报

teary889 发表于 5 天前
我是耶稣他儿子,椰子!
回复 支持 反对

使用道具 举报

letoo5488 发表于 5 天前
火前留名,前排占座,此楼出租,欢迎议价。
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读

扫码访问 @iTTTTT瑞翔 的微博
回页顶回复上一篇下一篇回列表手机版
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )|网站地图 酷辣虫

© 2001-2016 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表