用户级线程与内核级线程


线程与进程

  • 进程是资源分配的基本单位,线程是调度的基本单位。
  • 进程 = 资源 + 指令执行序列,如果一个进程中有多个指令执行序列(类似多个函数),可以认为这就是多个线程。即多个线程是共享进程的资源的。通过切换线程既实现了并发,又避免了进程切换时需要切换映射表的代价。映射表实际上是用来将虚拟内存映射到真实物理内存的。
  • 因此线程的切换实质上就是映射表不变而改变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空转。因此即使通过用户级线程启动了多个任务序列,但一旦在内核中阻塞了,则启动这多个序列的并发性就没有了效果。
  • 内核级线程的TCB都在内核中,因此不用自己写Yield,而是由操作系统自动调度。

内核级线程

  • 内核级线程能更好利用多核处理器,多核与多CPU的区别如下,多CPU有多个缓存和MMU,多核处理器则是使用同一套缓存和MMU。内核级线程中各个线程可以执行在不同的核上,从而实现并行。并发是同时出发交替执行,并行则是同时执行。

  • 内核级线程相比用户级线程的本质区别有两点,一点是内核级线程的TCB在内核中,由内核负责切换。第二点是两个用户级线程需要两个用户(函数)栈,而两个内核级线程则需要使用两套栈,即每个TCB包含两个栈,一个用户栈一个内核栈,当切换核心机线程即切换TCB时会同时切换用户栈和内核栈,不过这都是操作系统的活啦。

  • 用户线程通过中断陷入内核,然后把用户栈的一些相关信息压入内核栈,内核线程通过IRET指令返回用户态,并将内核栈中的相关信息出栈。

内核级线程切换5段论

  • 因为本质上我们写的程序都是应用程序,只有遇到跟硬件交互等任务时才会陷入内核,因此五段论最开始都是在用户栈开始的。
  • 第一段,用户态程序调用中断,进入切换。
  • 第二段,中断处理,比如启动磁盘读或时钟中断等,这会引发TCB切换,即线程切换。
  • 第三段,tcb切换,从一个线程到另一个线程。
  • 第四段,内核栈切换,从一个线程的内核栈,切换另一个线程的内核栈。
  • 第五段,中断出口,从内核栈切换回用户栈。
  • 在用户看来,就是两个线程的切换,内核中内核栈的切换是操作系统完成的,具体细节不可见。

用户级线程与内核级线程的对比


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM