进程与线程之间的关系

来源:互联网 发布:电路模拟软件 编辑:程序博客网 时间:2024/05/22 16:44

1、前言

      前段时间没事跑到北京去应聘,HR和我聊得很high,给我描述了来公司后的宏伟蓝图,一顿神游;到技术面时来了两个小崽子,一顿算法和数据结构,没有准备的我狼狈不堪,完事,HR很疑惑地问我,怎么技术那边说你技术不是很强,这是怎么回事?我估计HR是不是心里在嘀咕,这人不是个大忽悠吧,浪费我那么多吐沫星子!回来痛定思痛,还是埋头补课吧。本文是网上有关资料的基础上整理归纳的。


2、来历

(1)下面细说背景

       CPU+RAM+各种资源(比如显卡,光驱,键盘,GPS,等等外设)构成我们的电脑,电脑的运行,实际就是CPU和相关寄存器以及RAM之间交互的过程。

(2)一个最最基础的事实

       CPU太快,太快,太快了,寄存器仅仅能够追的上他的脚步,RAM和别的挂在各总线上的设备完全是望其项背。那当多个任务要执行的时候怎么办呢?轮流着来?或者谁优先级高谁来?不管怎么样的策略,一句话就是在CPU看来就是轮流着来。   

(3)一个必须知道的事实:

        执行一段程序代码实现一个功能的过程 :当得到CPU的时候,相关的资源必须也已经就位,就是显卡啊,GPS啊什么的必须就位,然后CPU开始执行。这里除了CPU以外其它所有资源就构成了这个程序的执行环境,也就是我们所定义的程序上下文。当这个程序执行完了,或者分配给它的CPU执行时间用完了,那它就要被切换出去,等待下一次CPU的临幸。在被切换出去的最后一步工作就是保存程序上下文,因为这个是下次它被CPU临幸的运行环境,必须保存。

(4)串联起来的事实:

       前面讲过在CPU看来所有的任务都是一个一个的轮流执行的,具体的轮流方法就是:先加载程序A的上下文,然后开始执行A,保存程序A的上下文,调入下一个要执行的程序B的程序上下文,然后开始执行B,保存程序B的上下文......

    得益于CPU的计算速度,我们可以同时运行多个任务,实质上是多个任务之间轮流使用CPU资源,由于速度超快,给用户的感觉就是并行的。

       进程和线程就是这样的背景出来的,两个名词不过是对应的CPU时间段的描述,名词就是这样的功能。


进程是什么呢?

         · 进程就是包换上下文切换的程序执行时间总和 = CPU加载上下文+CPU执行+CPU保存上下文

     所以,进程是资源分配的基本单位。一般来说宏观上可以看做是一个软件的运行,例如一个word文档的打开,开了一个进程开个QQ,开了一个进程;开了迅雷,开了一个进程。


为什么引入线程?

        首先我们引入了进程这个概念,虽然进程有利于资源的管理和保护。然而在实际应用中,进程有这样的问题: 


        1. 进程切换的代价、开销比较大; 
        2. 在一个进程内也需要并行执行多个程序,实现不同的功能。 
        3. 进程有时候性能比较低。

 引入线程有以下三个方面的考虑

  1. 应用的需要。比如打开一个浏览器,我想一边浏览网页,一边下载文件,一边播放音乐。如果一个浏览器是一个进程,那么这样的需求需要线程机制。
  2. 开销的考虑。在进程内创建、终止线程比创建、终止进程要快。同一进程内的线程间切换比进程间的切换要快,尤其是用户级线程间的切换。线程之间相互通信无须通过内核(同一进程内的线程共享内存和文件)
  3. 性能的考虑。多个线程中,任务功能不同(有的负责计算,有的负责I/O),如果有多个处理器,一个进程就可以有很多的任务同时在执行。

线程是什么呢?

     如果我们把进程比喻为一个运行在电脑上的软件,那么一个软件的执行不可能是一条逻辑执行的,必定有多个分支和多个程序段,就好比要实现程序A,实际分成abc等多个功能组合而成。那么这里具体的执行就可能变成:

     程序A得到CPU =CPU加载上下文,开始执行程序Aa小段,然后执行Ab小段,然后再执行Ac小段,最后CPU保存A的上下文。

     这里a,b,c的执行是共享了A的上下文,CPU在执行的时候没有进行上下文切换的。

             这里的a,b,c就是线程,也就是说线程是共享了进程的上下文环境的更为细小的CPU时间段。

    

