C++ 进程与线程

进程概述

一个完整的操作系统 = 内核 + 配套的应用程序(IE浏览器,画图板…)
内核是用来搞管理的软件,一方面是管理计算机的硬件设备,另一方面管理计算机的软件资源。每个硬件都有对应的驱动程序,内核调用驱动程序使用硬件设备

程序:就是磁盘上的可执行文件,并且只占用磁盘上的空间,是一个静态的概念。

进程:被执行后的程序就叫做进程,不占用磁盘空间,需要消耗系统的内存,CPU资源,每个运行的进程都对应一个属于自己的虚拟地址空间。

CPU时间片CPU在某个时间点只能处理一个任务,但是操作系统都支持多任务的,那么在计算机CPU只有一个的情况下是怎么完成任务处理的?原理和古时候救济灾民的思路是一样的,每个人分一点,但是又不叫吃饱。CPU会给每个进程分配一个时间段,进程得到这个时间片之后才可以运行,使各个程序从表面上看是同时进行的。如果在时间片结束时还在运行,CPU的使用权将被收回,该进程将会被中断挂起等待下一个时间片。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换,这样就可以避免CPU资源的浪费。

因此可以得知,在我们使用的计算机中启动的多个程序,从宏观上看是同时运行的,从微观上看由于CPU一次只能处理一个进程,所有它们是轮流执行的,只不过切换速度太快,我们感觉不到罢了,因此CPU的核数越多计算机的处理效率越高。

并发与并行,这两个概念可以笼统的解释为:多个进程同时运行,但是他们两个的同时并不是同一个概念。

并发:并发的同时运行是一个假象,是通过计算机CPU时间片快速切换实现,给人一种同时运行的假象。它是针对某一个硬件资源而言的,在某个时间端快速切换CPI时间片来处理相应的任务。

并行:并发的多进程同时运行是真实存在的,可以在同一时刻同时运行多个进程。并行需要依赖多个硬件资源,单个是无法实现的
进程id:每一个进程都有一个唯一的进程ID,本质是一个整型数

进程的状态:进程有不同的状态,状态是一直在变化的,有创建、就绪、运行、阻塞、停止等状态。

进程对应的虚拟地址空间的信息。

进程五种状态:创建态、就绪态、运行态、阻塞态(挂起态)、退出态(终止态),其中创建态和退出态维持的时间是非常短的,稍纵即逝。我们主要是需要将就绪态,运行态,阻塞态三者之间的状态切换搞明白。

就绪态:万事俱备,只欠东风(CPU资源)。进程被创建出来了,有运行的资格但是还没有运行,需要抢CPU时间片。得到CPU时间片,进程开始运行,从就绪态转换为运行态。进程的CPU时间片用完了,再次失去CPU,就会从运行态转换为就绪态。

运行态:获取到CPU资源的进程,进程只有在这种状态下才能运行。运行态不会一直持续,进程的CPU时间片用完之后,在次失去CPU,从运行态转换为就绪态。只有进程没有退出,就会在就绪态和运行态之间不停的切换。

阻塞态:进程被强制放弃CPU,并且没有抢夺CPU时间片的资格。比如:在程序中调用了某些函数(比如:sleep()),进程就会从运行态转换为阻塞态。当某些条件被满足了(比如:sleep()睡醒了),进程的阻塞状态也就解除了,进程从阻塞态转换为就绪态。

退出态:进程被销毁,占用的系统资源被释放了。任何状态的进程都可以转换退出状态。

注意:当创建出一个进程时,会随之创建一个主线程。

进程间通信

管道通信

管道的是进程间通信(IPC - InterProcess Communication)的一种方式,管道的本质其实就是内核中的一块内存(或者叫内核缓冲区),这块缓冲区中的数据存储在一个环形队列中,因为管道在内核里边,因此我们不能直接对其进行任何操作
在这里插入图片描述
因为管道数据是通过队列来维护的,我们先来分析一个管道中数据的特点:

  • 管道对应的内核缓冲区大小是固定的,默认为4k(也就是队列最大能存储4k数据)
  • 管道分为两部分:读端和写端(队列的两端),数据从写端进入管道,从读端流出管道。
  • 管道中的数据只能读一次,做一次读操作之后数据也就没有了(读数据相当于出队列)。
  • 管道是单工的:数据只能单向流动, 数据从写端流向读端。
  • 对管道的操作(读、写)默认是阻塞的。
  • 读管道:管道中没有数据,读操作被阻塞,当管道中有数据之后阻塞才能解除。
  • 写管道:管道被写满了,写数据的操作被阻塞,当管道变为不满的状态,写阻塞解除。

管道在内核中, 不能直接对其进行操作,我们通过什么方式去读写管道呢?其实管道操作就是文件IO操作,内核中管道的两端分别对应两个文件描述符,通过写端的文件描述符把数据写入到管道中,通过读端的文件描述符将数据从管道中读出来。

