进程和线程

目录

1.进程

     1.1进程的介绍

1.2进程控制块抽象(PCB)

      1.2.1pid --- 进程标识

      1.2.2内存指针

      1.2.3文件描述符表

      1.2.4CPU

      1.2.5进程的调度

1.3 PCB中关于进程调度相关的属性

      1.3.1状态

      1.3.2优先级

      1.3.3上下文

      1.3.4记账信息

      1.3.5虚拟地址空间

2.线程

2.1线程的介绍

 

3.进程和线程的区别和联系

   


1.进程

    1.1进程的介绍

    每个应用程序运行于现代操作系统之上时,操作系统会提供一种抽象,好像系统上只有这个程序在运行,所有的硬件资源都被这个程序在使用。这种假象是通过抽象了一个进程的概念来完成的,进程可以说是计算机科学中最重要和最成功的概念之一。进程是操作系统对一个正在运行的程序的一种抽象,换言之,可以把进程看做程序的一次运行过程;
   同时,在操作系统内部,进程又是操作系统进行资源分配的基本单位。

   简单点来说进程(process)/任务(tack),就是正在跑起来的程序(正在运行的程序),没有正在运行的程序不能被称作为进程,同一个程序运行多次,可能会产生出多个结果。

   平时所说的程序,指的是一些 exe 的可执行文件得是把程序跑起来,才会涉及到“进程”。

   程序,是一个可执行文件,只是在硬盘上的时候是一个静态的东西,当我们双击程序的时候,此时操作系统,就会把可执行文件中的数据和指令,加载到内存中,并且让 cpu 去执行这里的指令,完成一系列相关的工作. 运行起来的(静态---> 动态)。

   1.2进程控制块抽象(PCB)

      在计算机中针对“进程”的管理,核心思路就是 先描述,在组织

       1.描述:程序猿们使用一个专门的结构体(对于Java而言就是一个低配版的类),来记录一个进程里面的各个属性

       2.组织:这里每个语言会采用自己觉得比较适合的数据结构去把多个进程进行一个有效的组织,随时方便进行遍历,查找,汇总数据  ------ 对于Linux来说 在这里Linux是采用了双向链表的数据结构去解决了该问题(Linux是开源的,其他的就不清楚了)

        在描述的中使用到的专门的结构体就是我们所称的PCB(进程控制块)了,那么这个PCB里面到底有啥信息可以去描述进程里的各个属性呢?

        在这里我先上一段为代码,我接下来会慢慢解释

