一个简单、轻量的 Linux 协程实现

2019-12-18   linux内核

HevTaskSystem (https://github.com/heiher/hev-task-system) 是一个简单的、轻量的多任务系统(或称协程),它工作于 Linux 平台,I/O event poll 基于 Epoll。

协程其实是一种古老的技术,协程有这么几个特点:

  1. 协程是一个并发运行的多任务系统,一般由一个操作系统线程驱动。
  2. 协程任务元数据资源占用比操作系统线程更低,且任务切换开销小。
  3. 协程是任务间协作式调度,即某一任务主动放弃执行后进而调度另外一任务投入运行。

与异步、非阻塞式I/O模型类似,协程技术同样适用于处理海量的并发I/O任务,而且还不会像异步方式使业务代码逻辑支离破碎。

基本信息

HevTaskSystem 目前开放了四个类:HevTaskSystem、HevTask、HevTaskPoll 和 HevMemoryAllocator。

HevTaskSystem 是协程任务系统,管理、调度众多的 HevTask 实例运行。由单一操作系统线程驱动,多个线程可并行驱动多套任务系统。

HevTask 是协程任务,实例可加入某一 HevTaskSystem 中调度运行。

HevTaskPoll 是提供了 poll 兼容的系统调用。

HevMemoryAllocator 是一个内存分配器接口,其后端有两套实现:

  • 原始分配器,等价于 malloc/free。
  • Slice 分配器,按分配大小限量缓存的分配器,缓存替换算法是 LRU。

Public API

  • TaskSystem – 参考 github 代码仓库的头文件:hev-task-system.h
  • Task – 参考 github 代码仓库的头文件:hev-task.h
  • TaskPoll – 参考 github 代码仓库的头文件:hev-task-poll.h
  • MemoryAllocator – 参考 github 代码仓库的头文件:hev-memory-allocator.h

简单示例

该示例演示了在主线程上运行一个协程任务系统,并创建两个独立的协程任务,分别以不同的优先级运行各自的入口函数。各自的入口函数中各循环2次,每次打印一个字符串并 yield 释放CPU 触发调度切换。

/*============================================================================Name:simple.cAuthor:HeiherCopyright:Copyright(c)2017everyone.Description:============================================================================*/#include#include#includestaticvoidtask_entry1(void*data){inti;for(i=0;i<2;i++){printf("hello1\\n");/*主动放弃执行,yield函数会触发重新调度选取另一任务投入执行*/hev_task_yield(HEV_TASK_YIELD);}}staticvoidtask_entry2(void*data){inti;for(i=0;i<2;i++){printf("hello2\\n");hev_task_yield(HEV_TASK_YIELD);}}intmain(intargc,char*argv[]){HevTask*task;/*在当前线程上初始化tasksystem*/hev_task_system_init();/*创建一个新的task,栈空间采用默认大小*/task=hev_task_new(-1);/*设置该task的优先级为1*/hev_task_set_priority(task,1);/*将该task放入当前线程的tasksystem中,任务人口函数为task_entry1* task_entry1 并不会在 hev_task_run 执行后立即调用,需等到该 task 被调度。*/hev_task_run(task,task_entry1,NULL);task=hev_task_new(-1);hev_task_set_priority(task,0);hev_task_run(task,task_entry2,NULL);/*运行当前线程上相关的tasksystem,当无任务可调度时该函数返回*/hev_task_system_run();/*销毁当前线程上相关的tasksystem*/hev_task_system_fini();return0;}