线程与进程(浅谈系统篇)

进程与线程是一个大的概念,在操作系统中的线程与进程运行的大的环境,与JAVA线程与进程有别。但java的进程与线程是基于操作系统而言的,所以我们学习java的线程与进程开发时,必须要先了解在操作系统中其的概念、本质、生命周期等。

所以我会先把操作系统中的进程与线程按照我的理解尽可能的阐述。

 

让我们先来谈谈操作系统中的进程与线程

一、概念与原理

概念:

1、进程的主要概念有两点:第一:进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)[存储处理器执行的代码]、1数据区域(data region)[存储变量和运行期间动态分配的内存]、堆栈(stack region)[活动过程调用的指令和本地变量]。

2、线程(thread)是操作系统能够运算调度的最小单位。它被包含在进程中,是进程中的实际运作单位。一个标准的线程由线程ID、当前指令指针(PC)、寄存器和堆栈组成。

值得一提的是,线程进程都是一种抽象的概念。进程的抽象理解是这样:是计算机中的程序关于某数据集合上的一次运动活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

两者之间的联系:

抽象概念我建议就去抽象的理解它们。在面对一个困难的时候,我们的大脑就是计算机,而进程是我们解决问题的想法,想法确定之后,就在我们的大脑运作,然后指挥我们的身体去动作。而线程就是想法中的思路。思路可以共享想法中的各种灵光一闪的ider(同一个进程当中,多条线程共享该进程中的全部系统资源),但每一个思路都是独立的(每个线程拥有自己的调用栈【call stack】、自己的寄存器环境【register context】、自己的线程本地存储【thread-local stroage】)。

线程是执行程序的最小单位,线程一定拥有父进程。而进程是操作系统分配资源的最小单位。

 

二、特点

线程的特点:在多线程OS中,通常在一个进程中包括多个线程,每个线程都是作为利用cpu的基本单位,是花费最小的开销实体。

1.轻型实体:线程几乎不拥有系统资源,但仍然需要必不可少的保证其独立运行的资源。

线程的实体包括程序、数据和TCB(Thread Control Block)。TCB包括以下信息:线程状态;当线程不运行时候,被保存的现场资源;一组执行的堆栈;存放每个线程的局部变量主存区;访问同一个进程中的主存和其他资源。TCB用于指示被执行指令序列的程序计数器、保留局部变量、少数状态参数和返回地址等一组寄存器和堆栈。

2.独立调度和分派的基本单位。

3.可并发执行

4.共享进程资源:所有线程都具有相同的地址空间(进程的地址空间),所以线程之间通信不需要通过内核。

 

进程:

进程一般由程序、数据集合和进程控制块三部分组成。程序控制块(Program Control Block,简称PCB),包含进程的描述信息和控制信息,是进程存在的唯一标志。

动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生的,动态消亡的。

并发性:任何进程都可以同其他进程一起运行。

独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位。

异步性:由于进程之间相互制约,使进程的执行具有间断性,即进程按照各自独立的,不可预知的速度向前前进。

结构特征:由程序、数据、进程控制块三部分组成。

 

系统的任务调度:

大部分的操作系统的任务调度是采用时间片轮转的抢占调度方式,也就是说一个任务执行一小段之后强制暂停,去执行另一个任务,让每个任务轮流执行。由于cpu的执行效率相当高,时间片段特别短,在各个任务之间快速的切换,给人的感觉就是在同时进行。

(图片来自网络)

 

随着计算机的发展,进程之间的切换对于系统内存的开销特别大,于是发明了线程。在早期的操作系统中并没有线程的概念,进程是能拥有资源和独立运行的最小单位,也是程序执行的最小单位。它相当于一个进程只有一个线程,进程本身就是线程。所以有时候进程被称为轻量级进程(Light weight Process,LWP)。

 

三、操作系统中的线程模型:(这里引用)

它有两个模型:

1.线程实现在用户空间下:管理过程全部由用户程序控制,操作系统内核心只对进程管理。

当线程在用户空间下,操作系统对线程的存在一无所知,操作系统只知道进程而不知道线程。所有的线程都是在用户空间实现的。在操作系统看来,每一个进程都只有一个线程,过去的系统大部分还是这种实现方式。这样做的好处是,即使操作系统不支持线程,也可以通过库函数来支持线程。

在这模型下:程序员需要自己实现线程的数据结构、创建销毁和调度维护。也就相当于实现一个自己的线程调度内核,而同时这些线程运行在操作系统的一个进程内,最后操作系统直接对进程进行调度。

优点:首先确实在系统中实现了真实的多线程,其次就是线程的调度只是在用户态,减少了操作系统从内核态到用户态的切换开销。

缺点:由于操作系统不知道线程的存在,因此当一个进程的某一个线程进行系统调用的时候,该线程因某种原因中断而导致线程阻塞,此时操作系统会阻塞整个进程,即使这个进程中其它线程还在工作。还有一个问题是假如进程中一个线程长时间不释放CPU,因为用户空间并没有时钟中断机制,会导致此进程中的其它线程得不到CPU而持续等待。

