130行C语言实现个用户态线程库——后续(1)

ezCoroutine协程原型库只是个原型库,但是已经能够支持1000K以上数量的协程运行,而且是stackful模式。基本的对外接口有两类,一类是类似Posix线程的接口:协程创建threadCreat,协程回收threadJoin,调度函数switch_to;另外一类是类似lua协程接口,但是有所不同,我们的返回规则更加简单,所以功能也有所限制:挂起协程yield,恢复协程resume;然后是一些辅助函数接口:通过协程号寻找到协程结构体函数findThread,初始化协程函数init,销毁协程函数destroy,destroyAll函数,以及printThread,printLoop,printSharedStack等打印函数。

注意,该库只能在X86/X64 Linux环境下用gnu/gcc集成的编译工具(不能用llvm/clang),代码只支持32位,得用-m32参数,而且得加上-fno-stack-protector参数关闭栈检测机制,否则我不能保证不出现bus IO error 和 segment  fault。因为具体实现我用了一些特殊的手段,smash了栈空间,以后会逐步改善。

下面通过几个问题来介绍:

1.怎么用这个协程库呢?

这里给出经典的生产者-消费者模式的例子:

1 /************************* Test *************************/

2

3 #include "ezco.h"

4 #include

5 #include

6

7 #define NUM 2

8 #define CAL 2

9 Thread_t *tid[2];10 int *res1[2];11

12 void *produceFunc(void *d)13 {14 int j=0;15 while(1)16 {17 j = (int)yield(tid[1],233);18 printf("produce get: %d\n", j);19 }20

21 return ((void *)j);22 }23

24 void *consumeFunc(void *d)25 {26 int j=0;27

28 while(1)29 {30 j = resume(d,332);31 printf("consume get: %d\n", j);32 }33

34 return ((void *)j);35 }36

37 int main(int argc, char const *argv[])38 {39 int i = 0;40 init();41

42 threadCreat(&tid[1],produceFunc,NULL);43 threadCreat(&tid[0],consumeFunc,tid[1]);44

45 while (1){46 printf("main is grunting... %d\n", i++);47 sleep(1);48 switch_to(0); //Give up control to next thread

49 }50

51 for(i = 0; i

53 }54

55 printLoop(&dead);printLoop(&live);56 destroyAll();57 return 0;58 }59

测试结果如下:

2.设计与实现原理是什么?为何能够支持1000K数量级别的协程?平均每个协程内存占用多大?

设计与实现的基本原理请参考ucontext.h实现协程(用ucontext组件,所以代码量很小),或者参考longjmp实现协程,或者用内联汇编实现协程,在这里不累述。这里说下为何能支持大量协程。其实我们用了共享栈技术,每个协程共享一个4K或者更大的空间(你可以用堆,用匿名内存映射或者直接开个数组也都是可以的,总之得保证4K页对齐的空间),每个协程自己有私有栈空间指针privateptr,每个时刻只有一个协程在运行,此时栈空间在这个4K共享空间中(当然除了main以外),当切换协程时,动态分配一个堆内存,大小为此时协程栈实际大小(一般都很小,小的只有几十个Bytes, 大的有几百个Bytes,完全不用4KB),然后用privateptr指向它,把现在的栈里头的值复制进去。切换到下一个协程,当然下一个协程和上一个切出的一样,已经有privateptr指向了自己的私有栈空间,而这私有栈空间保存在堆内存中(有点拗口),我们只要用memcpy把这个堆内存中的栈复制回到4K共享空间中,再恢复些寄存器的值(保存在任务控制块结构体中),然后用该协程私有的寄存器的值跳转即可,这样一套操作就完成了运行时上下文切换工作。这样实现最大的开销就是不停的copy进copy出,不停地malloc和不停地free栈空间,这样的代价换来的是空间的节约。有多节约?我写了个测试用例如下:

1 /************************* Test *************************/

2

3 #include "ezthread.h"

4 #include

5 #include

6

7 #define NUM 1000000

8 #define CAL 2

9

