起初,操作系统中只有进程的概念,人们那时候对并发没有太高的要求。后来有些人想提高程序的井发,这才有了线程这一新生事物。任何新生事物在诞生之初都会被小心谨慎地对待,人们提出线程的需求时,操作系统也抱着“围观”的心态不敢轻举妄动,只能坐看其发展,真正待需求明朗时才会在操作系统一级来实现。想想也是,如果稍微有个新需求就往内核里面塞,内核开发成本很高不说,至少内核中肯定有很多“不切实际”的功能,所以人们还是能够体谅操作系统研发厂商的.
为此,既然不能说服操作系统支持线程,人们只好在用户进程内想办法。所以,线程的实现就有两种
方式,要么由操作系统原生支持,用户进程通过系统调用使用线程,要么操作系统不支持线程,由进程自己想办法解决. 因此,线程要么在0特权级的内核空间中实现,要么在3特权级的用户空间实现。
强调下,这里所说的“在0特权级的内核空间中实现线程”,只是说线程机制由内核来提供,并不是说线程中所运行的代码也必须是0特权级的内核级代码,也可以是3特权级的用户级代码,内核毕竟是为用户进程提供服务的。而“在3特权级的用户空间实现线程是指线程机制由用户进程自己提供,相当于用户进程除了负责业务外,还要在进程中实现线程调度器,这样一来程序员负担比较重,所以通常情况下很有程序员愿意在进程中写线程机制 ,故标准库便提供了用户级线程库,程序员直接使用标准线程库就行了。用户特权级是3 ,因此线程中只能运行自己进程内的代码,即只能同级调用,不能调用0特权的内核代码。总之,无论线程机制是由内核,还是用户进程提供,都是为用户进程服务的,线程中必须可以运行用户的代码。
下面看看由这两类提供方实现的线程各自的优缺点。
线程仅仅是个执行流,在用户空间,还是在内核空间实现它,最大的区别就是线程表在哪里,由谁来调度它上处理器 . 如果线程在用户空间中实现,线程表就在用户进程中,用户进程就要专门写个线程用作线程调度器,由它来调度进程内部的其他线程。如果线程在内核空间中实现,线程表就在内核中,该线程就会由操作系统的调度器统一调度,无论该线程属于内核,还是用户进程。
在用户空间中实现线程
用户空间中实现线程的好处是可移植性强,由于是用户级的实现,所以在不支持线程的操作系统上也可以写出完美支持线程的用户程序。
原理很简单,在用户空间中实现线程,操作系统根本就不会意识到线程的存在,因为操作系统调度器只会以整个进程的方式调度,将处理器的使用权交给这个进程,由进程中的调度器自己去协调分配处理器时间
无论线程在哪里实现,目的都是要到处理器上运行,因此必然要考虑到线程调度的问题,这涉及到调度器及线程表
如果在用户空间中实现线程,用户线程就要肩负起调度器的责任,因此除了要实现进程内的线程调度器外,还要自己在进程内维护线程表
在用户进程中实现线程有以下优点和缺点:
- 线程的调度算法是由用户程序自己实现的,可以根据实现应用情况为某些线程加权调度
- 将线程的寄存器映像装载到 CP 时,可以在用户空间完成,即不用陷入到内核态,这样就免去了
进入内核时的入战及出战操作 - 进程中的某个线程若出现了阻塞(通常是由于系统调用造成的),操作系统不知道进程中存在线程,它以为此进程是传统型进程(单线程进程),因此会将整个进程挂起,即进程中的全部线程都无法运行
- 线程未在内核空间中实现,因此对于操作系统来说,调度器的调度单元是整个进程,并不是进程中的线程,所以时钟中断只能影响进程一级的执行流
线程在用户空间实现,和在内核空间实现相比,只是在内部调度时少了陷入内核的代价,确实相当于提速,但由于整个进程占据处理器的时间片是有限的,这有限的时间片还要再分给内部的线程,所以每个线程执行的时间片非常非常短暂,再加上进程内线程调度器维护线程表、运行调度算法的时间片消耗,反而抵销了内部调度带来的提速
在内核空间中实现线程
- 相比在用户空间中实现线程,内核提供的线程相当于让进程多占了处理器资源, 每个线程在处理器都是一个单独的执行流
- 是当进程中的某一线程阻塞后 由于线程是由内核空间实现的,操作系统认识线程,所以就只会阻塞这一个线程,此线程所在进程内的其他线程将不受影响
- 缺点是用户进程需要通过系统调用陷入内核,这多少增加了 些现场保护的枝操作,这还是会消耗
些处理器时间,但和上面的大幅度提速相比,这不算什么大事。
摘自操作系统真象还原9.1.5节