为什么可以使用管道进行进程间通信,先看一下下面的图片:
在这里插入图片描述
在上图中假设父进通过一系列操作可以通过文件描述符表中的文件描述符fd3写管道,通过fd4读管道,然后再通过 fork() 创建出子进程,那么在父进程中被分配的文件描述符 fd3, fd4也就被拷贝到子进程中,子进程通过 fd3可以将数据写入到内核的管道中,通过fd4将数据从管道中读出来。

也就是说管道是独立于任何进程的,并且充当了两个进程用于数据通信的载体,只要两个进程能够得到同一个管道的入口和出口(读端和写端的文件描述符),那么他们之间就可以通过管道进行数据的交互。

匿名管道是管道的一种,既然是匿名也就是说这个管道没有名字,但其本质是不变的,就是位于内核中的一块内存,匿名管道拥有上面介绍的管道的所有特性,额外的我们需要知道,匿名管道只能实现有血缘关系的进程间通信,什么叫有血缘的进程关系呢,比如:父子进程,兄弟进程,爷孙进程,叔侄进程。

有名管道拥有管道的所有特性,之所以称之为有名是因为管道在磁盘上有实体文件, 文件类型为p ,有名管道文件大小永远为0,因为有名管道也是将数据存储到内存的缓冲区中,打开这个磁盘上的管道文件就可以得到操作有名管道的文件描述符,通过文件描述符读写管道存储在内核中的数据。

有名管道也可以称为 fifo (first in first out),使用有名管道既可以进行有血缘关系的进程间通信,也可以进行没有血缘关系的进程间通信。创建有名管道的方式有两种,一种是通过命令,一种是通过函数。

创建内存映射区通信

如果想要实现进程间通信,可以通过函数创建一块内存映射区,和管道不同的是管道对应的内存空间在内核中,而内存映射区对应的内存空间在进程的用户区(用于加载动态库的那个区域),也就是说进程间通信使用的内存映射区不是一块,而是在每个进程内部都有一块。

由于每个进程的地址空间是独立的,各个进程之间也不能直接访问对方的内存映射区,需要通信的进程需要将各自的内存映射区和同一个磁盘文件进行映射,这样进程之间就可以通过磁盘文件这个唯一的桥梁完成数据的交互了。
在这里插入图片描述
如上图所示:磁盘文件数据可以完全加载到进程的内存映射区也可以部分加载到进程的内存映射区,当进程A中的内存映射区数据被修改了,数据会被自动同步到磁盘文件,同时和磁盘文件建立映射关系的其他进程内存映射区中的数据也会和磁盘文件进行数据的实时同步,这个同步机制保障了各个进程之间的数据共享

操作内存映射区和操作管道是不一样的,得到内存映射区之后是直接对内存地址进行操作,管道是通过文件描述符读写队列中的数据,管道的读写是阻塞的,内存映射区的读写是非阻塞的。内存映射区创建成功之后,得到了映射区内存的起始地址,使用相关的内存操作函数读写数据就可以了

创建共享内存通信

共享内存不同于内存映射区,它不属于任何进程,并且不受进程生命周期的影响。通过调用Linux或windows提供的系统函数就可得到这块共享内存。使用之前需要让进程和共享内存进行关联,得到共享内存的起始地址之后就可以直接进行读写操作了,进程也可以和这块共享内存解除关联, 解除关联之后就不能操作这块共享内存了。在所有进程间通信的方式中共享内存的效率是最高的

共享内存操作默认不阻塞,如果多个进程同时读写共享内存,可能出现数据混乱,共享内存需要借助其他机制来保证进程间的数据同步,比如:信号量,共享内存内部没有提供这种机制。

信号通信

信号是一种消息处理机制, 它本质上是一个整数,不同的信号对应不同的值,由于信号的结构简单所以天生不能携带很大的信息量,但是信号在系统中的优先级是非常高的

信号产生可以有以下场景:

  • 通过键盘操作产生了信号:用户按下Ctrl-C,这个键盘输入产生一个硬件中断,使用这个快捷键会产生信号, 这个信号会杀死对应的某个进程
  • 通过shell命令产生了信号:通过kill命令终止某一个进程,kill -9 进程PID
  • 通过函数调用产生了信号:如果CPU当前正在执行这个进程的代码调用,比如函数 sleep(),进程收到相关的信号,被迫挂起
  • 通过对硬件进行非法访问产生了信号:正在运行的程序访问了非法内存,发生段错误,进程退出。

信号也可以实现进程间通信,但是信号能传递的数据量很少,不能满足大部分需求,另外信号的优先级很高,并且它对应的处理动作是回调完成的,它会打乱程序原有的处理流程,影响到最终的处理结果。因此非常不建议使用信号进行进程间通信

线程概述

线程是程序的最小执行单元。线程更加廉价,启动速度更快,退出也快,对系统资源的冲击小。在处理多任务程序的时候使用多线程比使用多进程要更有优势,但是线程并不是越多越好,如何控制线程的个数呢?1)文件IO操作:文件IO对CPU是使用率不高,因此可以分时复用CPU时间片,线程的个数 = 2 *CPU核心数(效率最高);2)处理复杂的算法(主要是CPU进行运算,压力大),线程的个数 = CPU的核心数(效率最高)。

C++11线程类+this_thread命名空间</

  • 27
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值