操作系统概念(第四章) 线程

线程

概述

概述

单个进程可以包括多个控制线程。

线程 ——一种CPU利用的基本单元,它是形成多线程计算机的基础。

线程是CPU使用的基本单元,它由线程ID、程序计数器、寄存器集合和栈组成。它与属于统一进程的其他线程共享代码段_数据段其他操作系统资源

一个传统重量级的进程只有单个控制线程,如果进程有多个控制线程,那么能同时做多个任务。

操作系统概念(第四章) 线程_第1张图片

动机

一个应用程序通常是作为一个具有多个控制线程的独立进程实现的。如一个忙碌的网页服务器如果有多个(或数千个)客户并发访问它,可以建立多个线程而非进程,因为创建进程很耗时间,而单个线程的进程一次只能处理一个请求。

线程在远程过程调用(RPC)系统中个也有很重要的作用,通常,PRC服务器是多线程的。当一个服务器接收到消息,它使用独立线程处理消息,这允许服务器能处理多个并发请求。

现代许多操作系统都是多线程的,少数线程在内核中运行,每个线程完成一个指定的任务。

多线程的优点

  1. 响应度高(Responsiveness):即使其部分阻塞或执行较冗长操作,该程序仍能继续执行,从而增加了对用户的相应程度。

  2. 资源共享(Resource Sharing):线程默认共享它们所属进程的内存和资源。代码和数据共享的优点是它允许一个应用程序在同一地址空间有多个不同的活动线程。

  3. 经济(Economy):进程创建所需要的内存和资源的分配比较昂贵。由于线程能共享它们所属进程的资源,所以创建和切换线程会更为经济。

  4. 多处理器体系结构的利用(Utilization of MP Architectures):多线程的优点之一是能充分使用多处理器体系结构。以便每个进程能并行运行在不同的处理器上。不管有多少CPU,单线程进程只能运行在一个CPU上,在多CPU上使用多线程加强了并发功能。

多线程模型

有两种不同的方法来提供线程支持:用户层的用户线程(User Threads)和内核层的内核线程。用户线程受内核支持,而无需内核管理;而内核线程由操作系统支持和管理。事实上所有当代操作系统都支持内核线程。在用户线程和内核线程之间必然存在一种关系。

多对一模型:

多对一模型将许多用户级线程映射到一个内核线程。线程管理由线程库在用户空间进行的,因而效率比较高。但是如果一个线程执行了阻塞系统调用,那么整个线程会阻塞。因为任意时刻只能有一个线程能够访问内核,多个线程不能并行运行在多处理器上。

操作系统概念(第四章) 线程_第2张图片

一对一模型:
一对一模型每个用户线程映射到一个内核线程。该模型在一个线程执行阻塞系统调用时,能允许另一个线程继续执行。它也允许多个线程能并行运行在多处理器系统上,这种模型的唯一缺点是每创建一个用户线程就会创建一个相应的内核线程。由于创建内核线程的开销会影响应用程序的性能,所以这种模型的绝大多数实现限制了操作系统所支持的线程数量。


多对多模型:

多对多模型多路复用了许多用户线程到同样数量或更小数量的内核线程上。多对多模型没有这两者的缺点:开发人员可创建任意多的用户线程,并且相应内核线程能在多处理器系统上并发执行。而且当一个线程执行阻塞系统调用时,内核能调度另一个线程来执行。

一个流行多对多模型的变种仍然多路服用了许多用户线程到同样数量或更小数量的内核线程上,但也允许将一个用户线程绑定到某个内核线程上。这个变种有是被称为二级模型。

多线程问题

系统调用fork()和exec()

在多线程程序中,系统调用fork()和exec()的语义有所改变。

如果程序中一个进程调用fork(),那么新进程会复制所有线程,还是新进程只有单个线程?有的UNIX系统有两种形式的fork(),一种复制所有线程,另一种只复制调用了系统调用fork()的线程。

Exec()工作方式:如果一个线程调用系统调用exec(),那么exec()参数所指定的程序会替换整个进程,包括所有线程。

如果调用fork()之后立即调用exec(),那么没有必要复制所有线程,因为exec()参数所指定的程序会替换整个进程。在这种情况下,只复制调用线程比较适当。不过,如果在fork()之后另一进程并不调用exec(),那么另一进程就应复制所有进程。

取消

线程取消(thread cancellation)是在线程完成之前来终止线程的任务。

要取消的线程通常称为目标线程。目标线程的取消可在如下两种情况下发生:

