操作系统的进程与线程
为了对操作系统系统的内部结构原理进行进一步的了解和研究,我们全面介绍了操作系统进程与线程的相关知识。本文一共分为3部分来讲述,分别是进程,线程 ,进程与线程区别。其中各章节里面又包含了操作系统进程引入、概念、特征、状态、进程控制块、操作系统 进程操作相关API、线程的引入、概念、组成、属性、线程周期及它的周期图、线程特点、 创建线程的方法与区别(针对C/C++/MFC)、进程与线程的区别、进程通信的13种机制、线程同步的一系列知识、线程调度算法的总体描述、操作系统线程的优先级、线程调度数据结构、处理机调度的四个层次、三种典型的线程调度算、 对优先级的抽象说明、 操作系统 支持的优先级类、相对的线程优先级、进程优先级类和线程相对优先级的映射、优先级编程、动态提升线程优先级、计算机结构示意图解析、线程的亲缘性示例。通过这些知识点的整理和讲解,读者可以融会贯通,更好的深入了解操作系统内部进程线程原理。
1.1 windows进程(Process): 1.1.1 进程引入
多道程序在执行时,需要共享系统资源,从而导致各程序在执行过程中出现相互制约的关系,程序的执行表现出间断性的特征。这些特征都是在程序的执行过程中发生的,是动态的过程,而传统的程序本身是一组指令的集合,是一个静态的概念,无法描述程序在内存中的执行情况,即我们无法从程序的字面上看出它何时执行,何时停顿,也无法看出它与其它执行程序的关系,因此,程序这个静态概念已不能如实反映程序并发执行过程的特征。为了深刻描述程序动态执行过程的性质,人们引入“进程(Process)”概念。
1.1.2 进程概念
进程通常被定义为一个正在运行的程序的实例,它由两个部分组成:一个是操作系统用来管理进程的内核对象。内核对象也是系统用来存放关于进程的统计信息的地方。另一个是地址空间,它包含所有可执行模块或DLL模块的代码和数据。它还包含动态内存分配的空间。如线程堆栈和堆分配空间。
进程是由进程控制块、程序段、数据段三部分组成。进程是一个具有功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一
个动态的概念,是一个活动的实体。它不只是程序的代码,还包括当前的活动,通过程序计数器的值和处理寄存器的内容来表示。
1.1.3 进程概念的两个特征
拥有资源的单位-进程被分派保存进程映像的续地址空间
分派Dispatching (调度)的单位-进程是由一个或多个程序的一次执行 可能会与其他进程交替执行
对进程两个特征,有些操作系统进行处理(改进)
分派被称为一个线程(thread)或轻型进程(lightweight process) 分派资源被称为进程或任务 1.1.4 进程的三种基本状态
一个进程的生命周期可以划分为一组状态,这些状态描述了整个进程。系统根据PCB结构中的状态值控制进程。
在进程的整个生命周期内,一个进程至少具有三种基本状态,它们是:执行状态、等待状态和就绪状态。这三种状态之间可以相互换,处于就绪状态的进程已经得到除CPU之外的其它资源,只要由调度得到处理机,便可立即投入执行。处于执行状态的进程因时间片到而放弃处理机进入就绪状态,因等待某个事件发生而放弃处理机进入等待状态。处于等待状态的进程因等待的事件发生而被唤醒进入就绪状态,如下图:
1.1.5 进程控制块
进程控制块是由OS维护的用来记录进程相关信息的一块内存数据结构。 每个进程在OS中的登记表项(可能有总数目),OS据此对进程进行控制和管理(PCB中的内容会动态改变),不同OS则不同
处于核心段,通常不能由应用程序自身的代码来直接访问,而要通过系统调用。
1.1.6 Windows 进程操作相关API CreateProcess():进程创建
ExitProcess()或TerminateProcess():进程退出
ExitProcess()终止一个进程和它的所有线程;它的终止操作是完整的,包括关闭所有对象句柄、它的所有线程等;
TerminateProcess()终止指定的进程和它的所有线程;它的终止操作是不完整的(如:不向相关DLL通报关闭情况),通常只用于异常情况下对进程的终止。
1.2 windows线程(thread): 1.2.1 线程的引入
是为了提高系统内程序的并发执行程度而提出来的概念,它是比进程更小的能够运行的基本单位。
进程是不活泼的。若要使进程完成某项操作,它必须至少拥有一个线程,该线程负责执行包含在进程的地址空间中的代码。
实际上,单个进程可能包含若干个线程,所有这些线程都“同时”执行进程地址空间中的代码。
为此,每个线程都有它自己的一组CPU寄存器和它自己的堆栈。 每个进程至少拥有一个线程,来执行进程的地址空间中的代码。
当创建一个进程时,系统会自动创建它的第一个线程,称为主线程。然后,该线程可以创建子线程,而这些子线程又能创建更多的子线程。
1.2.2 线程概念
线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属
一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程。在多线程OS中,通常是在一个进程中包括多个线程,每个线程都是作为利用CPU的基本单位,是花费最小开销的实体。
1.2.3 线程组成
线程也是由两个部分组成的:
一个是线程的内核对象,操作系统用它来对线程实施管理。
另一个是线程堆栈,它用于维护线程在执行代码时需要的所有函数参数和局部变量。
1.2.4 线程属性
线程具有以下属性:轻型实体、调度和分派的基本单位、共享进程资源。 在多线程OS中,线程是能运行的基本单位,因而也是调度和分派的基本单位。由于线程很“轻”,故线程的切换非常迅速且开销小。在一个进程中的多个线程之间,可以并发执行,甚至允许在一个进程中所有线程都能并发执行;同样,不同进程中的线程也能并发执行。在同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:所有线程都具有相同的地址空间(进程的地址空间),这意味着,线程可以访问该地址空间的每一个虚地址;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构等。
1.2.5 线程周期及它的周期图
①新建 ②就绪 ③运行 ④阻塞 ⑤死亡 1.2.6 线程特点 1.线程都具有一个ID 2.线程具有自己的安全属性 3.每个线程都有自己的内存栈 4.每个线程都具有自己的寄存器信息
1.2.7 创建线程的方法与区别(针对C/C++/MFC)
1、CreateThread( ) ——WIN32 API函数
2、_beginthreadex( ) ——MS对C Runtime库的扩展SDK函数 3、AfxBeginThread( ) ——MFC中线程创建的MFC函数 CreateThread:
提供操作系统级别的创建线程的操作,且仅限于工作者线程。线程函数中不调用MFC和RTL的函数时,可以用CreateThread,其它情况不要轻易使用,以免内存泄漏。 但它没有考虑:
(1)C Runtime库里面有一些函数使用了全局量,如果使用 CreateThread 的情况下使用这些C++ 运行库的函数,就会出现不安全的问题。而 _beginthreadex 为这些全局变量做了处理(典型的例子是strtok函数)
(2)MFC也需要知道新线程的创建,也需要做一些初始化工作。 _beginthreadex :
MS对C Runtime库的扩展SDK函数,首先针对C Runtime库做了一些初始化的工作,以保证C Runtime库工作正常。然后,调用CreateThread真正创建线程。 仅使用Runtime Library时,可以用_BegingThread。
_beginthreadex函数在创建线程的时候分配了一个堆结构并和线程本身关联起来,这个结构叫做tiddata结构 ,线程创建时传入的线程入口函数地址就保存在这个结构中;另外,C运行时库中有些函数需要通过这个结构来保存和获取一些数据,比如说errno之类的线程全局变量。
当一个线程调用一个要求tiddata结构的C Runtime库函数的时候,将发生下面的情况:
C Runtime库函数试图TlsGetvalue获取线程数据块的地址,如果没有获取到,函数就会现场分配一个 tiddata结构,并且和线程相关联,于是问题出现了,如果不通过_endthreadex函数来终结线程的话,这个结构将不会被撤销,内存泄漏就会出现了。但通常情况下,我们都不推荐使用_endthreadex函数来结束线程,因为里面包含了ExitThread调用。
AfxBeginThread :
MFC中线程创建的MFC函数,首先创建了相应的CWinThread对象,然后调用CWinThread::CreateThread,在CWinThread::CreateThread中,完成了对线程对象的初始化工作,然后,调用_beginthreadex(AfxBeginThread相比较更为安全)创建线程。它简化了操作或让线程能够响应消息,即可用于界面线程,也可以用于工作者线程,但要注意不要在一个MFC程序中使用_beginthreadex()或CreateThread()。
1.3 进程与线程的区别 1.3.1 区别的总体论述
线程和进程的区别在于,子进程和父进程有不同的代码和数据空间,而多个线程则共享数据空间,每个线程有自己的执行堆栈和程序计数器为其执行上下文。多线程主要是为了节约CPU时间,发挥利用,根据具体情况而定。线程的运行中需要使用计算机的内存资源和CPU。
通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为运行和调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度,从而显著提高系统资源的利用率和吞吐量。
1.3.2 进程与线程的区别和联系
(1)划分尺度:线程更小,所以多线程程序并发性更高;
(2)资源分配:进程是资源分配的基本单位,同一进程内多个线程共享其资源;
(3)地址空间:进程拥有的地址空间,同一进程内多个线程共享其资源; (4)处理器调度:线程是处理器调度的基本单位;
(5)执行:每个线程都有一个程序运行的入口,顺序执行序列和程序的出口,但线程不能单独执行,必须组成进程,一个进程至少有一个主线程。简而言之,一个程序至少有一个进程,一个进程至少有一个线程。
1.3.3 进程与程序的区别和联系
(1)程序只是一组指令的有序集合,它本身没有任何运行的含义,它只是一个静态的实体。而进程则不同,它是程序在某个数据集上的执行。进程是一个动态的实体,它有自己的生命周期。反映了一个程序在一定的数据集上运行的全部动态过程。
(2)进程和程序并不是一一对应的,一个程序执行在不同的数据集上就成为不同的进程,可以用进程控制块来唯一地标识每个进程。而这一点正是程序无法做到的,由于程序没有和数据产生直接的联系,既使是执行不同的数据的程序,他们的指令的集合依然是一样的,所以无法唯一地标识出这些运行于不同数据集上的程序。一般来说,一个进程肯定有一个与之对应的程序,而且只有一个。而一个程序有可能没有与之对应的进程(因为它没有执行),也有可能有多个进程与之对应(运行在几个不同的数据集上)。
(3)进程还具有并发性和交往性,这也与程序的封闭性不同。 1.3.4 进程与程序区别和联系的表现
1)程序只是一组指令的有序集合,它本身没有任何运行的含义,它只是一个静态的实体。而进程则不同,它是程序在某个数据集上的执行。
进程是一个动态的实体,它有自己的生命周期。它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而被撤消。反映了一个程序在一定的数据集上运行的全部动态过程。
2)进程和程序并不是一一对应的,一个程序执行在不同的数据集上就成为不
同的进程,可以用进程控制块来唯一地标识每个进程。而这一点正是程序无法做到的,由于程序没有和数据产生直接的联系,既使是执行不同的数据的程序,他们的指令的集合依然是一样的,所以无法唯一地标识出这些运行于不同数据集上的程序。一般来说,一个进程肯定有一个与之对应的程序,而且只有一个。而一个程序有可能没有与之对应的进程(因为它没有执行),也有可能有多个进程与之对应(运行在几个不同的数据集上)。
3)进程还具有并发性和交往性,这也与程序的封闭性不同。进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。进程和线程的区别在于:
简而言之,一个程序至少有一个进程,一个进程至少有一个线程. 线程的划分尺度小于进程,使得多线程程序的并发性高。
另外,进程在执行过程中拥有的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
线程在执行过程中与进程还是有区别的。每个的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
进程是具有一定功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个单位.
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序 13
计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.
线程与进程的区别可以归纳为以下几点:
1)地址空间和其它资源(如打开文件):进程间相互,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
2)通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
3)调度和切换:线程上下文切换比进程上下文切换要快得多。 4)在多线程OS中,进程不是一个可执行的实体。 1.3.5 Windows中与进程和线程相关联的数据结构 Windows进程和线程的关键数据结构: a.执行体进线程块
执行体进线程对象的对象体,包括进程ID、父进程ID、程序名、进程优先级、内存管理信息、设备映像等。
b.核心进线程块:
内核进线程对象的对象体,又称PCB,包括线程调度时需要的信息,如进线程状态、线程时间片等。
c.进线程环境块:
包括用户态代码需要和修改的信息。
Windows环境子系统核心态部件win32k.sys为每个进线程建立的进线程 信息数据结构WIN32KPROCESS、WIN32THREAD
Windows环境子系统进程csrss(用户态)为每个进线程建立的进线程信
息。