1 进程、线程、协程
最开始,为了为了达到并发的,充分利用cpu的目的,引入了进程,进程作为一个cpu一段时间的描述。切换进程的时候,需要保存上下文,切换cr3寄存器,tss段中的栈等等操作。
因为进程切换代价太大,因此出现线程,共享了进程的资源,因此同一进程中的线程切换的时候,需要切换的上下文比进程间切换更少。
线程和进程的切换都需要陷入内核态。为了追求效率,我们在线程中(其实是单线程模型)中实现在用户态的调度逻辑,也就是说一个线程中做到多个函数并发执行(注意不是并行),在用户态为每个协程维护调用上下文。
一个线程中的多个协程共享线程中时间片。
2 协程的有点
因为协程是在用户态实现切换逻辑,并维护上下文,因此:
- 跨平台
- 跨体系架构
- 无线程、进程的切换开销
- 协程一般都是但线程模型,因此无锁的开销。
但是不能充分利用多核,因此通常是多进程单线程模型。
3 协程
协程是语言内建的机制。
4 协程与函数
每个函数都有一个调用点,一个返回点,是固定的。函数可以嵌套,A函数可以调用B,B还可以在调用C。这是通过栈的机制来实现的,后进先出。
但是协程,在判断执行到特定的语句的时候,可以通过特定的语法(比如切出操作是由 co_yield_ct())跳转到别的协程去执行。然后从主协程再切换回某个协程。(co_resume )
也就是说协程需要主动让出CPU时间(一个线程的时间分片),由程序员控制协程的调度。
协程不共享栈,因此在协程切换的时候,需要使用汇编,更换栈。
4 libco
libco是腾讯开源的一个协程库。
一个线程中存在多个协程,协程之间的栈是不同享的。因为libco没有使用操作系统提供的栈,应该是自己为每个协程。malloc了一块内存。
然后通过改变sp寄存器的方式切换不同的栈,通过修改不同栈中地址配合ret指令,控制cs:ip寄存器的指向。从而达到了保存协程上下文,修改sp指针,修改栈中保存的返回地址,pop寄存器恢复协程上下文,ret返回修改cs:ip寄存器的方式切换线程执行流从而切换了协程。
5 用户态线程
线程最开始的时候有一个用户态线程和内核态线程之分,但那时候貌似没有异步,因此一个进程中的某个线程阻塞,整个进程就会阻塞。但是有异步,就不会这样了。