操作系统概念拾遗(三)

不为考试,重读操作系统概念——Operating System Concepts

线程是CPU使用的基本单元,它由线程ID、程序计数器、寄存器集合和栈组成。它与属于同一进程的其他线程共享代码段、数据段和其他操作系统资源,如打开文件和信号。多线程编程具有如下优点:响应度高,如果对一个交互程序采用多线程,即使其部分阻塞或执行较冗长的操作,那么该程序仍能继续执行,从而增加了对用户的响应程度,例,多线程Web浏览器再用一个线程装入图像时,能通过另一个线程与用户交互;资源共享,线程默认共享它们所属进程的内存和资源,代码和数据共享的有点事它能允许一个应用程序在同一地址空间有多个不同的活动线程;经济,进程创建所需要的内存和资源的分配比较昂贵,由于线程能共享它们所属进程的资源,所以创建和切换线程会更为经济;多处理器体系结构的利用,多线程的优点之一是能充分使用多处理器体系结构,以便每个进程能并行运行在不同的处理器上,在多CPU上使用多线程加强了并发功能。PS. 按照教科书上的定义,进程是资源管理的最小单位,线程是程序执行的最小单位。在操作系统设计上,从进程演化出线程,最主要的目的就是更好的支持SMP(Shared-memory MultiProcessor,共享内存多处理器)以及减小(进程/线程)上下文切换开销。

多线程模型,有两种不同的方法来提供线程支持:用户层的用户线程(user thread)或内核层的内核线程(kernelthread)、用户线程受内核支持,而无需内核管理;而内核线程有操作系统直接支持和管理。事实上,所有当代操作系统,如Windows、Linux、Mac OS X、Solaris等都支持内核线程。
多对一模型将许多用户级线程映射到一个内核线程。线程管理是由线程库在用户空间进行的,因而效率比较高。但是如果一个线程执行了阻塞系统调用,那么整个进程会阻塞。而且,因为任意时刻只有一个线程能访问内核,多个线程不能并行运行在多处理器上。Green threads——Solaris所应用的线程库——就使用了这种模型,另外还有GNU可移植线程(GNU Portable Threads)。
一对一模型将每个用户线程映射到一个内核线程。该模型在一个线程阻塞系统调用时,能允许另一个线程继续执行,所以它提供了比多对一模型更好的并发功能,它也允许多个线程能并行地运行在多处理器系统上。这种模型的唯一缺点是创建一个用户线程就需要创建一个相应的内核线程,由于创建内核线程的开销会影响应用程序的性能,所以这宗模型的绝大多数实现限制了系统所支持的线程数量。Linux与Windows操作系统系列——包括Windows 95/98/NT/2000/XP——实现了一对一模型。
多对多模型多路复用了许多用户线程到同样数量或更小数量的内核线程上。内核线程的数量可能与特定应用程序或特定机器有关(位于多处理器上的应用程序可比单处理器上分配更多数量的内核线程)。虽然多对一模型允许开发人员创建人一多的用户线程,但是由于内核只能一次调度一个线程,所以并没有增加并发性。一对一模型提供了更大的并发性,但是开发人员必须小心,不要在应用程序内创建太多的线程(有时可能会限制创建线程的数量)。一个流行的多对多模型的变种仍然多路复用了许多用户线程到同样数量或更小数量的内核线程上,但也允许将一个用户线程绑定到某个内核线程上。这个变种有事被称为二级模型,Solaris在其Solaris 9枝江版本中支持二级模型,但从Solaris 9开始使用一对一模型。

线程库(thread library)为程序员提供创建和管理线程的API。主要有两种方法来实现线程库。第一种方法是在用户空间中提供一个没有内核支持的库,此库的所有代码和数据结构都存在于用户空间中,调用库中的一个函数只是导致了用户空间中的一个本地函数调用,而不是系统调用。第二种方式执行一个由操作系统直接支持的内核级的库,此时,库的代码和数据结构存在与内核空间中,调用库中的一个API函数通常会导致对内核的系统调用。
目前使用的三种主要的线程库是POSIX Pthread、Win32、Java。Pthread作为POSIX标准的扩展,可以提供用户级或内核级的库;Win32线程库是适用于Windows操作系统的内核级线程库。Java线程API允许线程在Java程序中直接创建和管理。然而,由于大多数JVM实例运行在宿主操作系统之上,Java线程API通常采用宿主系统上的线程库来实现。这意味着在Windows系统上,Java线程通常会用Win32 API来实现,而在UNIX和Linux系统中采用Pthread。PS. Pthread是由POSIX标准为线程创建和同步定义的API,是线程行为的规范,而不是实现。

Java线程,线程是Java程序中程序执行的基本模型,所有Java程序至少有一个控制线程组成——即使一个只有main()方法的简单Java程序也是在JVM中作为一个线程运行的。创建Thread对象并不会创建一个新的线程,实际上是用start()方法来创建新线程。为新的对象调用start()方法需要做两件事:在JVM中分配内存并初始化新的线程;调用run()方法,使线程适合在JVM中运行(注意从不直接调用run()方法,而是调用start()方法,然后它再调用run()方法)。Java实际上识别两种不同类型的线程:后台线程和非后台线程。区别这两种线程的一个简单规则是:当所有的非后台线程退出后,JVM就会关闭。否则,两者是一样的。当JVM启东市,它创建几个内部后台线程来执行来执行垃圾收集等任务。后台线程是通过调用Thread类的setDaemon()方法并向该方法传递值true来创建的。Java规范并没有制定Java线程应如何映射到底层操作系统上,相反,它把这个决定留给了特定的JVM实现。

线程取消(thread canceling)是在线程完成之前来终止线程的任务,例,如果多个线程并发执行来搜索数据库,并且一个线程已经得到了结果,那么其他线程就可被取消;用户按下Web浏览器上的按钮已停止进一步装入Web页,通常有几个线程装入Web页——每个图像都是在一个独立线程中被装入的,当用户按下浏览器的停止按钮时,所有装入Web页的线程就被取消了。要取消的线程通常称为目标线程(target thread),目标线程的取消可在如下两种情况下发生:异步取消(asynchronous cancelling),一个线程立即终止目标线程;延迟取消(deferred cancelling),目标线程不断地检查它是否应终止,这允许目标线程有机会按着有序方式来终止自己。
Java线程可以用Thread类的stop()方法异步终止,然而这个方法是被否决(deprecated)的,被否决的方法在当前的API中仍然会被实现,但不鼓励使用他们。还可以用延迟取消来终止Java线程。延迟取消通过周期性地检查自己是否应终止来实现,在Java中,检查要调用interrupt()方法,Java API为Thread类定义了interru()方法,当interrupt()方法被调用时,目标线程的中断状态(interruption status)被设置。线程可以调用interrupted()或isInterrupted()来检查中断状态,如果目标线程的中断状态被设置,这两个方法都返回true(注意,interrupted()方法会清除线程的中断状态,而isInterrupted()则保持当前的中断状态)。

 

posted on 2013-04-08 22:51  夜月升  阅读(277)  评论(0)    收藏  举报

导航