线程实现在操作系统内核中:

内核系统就是直接由操作系统(kernel)支持的线程,这种线程由内核来完成线程切换,内核通过操作调度器(Scheduler)对线程进行调度,并负责将线程的任务映射各个处理器上。每个内核线程可以视为内核处理的分身,这样操作系统有能力同时处理多件事情,支持多线程的内核叫做多线程内核(Multi-Threads Kernel)。

通俗的将就是,程序员直接使用操作系统中已经实现的线程,而线程的创建、销毁、调度和维护,都是靠操作系统(准确的说是内核)来实现,程序员只需要使用系统调用,而不需要自己设计线程的调度算法和线程对CPU资源的抢占使用。

而不得提到的就是,Linux下的轻量级进程,因为Linux并不像Windows那样,真正的实现了线程的数据结构,而是直接采用了和系统中进程一样的实现方式。

轻量级进程(LWP)是建立在内核之上并由内核支持的用户线程,它是内核线程的高度抽象,每一个轻量级进程都与一个特定的内核线程关联。内核线程只能由内核管理并像普通进程一样被调度。

Linux中程序一般不会直接去使用内核线程,而是去使用内核线程的一种高级接口–轻量级进程,轻量级进程就是我们通常意义上所讲的线程,由于每个轻量级进程都由一个内核线程支持,因此只有先支持内核线程,才能有轻量级进程。这种轻量级进程与内核线程之间1:1的关系称为一对一的线程模型。

PS:其实Linux中的pthread库就是调用了轻量级线程接口,来在操作系统中创建一个内核线程。

1.3使用用户线程加轻量级进程混合实现
在这种混合实现下,即存在用户线程,也存在轻量级进程。用户线程还是完全建立在用户空间中,因此用户线程的创建、切换、析构等操作依然廉价,并且可以支持大规模的用户线程并发。而操作系统提供支持的轻量级进程则作为用户线程和内核线程之间的桥梁,这样可以使用内核提供的线程调度功能及处理器映射,并且用户线程的系统调用要通过轻量级进程来完成,大大降低了整个进程被完全阻塞的风险。在这种混合模式中,用户线程与轻量级进程的数量比是不定的,即为N:M的关系:

 

明白了前面两种模型,就应该很好理解这种线程模型了,但实际上现在主流的操作系统已经不太常用这种线程模型了。
---------------------
作者:CringKong
来源:CSDN
原文:https://blog.csdn.net/cringkong/article/details/79994511
版权声明:本文为博主原创文章,转载请附上博文链接!

 

四、生命周期

进程的生命周期:

 

1.就绪状态(Ready):进程已获得除处理器之外的所需资源,等待分配处理器资源;只要分配了处理器进程就可以执行。就绪进程可以按照多个优先级来划分队列。例如,当一个进程由于时间片用完而进入就绪状态时,就排入低优先级队列;当进程由I/O流操作完成而进入就绪状态时候,排入高优先级队列。

2.运行状态(Running):进程占用处理器资源;处于此状态的进程数目小于处理的数目。在没有其他进程可以执行的时候,(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。

3.阻塞状态(Blocked):由于进程等待某种条件(如I/O操作或进程同步),在条件满足之前无法继续执行。该事件发生前即使把处理器资源分配给该进程,也无法运行。

线程生命周期:

1.新生状态(New Thread):产生一个Thread对象就生成了一个新的线程。当线程处于"新线程"状态时,仅仅是一个空线程对象,它还没有分配到系统资源。因此只能启动或终止它。任何其他操作都会引发异常。例如,一个线程调用了new方法之后,并在调用start方法之前的处于新线程状态,可以调用start和stop方法。

2.可运行态(Runnable):start()方法产生运行线程必须的资源,调度线程执行,并且调用线程run()方法。
3.阻塞态(Not Runnable):当以下事件发生时,线程进入非运行态。①suspend()方法被调用;②sleep()方法被调用;③线程使用wait()来等待条件变量;④线程处于I/O请求的等待。
4.死亡态(Dead):当run()方法返回,或别的线程调用stop()方法,线程进入死亡态。通常Applet使用它的stop()方法来终止它产生的所有线程。
 
线程的基本操作:
派生:线程在进程内派生出来,它即可由进程派生,也可由线程派生。
阻塞(Block):如果一个线程在执行过程中需要等待某个事件发生,则被阻塞。
激活(unblock):如果阻塞线程的事件发生,则该线程被激活并进入就绪队列。
调度(schedule):选择一个就绪线程进入执行状态。
结束(Finish):如果一个线程执行结束,它的寄存器上下文以及堆栈内容等将被释放。
posted @ 2019-03-24 15:35  superDDG  阅读(103)  评论(0编辑  收藏  举报