class PCB {
// 进程的唯一标识 —— pid;
// 内存指针
// 文件描述附表
// 进程关联的程序信息,例如哪个程序,加载到内存中的区域等
// 分配给该资源使用的各个资源
// 进度调度信息
}
    1.2.1pid --- 进程标识

       pid就相当于是进程的身份证了,同一个系统上,同一个时刻中,每个线程的pid一定都是不同的,我们就是通过pid在多个进程中寻找到对应所需的进程的

    1.2.2内存指针

        内存指针表示了该进程,对应的内存资源是啥样的,那么这里就有点困惑了,内存资源中要存储啥呢?

        最重要要存储的就是从exe可执行文件中加载过来的指令数据

        指令:指令是二进制的,都是程序猿在开发这个进程的适合最终编译生成的结果,也就是程序猿们给这个进程写的代码逻辑,通俗点就是把进程比作在舞台上演戏,指令就相当于这台戏的剧本,这台戏大概怎么演都是按照这个剧本来走的。

       数据:数据就是进程在执行指令的时候会依赖的一些数据。

   1.2.3文件描述符表

        文件描述符表就涉及到了硬盘资源了,但是硬盘是硬件资源,应用程序一般是没法接触到"硬件"这一层的,实际上是操作系统抽象成“文件”这一概念,程序操作的是文件,文件实际上是存储在硬盘上的。

        每一个进程都有一个对应的“文件描述符表”来记录,当前这个进程正在使用哪些文件(就比如C语言中对文件操作的函数(方法),fopen,fread等),操作系统打开一个文件就会产生一个“文件描述表”(就像文件的身份标识一样,只是在进程内部生效,出了进程就寄了),同时会使用文件描述符表(类似于数组),把文件描述符表给组织起来了。

   1.2.4CPU

      说到CPU,对电脑组装跟兴趣和对冯诺依曼体系有一定了解的铁子们并不会陌生,CPU在进程这一块起到的作用就相当于表演的舞台,进程需要在CPU上执行对应的指令的(演员要在舞台上把剧本的内容给演绎出来),每个进程,要执行里面的指令完成对应的任务,都需要在CPU(也不一定是只有一个舞台的,现在的CPU很发达,要看你的CPU有多少个逻辑处理器)上执行!!

       这个是我的电脑的逻辑处理器数量,也就意味着有16个“舞台” 

       这样就会产生问题了,一台机子上同一时刻可能会有多个进程需要在这个CPU上进行指令的执行,这样岂不是会起冲突吗?这里就像影视城一样有多个剧组需要在这里拍摄自己的电视剧,那么着多个剧组轮着来使用这块场地不就解决问题了,在进程中也是通过类似的方法来解决的,这种方法被称为进程的调度

   1.2.5进程的调度

    在讲解进程的调度前,我们先讲一下多个进程在CPU上可能出现的执行模式。

    1.并行执行:同一时刻,两个(或多个)进程,同时运行在两个(或多个)CPU逻辑核心上

    2.并行执行:两个(或多个)进程,在同一个“舞台”上轮着上,由于CPU切换进程的速度极快,微观上这俩进程是串行执行的(先执行完一个再执行另一个),当时CPU处理的实在是太快了,导致宏观上看起来这俩进程就是“同时”执行的(注意同时是加上双引号的)。

    操作系统,在调度这些进程的时候(这里调度进程就相当于叫剧组去场地进行拍摄),可能是按照并行执行的方式来调度的,也可能是按照并发执行的方式来调度的,而且在应用程序这一层,是感知不到操作系统到底是按照哪种方式进行调度的(这要在操作系统内核中才能感知到了)。

   由于我们感知不到是哪种方式进行对进程的调度的,并且这两种调度方式在宏观上来说体验效果是一样的,所以通常会使用“并发执行”去代指“并行”和“并发”

