内核线程-用户线程-理解


内核态和用户态

  • 内核态: CPU可以访问内存的所有数据,包括外围设备,例如网卡,cpu也可以将自己从一个程序切换到另一个程序.
  • 用户态: 只能受限的访问内存,且允许访问外围设备,占用cpu的能力被剥夺,cpu资源可以被其他程序获取.
    之所以划分内核态和用户态,是为了限制不同程序之间的访问能力,防止他们获取其他程序的内存数据,或者外围设备的数据.
    程序运行在用户态,但有时候需要做一些内核态的事情,比如从硬盘读数据,这个时候就需要从用户态切换到内核态,切换的机制就叫做系统调用.

内核线程,用户线程,轻量级进程

有了上面的关于内核态和用户态的理解,理解下面就很容易了:

  • 内核线程:
    内核线程就是内核的分身,一个分身可以处理一件特定的事情,内核线程只运行在内核态,不受用户态上下文的拖累.
  • 轻量级进程:
    轻量级进程是建立在内核之上并有内核支持的用户线程,它是内核线程的高度抽象,每一个轻量级进程都与一个特定的内核线程关联.它是内核线程的一个高级接口。
  • 用户线程:
    用户线程是完全建立在用户空间的线程库,用户线程的创建,调度,同步,销毁全由库函数在用户空间完成,不需要内核的帮助,其内部的活动对于内核是透明的,

协程

先说一下线程实现模型,也就是1:1,N:1,N:M,分别说一下:

  • 轻量级进程:内核线程 = 1:1
    这也是Java的线程实现模型,一个用户线程映射到一个内核线程,所以用户空间的切换就涉及到了内核态的线程的切换.
  • 用户线程:内核线程 = N : 1
    1:1的模型性能开销较大,而且受限于线程数量的限制,而N:1的模型在用户空间完成线程间的同步,销毁,切换,对于内核来说是完全透明的,不涉及线程的切换
  • 用户线程:内核线程 = N : M
    N:1的缺点在于操作系统无法感知到用户态的线程,所以可能造成一个线程进行被阻塞,导致整个进程被阻塞.
    N:M的模型就解决了这个问题,而这也是实现原生协程的关键

我们知道Java是没有原生的协程的,比较著名的协程语言应该就是GO了,Go就是使用了N:M的模型实现自己的调度器,
协程是怎样的呢?其实使用的目的和多线程差不多,都是为了实现并发,只是线程在有些时候过于重了,这都是相对而言,就好像进程的调度比之线程很重,协程更多的是一种暂停的概念,而线程就是上下文切换,我们可以再一个线程中.通过协调器来暂停继续不同的协程而不用线程的上下文切换,从而实现不同协程的交替运行.这个暂停继续都是在用户空间进行的,所以并没有用户态到内核态的切换,更没有内核态的上下文切换.
协程也是更加适合IO频繁的程序,我使用过的协程是python的gevent,在gevent的猴子补丁中,专门为socket的相关方法添加了补丁,当我们使用socket的相关方法时,如果发生阻塞等待,比如requests.get(),就会自动切换到其他协程,非常好用.可以尝试一下.


  目录