1.进程与线程
由于线程切换是进程切换的核心内容,且相对于进程切换更容易理解,所以我们先讲解线程切换。
进程切换是由资源切换和指令流切换两部分组成的,其中资源切换是将分配给进程的非CPU以外的资源进行切换,如对当前的地址空间进行切换。而指令流切换是对CPU的切换,也就是线程切换。线程保留了并发的优点,避免了进程切换的代价。
线程切换的实质就是映射表不变而PC指针改变。
线程与进程的区别在于,一个进程可以有一个或多个线程,同一进程中的多个线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈和线程本地存储。
系统利用PCB来完成对进程的控制和管理。同样,系统为线程分配一个线程控制块TCB(Thread Control Block),将所有用于控制和管理线程的信息记录在线程的控制块中,TCB中通常包括:
线程标志符,一组寄存器,线程运行状态,优先级,线程专有存储区,信号屏蔽的信息
和进程一样,线程同样有五种状态:初始态、执行状态、等待(阻塞)状态、就绪状态和终止状态,线程之间的切换和进程一样也需要上下文切换,这里不再赘述。
2. 用户级线程的切换
线程是在同一个地址空间内启动并交替执行的多个程序,交替执行的多个线程可以由操作系统管理,也可以由用户程序自己管理。
由于用户程序自己管理的线程对操作系统透明,操作系统完全不知道这些线程的存在,这样的线程被称为用户级线程,也就是我们所说的协程。相应地,由操作系统管理的线程就是我们所谓的内核级线程。
对于一个用户级线程来说,切换的本质就是由用户程序自己调用yield()函数。在某个函数需要等待阻塞的时候将PC指针切换到另一个地址执行。
不同的用户级线程拥有各自的用户栈,而yield()函数完成的基本工作是找到下一个线程的TCB,然后根据当前线程的TCB和下一个线程的TCB完成用用户栈的切换。
切换到新栈后就利用yeild()函数中的“}”将PC指针切换到下一个线程要执行的指令处(所以不需要在yield()中写jmp,因为切换了栈后返回就是返回新栈之中的地址)。
当然还需要在线程切换的时候保存和恢复一些执行现场。无非就是保存一些通用寄存器,这些寄存器的值也要放在线程各自的栈中进行保存,在栈切换完成后弹栈恢复下一个线程的执行现场。
3. 用户级线程的创建
用户级线程的创建其实就是创建一个可以让CPU切换进去的初始样子。
ThreadCreate的核心就是用程序做出这三样东西:
ThreadCreate的大致工作:
- 申请一段内存作为TCB(Thread Control Block)
- 申请一段内存作为栈
- 栈内填上程序的初始地址