用户级线程与内核级线程


目录
  • 线程与进程
  • 线程的价值
  • 用户级线程
  • 内核级线程
  • 内核级线程切换5段论
  • 用户级线程与内核级线程的对比

线程与进程

  • 进程是资源分配的基本单位,线程是调度的基本单位。
  • 进程 = 资源 + 指令执行序列,如果一个进程中有多个指令执行序列(类似多个函数),可以认为这就是多个线程。即多个线程是共享进程的资源的。通过切换线程既实现了并发,又避免了进程切换时需要切换映射表的代价。映射表实际上是用来将虚拟内存映射到真实物理内存的。
  • 因此线程的切换实质上就是映射表不变而改变PC指针。
  • 每个线程都有自己的栈,即线程栈。

线程的价值

  • 线程即多个执行序列共享一个地址空间

用户级线程

  • 用户级线程本质上是不经过内核的,感觉像是用自定义的函数Yield()来模拟调度,自己控制线程的切换。即用户级线程本质上就是一系列函数集合。
  • 如上每个方框中都是一个用户级线程的代码,每个线程都有一个线程控制块TCB,每个线程都有自己的函数调用栈,栈的地址就存放在线程TCB.esp中。
  • 从第一个线程,即地址100开始执行,执行到函数B则把下一个地址104压入线程1的函数栈
  • 然后跳转到B开始执行,执行到Yield函数开始切换线程,如图中Yield函数的定义,注意esp是函数栈顶指针寄存器,即当前函数执行结束后会接着执行esp出栈的地址。图中示意的是线程2中的Yield函数,从线程2切换到线程1需要做两个操作,第一步是将当前栈顶指针寄存器esp中的内容保存到线程2的TCB中,即TCB2.esp中。第二步是将线程1的TCB1.esp放到cpu的寄存器esp中。即用户级线程的切换本质上就是切换TCB和每个线程的栈(也在TCB中存着)。
  • 注意图中线程2的Yield函数中蓝线圈出来的部分需要删除,因为不需要手动跳转到204,只要函数Yield执行完毕(到右括号),栈顶指针寄存器就会执行出栈,并从出栈元素处开始执行,此时栈顶指针寄存器中的栈顶元素就是204,因此可以直接从204开始执行。换言之,如果在Yield中jmp语句不删去,那么会先跳转到204处执行,然后将线程1的B函数执行完毕后会接着从执行函数栈顶元素处的代码,此时栈顶是204,,,于是相当于执行了两个204处的代码。
  • 用户级线程不经过内核,少了进出内核的消耗,因此效率较高,但是如果某个线程要与硬件交互,比如网卡IO,则会引起对应的进程阻塞,此时cpu不会切换到其它线程去执行,而是直接切换到了另一个进程,如果此时没有其它进程,则直接导致cpu空转。因此即使通过用户级线程启动了多个任务序列,但一旦在内核中阻塞了,则启动这多个序列的并发性就没有了效果。