10 void *sum1tod(void *d)11 {12 int i, j=0;13

14 for (i = 1; i <= d; ++i)15 {16 j +=i;17 printf("thread %d is grunting... %d\n",live.current->data->tid , i);18 switch_to(0); //Give up control to next thread

19 }20

21 return ((void *)j);22 }23

24 void *hello(void *d)25 {26 int i, j=0;27

28 for (i = 1; i <= d; ++i)29 {30 printf("hello\n");31 switch_to(0); //Give up control to next thread

32 }33

34 return ((void *)j);35 }36

37 int main(int argc, char const *argv[])38 {39 int res = 0;40 inti;41 init();42 Thread_t *tid1[NUM];43 int *res1[NUM];44 for(i = 0; i

48

49

50 for (i = 1; i <= CAL; ++i){51 res+=i;52 printf("main is grunting... %d\n", i);53 switch_to(0); //Give up control to next thread

54 }55

56 for(i = 0; i

58 res += (int)res1[i];59 }60

61 printf("parallel compute: %d\n", (int)res);62 printLoop(&dead);printLoop(&live);63 destroyAll();64 return 0;65 }

Test

测试结果如下:

简单地解释一下:50W个协程做1+2,并返回计算结果3,另外50W个打印hello,用top指令看了下内存占用率,基本是1022404KB*27.2%/1000000 = 285Bytes/Coroutine, 即峰值是平均每个协程占用285Bytes左右。1500003这个结果是3*500000+3(main也在无聊的计算1+2)得来的。无独有偶,我们这种共享栈实现的原理和云风的协程库是一样的。

3.大量的内存分配和释放难道不会造成内存碎片化么?

对于这个问题,我的理解是,把任务交给编写标准库的人去做吧,当然你也可以实现自己的malloc,free内存分配器,用上slab等流行的方式分配内存,但是这不是我们所要考虑的任务。

4.可移植性问题以及能否和其他语言配合使用?

理论上,可以拓展成X64,ARM,MIPS,SPARC等等版本,并且能够配合C++使用,但是其中共享栈实现依赖了编译器对volatile关键字处理的行为,能否实现还得看这类CPU的操作系统有没有提供gnu/gcc编译工具。

5.TODO list:

(a)不依赖编译器行为,重构共享栈的实现。(难度大,本人不希望用ucontext.h组件实现,但以后stackless模式会用setjmp,longjmp实现)

(b)用hash表结合原有的循环链表使得搜索速度达到O(1)。(几乎没难度)

(c)拓展到X64版本等版本。(难度一般)

(d)提供多种实战用例,在实际开发项目中用上。(可行性未知,后果未知)1楼lucpp人才啊。可惜了自己g++内联汇编看不太明白。但是感觉切换的时候,不仅栈和ip寄存器,还是要保存切换其他寄存器的值的。Re: zyzacz@lucpp,老哥,回复的够快的,话说我刚没发多久就被系统管理给删出首页了。真尴尬。其他寄存器的确需要恢复,但是我们用的是内联汇编,如果是完全汇编就要小心翼翼地回复,但是内联可以用C语法帮我们回复。一个return就完成几乎所有恢复工作。

线程库 c语言实现,130行C语言实现个用户态线程库——后续(一)相关推荐

  1. 用户态线程库原理、设计与实现

    目录 一.概述 二.开发环境 三. 相关原理及算法 3.1  不得不知道的知识--c语言的函数参数传递机制 3.1.1 从汇编的角度理解C语言函数传参的方式 3.1.2 函数调用过程的堆栈变化 3.2 ...

  2. 用户态线程在AI中的应用

    最近这段时间在修改服务器AI,准备将AI分配到单独的服务器中做,但为了不至于对原有架构造成 太大的影响,攻击的判定,移动的判定仍然在gameserver上处理,AI服务器的角色就是根据状态 选择合适的 ...

  3. 用户态和内核态:用户态线程和内核态线程有什么区别?

    转载 文章来源于 拉钩教育 重学操作系统 林䭽 用户态和内核态:用户态线程和内核态线程有什么区别? 什么是用户态和内核态 Kernel 运行在超级权限模式(Supervisor Mode)下,所以拥有 ...

  4. r语言dataellipse_几行R语言代码搞定菌群与环境因子或临床指标相关性的可视化...

    相关性分析是生物信息学中常用的分析方法,可以用来分析菌群与菌群的关联,菌群与因子的关联等等.本文使用R语言内置函数cor()计算变量之间的相关系数,并用corrplot包进行可视化.(本文测试数据为R ...

  5. golang goroutine协程概念及入门:轻量级线程(或用户态线程)

    import ("strconv""time""fmt" )

  6. 华为C语言面试题集 C语言面试题-华为C语言面试题 深圳华为面试

    C语言面试题  1.局部变量能否和全局变量重名? 答:能,局部会屏蔽全局.要用全局变量,需要使用"::" 局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量, ...

  7. Linux下的LWP(轻量级进程)、进程 、 线程、用户级线程、内核线程

    一.定义 再看正文之前我要先强调一下几点: 1. Linux中没有真正的线程,但windows中确实有线程 2. Linux中没有的线程是由进程来模拟实现的(又称作:轻量级进程) 3. 所以在Linu ...

  8. 应用退出前不让线程切换_用户级线程和内核级线程,你分清楚了吗?

    前天晚上有个伙伴私信我说在学进程和线程,问我有没有好的方法和学习教程,刚好我最近也在备相关的课. 班上不少学生学的还是很不错的.拿班上小白和小明的例子吧(艺名哈).小明接受能力很强,小白则稍差些. 关 ...

  9. 利用用户级线程提高多线程应用的性能

    随着处理器往多核的发展,多线程被越来越多的应用到软件的开发中.但是如果没有正确的使用多线程,反而可能会导致软件性能的下降. 多线程程序中一个影响程序性能的因素就是同步.对于windows系统来说,最快 ...

最新文章

  1. 数据结构笔记--二叉查找树概述以及java代码实现
  2. 线性分类器与非线性分类器的区别是什么?有哪些优劣特性?
  3. js date 当前日志往后一个月_【应用实例】如何利用 Python 生成器 yield 监控日志?...
  4. mysql数据库修改编码
  5. HTTP 错误 404.3 – Not Found 由于扩展配置问题而无法提供您请求的页面。如果该页面是脚本,请添加处理程序。如果应下载文件,请添加 MIME 映射。...
  6. python多线程基本操作
  7. Python相关的考试和认证
  8. Qt笔记-Linux程序打印带颜色的字符串
  9. java执行完main就结束了吗_为什么main方法中执行完第一个方法完之后,后面的代码都不执行了?...
  10. Kali Linux 网络扫描秘籍 第三章 端口扫描(一)
  11. Python的迭代器
  12. 痴人、信徒、先驱:深度学习三巨头等口述神经网络复兴史
  13. KVM虚拟机openVSwitch杂记(2)
  14. 为什么html中图片显示不出来,网页图片不能显示 网页图片显示不出来的解决办法...
  15. 良心安利草皮3d纹理图片素材网站
  16. 魔兽地图服务器修改,如何修改魔兽地图(傻瓜版)
  17. 安卓逆向——刷机Pixel2 ROOT
  18. TL-WDN5200H无线usb网卡在ubuntu18.04/16.04上的使用
  19. 什么学习软件需要身份证验证_什么是两层身份验证,为什么我需要它?
  20. CSDN博客写作编辑器如何使用?

热门文章

  1. php java session共享_PHP实现多服务器session共享之NFS共享
  2. python容易出错的地方_Python中try-except出错后如何从try出错地方继续执行?
  3. 盒子模型代码_果冻公开课第五课:五分钟理清盒模型的前世今生
  4. 风险预测模型_【期刊导读】长期NA治疗的慢乙肝患者也有专属肝癌风险预测模型...
  5. c语言木马源代码下载,木马编程 之超强服务... 附代码 原创.
  6. elasticsearch报错exceptions.RequestError(400, u'mapper_parsing_exception', u'No handler field..
  7. HEVC官方代码下载及码流分析软件使用
  8. 软件常见出现缺陷的地方
  9. 21天Jenkins打卡Day17-查看Jenkins服务器上的目录结构
  10. 从0到1开发自动化测试框架(硬货太多,建议阅读)