线程的属性

  • 有标识符ID
  • 有状态及状态转换—->>需要提供一些状态转换操作
  • 不运行时需要保存上下文环境—>>需要程序计数器等寄存器
  • 有自己的栈和栈指针
  • 共享所在进程的地址空间和其它资源

         线程只是CPU轮流调度的基本单位,其它上下文信息用所在进程中的。这样上下文切换的耗时就降了下来。同样的,宏观上来可以看做是一个软件中的多个处理功能,例如上述打开word中拼写检查功能、字体加粗……,例如QQ的这个进程里,传输文字开一个线程、传输语音开了一个线程、弹出对话框又开了一个线程。


总结: 

       进程和线程都是一个时间段的描述,是CPU工作时间段的描述,不过是颗粒大小不同。

      一般来说,进程是资源分配的基本单位,线程是CPU调度的基本的单位,线程属于进程。


3、换个姿势

     抛开各种技术细节,从应用程序角度讲:

1、在单核计算机里,有一个资源是无法被多个程序并行使用的:cpu。

         没有操作系统的情况下,一个程序一直独占着全都cpu。

         如果要有两个任务来共享同一个CPU,程序员就需要仔细地为程序安排好运行计划--某时刻cpu和由程序A来独享,下一时刻cpu由程序B来独享

        而这种安排计划后来成为OS的核心组件,被单独名命为“scheduler”,即“调度器”,它关心的只是怎样把单个cpu的运行拆分成一段一段的“运行片”,轮流分给不同的程序去使用,而在宏观上,因为分配切换的速度极快,就制造出多程序并行在一个cpu上的假象。

2、在单核计算机里,有一个资源可以被多个程序共用,然而会引出麻烦:内存。

         在一个只有调度器,没有内存管理组件的操作系统上,程序员需要手工为每个程序安排运行的空间 -- 程序A使用物理地址0x00-0xff,程序B使用物理地址0x100-0x1ff,等等。

         然而这样做有个很大的问题:每个程序都要协调商量好怎样使用同一个内存上的不同空间,软件系统和硬件系统千差万别,使这种定制的方案没有可行性。
        为了解决这个麻烦,计算机系统引入了“虚拟地址”的概念,从三方面入手来做:

        2.1、硬件上,CPU增加了一个专门的模块叫MMU,负责转换虚拟地址和物理地址。
        2.2、操作系统上,操作系统增加了另一个核心组件:memory management,即内存管理模块,它管理物理内存、虚拟内存相关的一系列事务。
        2.3、应用程序上,发明了一个叫做【进程】的模型,(注意)每个进程都用【完全一样的】虚拟地址空间,然而经由操作系统和硬件MMU协作,映射到不同的物理地址空间上。不同的【进程】,都有各自独立的物理内存空间,不用一些特殊手段,是无法访问别的进程的物理内存的。

3、现在,不同的应用程序,可以不关心底层的物理内存分配,也不关心CPU的协调共享了。然而还有一个问题存在:有一些程序,想要共享CPU,【并且还要共享同样的物理内存】,这时候,一个叫【线程】的模型就出现了,它们被包裹在进程里面,在调度器的管理下共享CPu,拥有同样的虚拟地址空间,同时也共享同一个物理地址空间,然而,它们无法越过包裹自己的进程,去访问别一个进程的物理地址空间。

4、进程之间怎样共享同一个物理地址空间呢?不同的系统方法各异,符合posix规范的操作系统都提供了一个接口,叫mmap,可以把一个物理地址空间映射到不同的进程中,由不同的进程来共享。

5、PS:在有的操作系统里,进程不是调度单位(即不能被调度器使用),线程是最基本的调度单位,调度器只调度线程,不调度进程,比如VxWorks


4、区别

  1. 定义方面:进程是程序在某个数据集合上的一次运行活动;线程是进程中的一个执行路径。
  2. 角色方面:在支持线程机制的系统中,进程是系统资源分配的单位,线程是系统调度的单位。
  3. 资源共享方面:进程之间不能共享资源,而线程共享所在进程的地址空间和其它资源。同时线程还有自己的栈和栈指针,程序计数器等寄存器。
  4. 独立性方面:进程有自己独立的地址空间,而线程没有,线程必须依赖于进程而存在。

5、参考文献

   1、图文并茂篇:进程与线程的一个简单解释

    2、循序渐进篇:android 线程与进程 区别 联系     、 

linux基础——linux进程与线程的区别与联系

    3、编程说明篇:多线程和多进程的区别(小结)其中提到了多线程带来的线程安全和函数可重入的问题。

   4、深入细致篇:进程,线程 ->iOS 多线程 runloop

    5、高级进阶篇:Processes and Threads


原创粉丝点击