一是异步取消(asynchronous cancellation):一个线程立即终止目标线程。

二是延迟取消(deferred cancellation):目标线程不断地检查它是否应终止,这允许目标线程有机会以有序方式来终止自己。

如果资源已经分配給要取消的线程,或者要取消的线程正在更新与其他线程所共享的数据,那么取消会有困难,对于异步取消尤为麻烦。操作系统回收取消线程的系统资源,但是通常不回收所有资源。因此,异步取消线程并不会使所需的系统资源空闲。相反采用延迟取消时,允许一个线程检查它是否是在安全的点被取消,pthread称这些点为取消点(cancellation point)

信号处理

信号处理:信号在Unix中用来通知进程某个特定时间已发生了,信号可以同步或异步接收。所有有信号具有同样的模式:

(1)信号有特定事件的发生所产生

(2)产生的信号要发送到进程

(3)一旦发送,信号必须交易处理。

同步信号的例子包括访问非法内存或被0除。在这种情况下,如果运行程序执行这些动作,那么就产生信号,同步信号发送到执行操作而产生信号的同一进程(同步的原因)。

当一个信号由运行进程之外的事件产生,那么进程就异步接收这一信号。这种信号的例子包括使用特殊键(Ctrl + C)或者定时器到期。通常,异步信号被发送到另一个进程。

每个信号可能由两种可能的处理程序中的一种来处理:

(1)默认信号处理程序

(2)用户定义的信号处理程序

每个信号都有一个默认信号处理程序,当处理信号是在内核中运行的,这种默认动作可以用用户定义的信号处理程序来改写。信号可以按照不同的方式处理。有的信号可以简单的忽略(如改变窗口大小),有的需要终止程序来处理(非法内存访问)

单线程程序的信号处理比较直接,信号总是发送给进程。

当多线程时,信号会

(1)发送信号到信号所应用的线程

(2)发送信号到进程内的每个线程

(3)发送信号到进程内的某些固定线程

(4)规定一个特定线程以接收进程的所有信号。

发送信号的方法依赖于信号的类型。

大多数线程版Unix允许线程描述它会接收什么信号和拒绝什么信号。,因为信号只能处理一次,所以信号通常发送到不拒绝它的第一个线程。

pthread还提供了pthread_kill函数,此函数允许限号被传送到一个指定线程。

Windows,通过异步过程调用(asynchronous procedure call,APC)来模拟信号。

线程池

多线程服务器有一些潜在问题:第一个是关于处理请求之前用以创建线程的时间,以及线程在完成工作之后就要被丢弃这一事实。第二个,如果允许所有并发请求都通过新线程来处理,那么将没法限制在系统中并发执行的线程的数量。无限制的线程会耗尽系统资源。解决这一问题是使用线程池。

线程池的思想是在进程开始时创建一定数量的线程,并放入到池中以等待工作。当服务器收到请求时,他会唤醒池中的一个线程,并将要处理的请求传递给他,一旦线程完成了服务,它会返回到池中在等待工作。如果池中没有可用的线程,那么服务器会一直等待直到有空线程为止。

线程池的优点:

(1)通常用现有线程处理请求要比等待创建新的线程要快

(2)线程池限制了在任何时候可用线程的数量。

线程池中的线程数量由系统CPU的数量、物理内存的大小和并发客户请求的期望值等因素决定。比较高级的线程池能动态的调整线程的数量,以适应具体情况。

线程特定数据

同属一个进程的线程共享进程数据。

在某些情况下每个线程可能需要一定数据的自己的副本,这种数据称为线程特定数据。可以让每个线程与其唯一的标识符相关联。

调度程序激活

内核与线程库的通信问题,就是要讨论多对多模型。这种协调允许动态调整内核线程的数量以保证其最好的性能。

在用户内核线程之间设置一种中间数据结构。轻量级进程(LWP),他表现为一种应用程序可以调度用户线程来运行的虚拟处理器。每个LWP与内核线程相连,该内核线程被操作系统调度到物理处理器上运行,如果内核线程阻塞,LWP阻塞,与LWP相连的用户线程也阻塞。如果在单处理器上,一次只能运行一个线程,则只需要一个LWP就够了,但I/O密集的应用程序可能需要多个LWP来执行。

一种结局用户线程库与内核间通信的方法为调度器激活(scheduler activation),内核提供一组虚拟处理器(LWP)给应用程序,应用程序可调度用户进程到一个可用的虚拟处理器上。

你可能感兴趣的:(操作系统概念(第四章) 线程)