1.3 PCB中关于进程调度相关的属性

     1.3.1状态

        在上述表达调度的时候我们不难发现,往往是多个剧组(进程)去等待一块场地(CPU)去完成对剧本(指令)的演绎的,将要被调度到CPU上去执行指令的进程的状态称为“就绪状态”,那么剩下还在等待调度的进程我们将这种状态称为“阻塞状态/睡眠状态”,而且进程能不能上CPU上执行完全是看调度的,所以进程的状态是可以相互转化的(比如我上一秒还在“阻塞状态”,下一秒系统就调度到我了,我此时的状态就从原来的“阻塞”-->“就绪”)。

       关于调度的状态还有很多种,这里先解释这两种就够了,剩下的几种状态就放到多线程的博客去慢慢讲解了

    1.3.2优先级

        在将进程的调度的时候,铁子们可能会有疑惑,这个系统去调度进程上CPU“玩”是有规律的调度还是随机的调度,其实这里没有正确答案,系统的调度确实是随机的,但是系统给进程调度的时候也不是完全公平的(不是每个进程被调度的可能性是相等的),系统也会根据优先级的不同,来决定把时间分配的权衡-----这样做的话 就可以把系统的支援分配给更重要的进程上。

   1.3.3上下文

           这些进程是轮着执行的,可能一次系统的调度是执行不完指令这个时候就只能等待系统的下一次调度了,那么我这一次处理到哪没有东西去记录的话,那我下一次被系统调度的话我岂不是又要从头开始执行指令了?

           这个时候“天降猛男”,上下文就出场了,上下文能保证在下一次被系统调度到CPU的时候能记录到该线程上一次被调度指令完成到哪了,接下来可以从上一次结尾的位置继续往后执行指令,上下文就有点像我们在手机常用的记事本吧。

  1.3.4记账信息

        记账信息就相当于一个统计信息的地方,会统计每一个进程在CPU上执行了多久和执行多少指令,其实由于是随机调度的,记账信息这一功能起到的作用不是特别大,但是就是防止一个特殊情况 系统调度跟某个线程跑了(系统调度到这个线程的频率有点不正常)此时就有给操作系统点反应了,所以记账信息相当于一个兜底。

  1.3.5虚拟地址空间

    在聊这个虚拟地址空间之前,我们先聊聊C语言中的特色---指针。

    在C语言中指针是一个变量,假设这个指针存储了一个数据,那么指针就会指向这个数据存储的地址,在使用指针,尤其是指针的解引用操作的时候,就需要保证你当前进行操作的指针指向的地址是有效的内存,否则对一个指向未知内存的执行进行解引用操作是十分危险的(野指针)。

     按照上述的解释,直接分配物理内存的模型,此时一旦出现指针越界指到其他位置去了,很有可能自己的进程没有很好的执行,还影响了别的进程的执行,但是我们靠人工(程序猿)去保证一是可能会加重程序猿本来就不多的头发,而且最重要的一点就是人工保证是最不靠谱的,系统不难预测这个程序猿的能力和状态到底好不好,能去靠电脑维护死都不打算找人工去维护的,这里又“天降猛男”了,虚拟地址空间就来了。

     简单点来说,系统会献给这个进程去分配一个虚拟的地址,任何在实际的物理内存中随机的找一块大小一样的“空地”,将这块空间去分配给进程,虚拟地址并非是在物理内存上真实存在的地址,它只是在该进程中。

     在不同的进程中,可以存在相同的虚拟地址,但实际上对应到不同的物理地址(跟看二次元皮套人一样,你跟其他人可以喜欢同一个皮套人,但是在现实生活中你只能对你老婆或者女朋友真心(对于恋爱观正常的来说哈)),操作系统在这里很牛的一点就是虚拟地址和实际的物理地址可以灵活的转换

    这样设定之后,每个进程的有效的虚拟地址,都是固定范围.进程使用该虚拟地址的内存,都需要提作系统进行转换成物理地址的过程。这个转换过程中,就可以针对虚拟地址是否 有效 做出一个 校验。

    在虚拟地址空间的操作下进程具有了“独立性”--->每个进程都有自己的虚拟地址空间了,一个进程是无法直接访问或者去修改其他进程虚拟地址空间的内容的(隔离性),这样子就加强了进程的稳定性了。

     但是哈,有时候还是要“犯一下贱”的,我们有时候会有需要多个进程有一点点交互性,这个时候我们就需要提供一个产地去给他们进行交互(这一快博主是学的Java路线对这一块的了解不是很多,目前只知道两种方法),

         1.基于文件(公共区域硬盘)

         2.基于网络(socket)(公共区域网卡) ---- 这种使用的比较多

         //有大佬对这一块有研究的话欢迎在评论区进行说明哈,相互学习学习哈

