Kilim工作原理
1. Kilim中的Task,即用户线程如何调度和切换?
相比传统的Thread多线程间抢占式调度,Kilim中的Task采用的是协作式调度,即由Task本身负责释放和恢复占用CPU
在多任务的调度上操作系统采取抢占式和协作式两种方式。
2.Kilim如何识别线程堆栈中哪些方法是Pauseable,即可暂停的?
Kilim通过代码编译期识别抛出的Pauseable异常注解,来判断识别方法可暂停
3.Kilim是如何实现线程执行过程中当前方法的暂停和恢复?
Kilim通过编译期字节码编织,对每一个可暂停的方法进行字节码处理,在方法执行前和执行后加上相关的执行上下文的处理,暂停时会保存整个线程堆栈,包括完整栈帧,程序计数器等等,然后通过特定的字节码跳转指令jsr跳转到另外一个Task的执行方法中,恢复时将复原整个线程堆栈,包括完整栈帧,程序计数器等等,回到上次暂停时的指令地址处继续执行。
4.Kilim中的Weaver工具是如何针对编译的代码实现织入的?
字节码技术,具体来说通过ASM字节码框架实现对class文件的重写
5.如何将一个传统的线程执行方法改造成Kilim的Task模型?
首先,需要实现一个类继承Task,实现Task的execute方法,该execute中调用的方法必须声明抛出Pauseable异常,其次,线程间的通信全部通过Mailbox来传递消息,put和get时注意三种版本,包括阻塞线程,阻塞Task但不阻塞线程,无阻塞。
最后,针对这些Task和抛出Pauseable的方法编译时,使用Weaver工具进行编织处理
6.Kilim中什么样的方法在执行过程中是可暂停的?
整个方法调用链中方法必须声明抛出Pauseable异常,This annotation is used by the Kilim weaver to rewrite the bytecode of all pausable methods to incorporate the voluntary yielding logic. One important property of this scheme is that a pausable method can only be called by another pausable method.
7.Kilim中哪些操作可以使得Task暂停或者恢复运行?
Task.sleep() is a pausable method that pauses a task for a give time. Another example is Mailbox.get(), which pauses a task until a mailbox is non-empty.
##8.Kilim中Task和Thread之间是如何调度的?
Kilim’s tasks are cooperatively scheduled on a kernel thread pool.
A Kilim task is owned and managed by a scheduler, which manages the
thread pool. When a task needs to pause, it removes itself from the
thread by popping its call stack, remembering enough about each
activation frame in order to help rebuild the stack and resume, at a
later point). The scheduler then reuses that thread for some other
task.
You can have more than one scheduler and assign
each task to a particular scheduler.
9.Kilim中Task占用的内存大小?
The amount of memory occupied by a task is:
- The java object that represents the task class
- If paused, an array of activation frames is stored. The Kilim
weaver performs data flow and live variable and constant analysis
(intra-procedurally) to ensure that it capture only as
much as is needed to resume. - The contents of all mailboxes that the task is receiving on.
10.什么场景下适合做Kilim协程?
IO密集型的应用比较适合使用协程,比如应用中存在较多的与后端的网络交互,存在较多的时间在等待后端响应
11.Kilim中Task的工作机制?
Actor采取的这种类似消息机制的方式,实际在守护线程和外部线程之间有一个队列,俗称信箱,外部线程只要把请求放入,守护线程就读取进行处理。这种异步高效方式是Actor基本原理。Task 是轻量型的线程,它们通过 Kilim 的 Mailbox 类型与其他 Task 通信。
12.Kilim框架做了什么?
- 利用字节码增强(基于ASM字节码框架),将普通代码转化为支持协程的代码;
- 调用pausable的时候,如果pause了就保存当前方法栈的State,停止执行,将控制权交给调度器;
- 调度器负责协调就绪的协程;
- 协程resume的时候,自动恢复State,回复到上次执行的位置继续执行
小记
Java等语言的基本编程模型是基于线程的。
要自己实现fiber,主要是保存线程上下文。注意要保存的信息有:各个寄存器,栈顶和栈底,异常信息,浮点寄存器。
window Fiber的默认堆栈是1M
协程虽然如此之好,看是很长时间以来,因为受到基于堆栈的子例程实现的限制,并没有多少语言在其实语言或库中支持协程
子例程容易实现于堆栈之上,因为子例程将调用的其他子例程作为下级。相反地,协程对等地调用其他协程
类似于C这样的语言全都要依赖于基于栈的结构,因此在函数间传递控制时,必须要有一个调用函数和一个被调函数。
我们必须接受这个现实:在C语言中,必须要有调用函数和被调函数。
有见过几种协程的实现,因为没有 C/C++ 的原生支持,所以多数的库使用了汇编代码,还有些库利用了 C 语言的 setjmp 和 longjmp 但是要求函数里面使用 static local 的变量来保存协程内部的数据。
用 C/C++ 实现协程的最大困难就是创建,保存和恢复程序的上下文。因为这涉及到了程序栈的管理,以及 CPU 寄存器的访问,但是这两项内容在 C/C++ 标准里面都没有严格的定义,所以我们是不可能有一个完全跨平台的 C/C++ 实现的。