“协程”(coroutine) , 就是把linux epoll的异步IO机制通过长跳转(long jmp)封装起来 , 形成一个在用户看来“连续的”流程 。
所有操作系统的异步IO , 都分为启动函数和回调函数 。
以Linux为例 , 启动函数负责往epoll框架里添加读写事件 。
在事件触发之后 , 再通过回调函数去进行下半部的处理 。
整个事件处理过程 , 与Linux内核里的中断处理差不多 。
一个完整的IO流程需要回调好几次 , 而且读代码时到处查找回调函数在哪里设置的 。
甚至 , 有的程序员会中途修改回调函数的指针[捂脸]
C的函数指针比C++的虚函数更“灵活”的地方是 , C++的虚函数表在编译时就固定了 , 但C的函数指针可以在运行时修改(它就是个普通变量) 。
然后 , 就真有人半截里修改它 , 让代码的可读性急剧下降 。
再然后 , 就出现了coroutine , 看上去至少是同步的了 。
程序流程在一个函数里跳转 , 就是普通的goto语句 。
程序流程在2个函数的半截里跳转 , 就是长跳转(long jmp) 。
协程的原理如下:
1 , 当某个文件描述符需要IO等待的时候 , 通过长跳转回到epoll的主框架函数 , 让其他的IO可以运行 。
2 , 当这个文件描述符的IO再次就绪之后 , 再通过长跳转从主框架函数跳回来 , 接着上次的位置继续运行:
这个位置 , 是函数上一次放弃运行的位置 , 它是函数内的某个点 。
文章插图
在函数的半截里放弃CPU之后还能回来 , 就需要保存函数的运行上下文:栈信息、寄存器信息 。
保存到哪里?
只能保存到堆上 , 因为栈和寄存器都会随着代码的运行而不断地覆盖 , 只有堆是受用户控制的 。
文章插图
用户测试代码
上图是“用户代码” , 虽然两个函数__async_connect()和__async_write()的内部是异步执行的 , 但它们都在一个函数_async_test()里 , 整个流程看上去是同步的 。
文章插图
__async_connect()函数 , 上半部
__async_connect()分为上半部和下半部 , 以__asm_co_task_yield()为分隔点 。
上半部调用异步的connect() , 下半部调用getsockopt()读取结果 。
为了避免阻塞线程 , 需要在异步connect()之后让出CPU , 让主框架函数可以做别的 。
这个让出CPU的函数__asm_co_task_yield() , 是“协程库”的关键 。
它让出了CPU之后 , 在事件触发之后再次恢复运行:这时函数__asm_co_task_yield()才会返回 , 然后接着运行下图的代码 。
文章插图
__async_connect()函数 , 下半部
当异步connect()成功时 , getsockopt()获取的错误码err是0 。
文章插图
__async_write()函数
__async_write()函数的流程与__async_connect()类似 , 也是在文件描述符变得不可写时放弃CPU , 等待下次可写时再恢复运行 。
文章插图
epoll主框架函数
epoll的主框架函数是一个while循环:使用epoll_wait()系统调用去监控事件的触发 。
它会同时处理IO事件和定时器 。
推荐阅读
- 道德经中老子的名言名句大全,老子道德经里的八句经典语言句句精辟-
- 来做一次表达能力的测试, 语言表达能力测试
- 明代官话是以什么为基础语言?江淮官话是明朝官话
- 服装店导购卖衣服的经典销售技巧和话术! 卖服装的语言技巧
- 卖衣服的语言技巧有哪些? 卖服装的语言技巧
- 经典网络语言!(最新网络经典句子)
- 朝花夕拾阿长与山海经佳句赏析100字?朝花夕拾阿长与山海经佳句赏析?外貌,语言,动作,神态
- 家长用语言侮辱孩子?如果老师言语侮辱孩子_1
- 有没有一本书教会你说话?语言·身体·他者书籍
- 前端编程软件有哪些,前端编程语言有几种?