coroutine: libmill.h
libmill是一个基于c语言开发的go风格协程库,实现了go风格的协程操作、chan操作、非阻塞的网络io操作等等,是一个不错的linux平台下c风格协程库实现。如果你想从0到1的快速了解如何开发一个协程库,libmill将是一个非常不错的案例;如果你想更深入地了解go,libmill里面也借鉴了go的一些设计思想;或者你想在生产环境中使用,开发者也提供了一个更健壮的版本libdill。
file: libmill.h
0 ABI版本
// ABI应用程序二进制接口
#define MILL_VERSION_CURRENT 19 // 主版本
#define MILL_VERSION_REVISION 1 // 修订版本
#define MILL_VERSION_AGE 1 // 支持过去的几个版本1 符号可见性
libmill这里是要编译成共享库的,共享库应该将实现相关的细节屏蔽,只暴露接口给外部调用就好,因此在共享库中有个“符号可见性”的问题(可参考下面的引文)。该工程在在Makefile里通过编译器选项-fvisibility inlines=hidden来设置默认对外隐藏工程中的符号,对于提供给外部使用的接口使用可见性属性__attribute__((visibility("default")))来单独设置其可见性。
Functions with default visibility have a global scope and can be called from other shared objects. Functions with hidden visibility have a local scope and cannot be called from other shared objects. Visibility can be controlled by using either compiler options or visibility attributes. 更多关于符号可见性的描述,可以参考:点击查看。
#if !defined __GNUC__ && !defined __clang__
#error "Unsupported compiler!"
#endif
#if defined MILL_NO_EXPORTS
# define MILL_EXPORT
#else
# if defined _WIN32
# ......
# else
# if defined __SUNPRO_C
# ......
# elif (defined __GNUC__ && __GNUC__ >= 4) || defined __INTEL_COMPILER || defined __clang__
# define MILL_EXPORT __attribute__ ((visibility("default")))
# else
# define MILL_EXPORT
# endif
# endif
#endif如果函数名前有宏MILL_EXPORT,表示该函数具有默认可见性,可在libmill.so外的代码中被调用。这里我们举个两字来说明一下:
libmill.h中涉及到大量的函数名导出的问题,这里由于篇幅的原因不再一一列出。
2 定时器精度
获取系统时间函数gettimeofday还是比较耗时的,对于频繁需要获取系统时间的情景下,需要对获取到的系统时间做一定的cache。为了保证时间精度,这里的cache更新时间必须要控制好。
如何决定何时更新cache的系统时间呢?rdtsc(read timestamp counter)指令执行只需要几个时钟周期,它返回系统启动后经过的时钟周期数。这里可以根据CPU频率指定一个时钟周期数量作为阈值,当前后两次rdtsc读取到的时钟周期数的差值超过这个阈值再调用gettimeofday来更新系统时间。这里libmill中的定时器timer就是这么实现的。
下面是rdtsc指令的简要说明,详情请查看:wiki rdtsc。
The Time Stamp Counter (TSC) is a 64-bit register present on all x86 processors since the Pentium. It counts the number of cycles since reset. The instruction RDTSC returns the TSC in EDX:EAX. In x86-64 mode, RDTSC also clears the higher 32 bits of RAX and RDX. Its opcode is 0F 31.[1] Pentium competitors such as the Cyrix 6x86 did not always have a TSC and may consider RDTSC an illegal instruction. Cyrix included a Time Stamp Counter in their MII.
该头文件涉及源码较多,这里只列出了比较关键的代码,感兴趣的可以查看源代码来了解更多细节。
3 mill_fdwait关注的io事件
4 协程上下文的保存和切换
4.0 协程上下文
4.1 协程上下文的保存
4.2 协程上下文的恢复
5 go(func)实现
go(func)的作用是挂起当前协程,并在一个新创建的协程中运行指定的函数func,func执行完成后再销毁新创建的协程,并重新调度其他协程运行(也包括当前协程)。
millgo(fn)的实现,我花了不少时间才看懂,这里还要感谢libmill贡献者raedwulf对我的帮助,通过交流他一下就明白了我困惑的源头并给我指出了不该忽略的关键4行代码!
这4行代码确实令人困惑,Stack Overflow上的朋友们看到提问的这个问题甚至给踩了好几次,同事看后也觉得有点无厘头,也难怪被raedwulf戏称为black magic around c language!
6 chan实现
6.0 常用数据结构
6.1 发送数据到chan
6.2 从chan接收数据
6.3 chan操作结束
7 choose从句实现
7.1 从句初始化
7.2 读就绪事件
7.3 写就绪事件
7.3 deadline实现
7.4 otherwise实现
7.5 从句结束
Last updated
Was this helpful?