2.线程

     2.1线程的介绍

       在介绍线程之前,我们先要搞懂进程到底是为了啥要花费我们这么多的文笔去解释它,进程实际上就是是为了去满足“并发编程”这样的需求,随着CPU的发展逐渐变成了多个核心,应用程序也需要做出对应的调整,来让代码可以把多个核心都充分的利用起来,避免“一核有难,多核围观”这样的离谱现象出来。

 

     其实多进程在这一块已经把“并发执行”实现的很不错了,但是还是有个明显的缺点:进程“太重”了,就是说去创建(销毁)一个进程的开销太大了,1是创建(摧毁)一个进程所消耗的资源蛮多一下的,2是创建(销毁)一个进程的速度慢的很。

     而且频繁创建和销毁进程在服务器上出现的场景还是很多的

    这个时候一些鬼才程序猿就想这个居然进程的开销这么大,我们能不能在创建的时候就分给它一个PCB,而不去分配其他的硬件资源,这样不就开销下去了,速度也上来了吗。这个时候本篇的第三位“天降猛男”就出来了 ----- 轻量化进程--->线程(Thread)

    线程和进程本身是属于包含关系的,一个进程中可以包含一个线程,也可以包含多个线程(多线程),这个进程中的多个线程,共同复用了进程中的各种资源(内存,硬盘),但是这些线程各种独立的在CPU上进行调度的,此时一个PCB对应一个线程,多个PCB(线程)对应一个进程,也就是说PCB中的内存指针,文件描述符表(硬件资源)同一个进程中的多个PCB中,这俩字段的内容是一样的,但是上下文,状态,优先级,记账信息...支持调度的属性,则这些PCB每个人的都是不一样的,因为每一个线程都是各自独立在CPU上进行调度的。

    线程是操作系统调度执行的基本单位!!!

 接下来就由我画的图去概括一下进程和线程

 如果我们多加一个进程去完成这个“吃鸡”操作的话

这个时候就会有两个滑稽老铁在两个不同的房间去完成这个“吃鸡”操作,但是可以很明显的发现我为了更快的完成这个吃鸡操作我还要多开一间房去给滑稽老铁去吃鸡,他是不是太赚了。

     此时,就相当于在一个进程中开了两条线程(滑稽老铁)出来,相比于上一个图(开了两个进程)来说效率确实上去了,资源的浪费也减少了。

     这个时候就会有“鬼才”会提出一个大胆的想法了,我直接往这个进程里面加100个滑稽老铁,蛙趣这效率不是嘎嘎高了,有这个想法很不错(准备入院),首先从现实角度来讲嗷

     你这张桌子也不够你100个滑稽老铁去使用把,其次是我这房间就这么大点空间,你确定可以放的下这么多人吗,最后假设我滑稽老铁1就喜欢吃那只坤的鸡脚加,但是有另外一个滑稽老铁就逮着鸡脚吃,这两个老铁就起冲突了,还影响了着一桌人吃鸡的效率,如果有专门调解的滑稽老铁还好说,但是还是会影响整个进程的效率

    线程太多的时候,线程调度的开销反而会拖慢整个线程的效率,而且这样的冲突可能会给整个进程带来bug,一旦某个线程,执行过程中出现异常,并且这个异常还没有被很好的处理, 此时,就可能会导致整个进程直接终止(进程中的所有线程也就随之终止了)。

   因此也不是说线程一定比进程好,多线程编程的优点就是相比于进程更加轻量,创建和销毁的速度会很快,也有缺点--不像进程那样具有隔离性(稳定性低),多线程也好 多进程也好本质上都是为了“并发编程”提供场所,但是作为Java程序猿的话还是采用多线程进行“并发编程”的。

3.进程和线程的区别和联系

   1.进程包含线程,都是为了实现“并发编程”的方式,线程比进程更加的轻量

   2.进程是系统分配资源的基本单位,而线程是系统调度执行的基本单位。创建进程的时候吧分配资源(虚拟地址空间,文件描述符表)的工作给完成了,后续创建线程,直接使用这些公共资源就好了(调度属性的资源还得靠自己去创建)

  3.每个进程都会有独立的地址空间,彼此之间是不会相互影响到的(隔离性)进程具有独立性--》稳定的很,然而多个线程是共同使用一块空间的,一旦其中一个线程抛出异常(遇到bug了),就可能会导致整个进程因为这个异常而结束--》多个线程之间容易相互影响(稳定性就不是很强了)。

    本篇的内容到这就差不多该结束了,如果博主在哪有表述错的地方欢迎大家在评论区指正的,大家共同学习

 

   
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值