【理解进程与线程、并行与并发等】

一、进程

1、进程的概念

1、进程的基本概念

进程是对运行时程序的封装,是系统进行资源调度和分配的基本单位,是操作系统结构的基础,实现了操作系统的并发。

进程是程序真正运行的实例,若干进程可能与同一程序相关,且每个进程皆可以同步或者异步的方式独立运行。

  1. 进程是一个实体,每一个进程都有自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)、堆栈(stack region)。文本区域存储着处理器执行的代码;数据区域存储着变量和进程执行期间使用的动态分配的内存;堆栈存储着活动过程调用的指令和本地变量。
  2. 进程是一个运行中的程序。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行的时候),它才能成为一个活动的实体,就称之为进程。

2、进程与程序的区别

  1. 程序是永存的,进程是暂时的,进程是程序在数据集上的一次执行,有创建有撤销,其存在是暂时的;
  2. 程序是静态的观念,进程是动态的观念;
  3. 进程具有并发性,程序没有;
  4. 进程是竞争计算机资源的基本单位,程序不是;
  5. 进程和程序不是一一对应的:
    一个程序可以有多个进程;
    一个进程可以执行一个或多个程序。

3、并行(parallel)与并发(concurrency)的区别

  1. 并行:**指在同一时刻,有多条指令在多个处理器上同时执行。**就像是两个人各自拿着一把铁锹在不同的地点同时挖坑,一段时间后,每个人都挖了一个大坑,所以无论是从微观还是宏观来看,二者都是一起执行的。
  2. 并发:**指在同一时刻只能有一条指令执行,但有多个进程的指令被快速地轮换执行,**使得在宏观上具有多个进程同时执行的效果,但从微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替地执行。就像是两个人用同一把铁锹,轮流挖坑,一段时间后,两个人各自挖了一个小一点的坑,如果想要挖的坑要大一些,时间就需要长一些。
  3. 并行在多处理器系统中存在,而并发可以在单处理器和多处理器中都存在,并发能够在单处理器系统中存在是因为并发是并行的假象,并行要求程序能够同时执行多个操作,而并发只是要求程序假装同时执行多个操作。

4、描述进程—PCB

进程信息被放在一个叫做进程控制块(process control block)的数据结构中,可以理解为进程属性的集合。

2、进程间通信的方式

进程间通信主要包括管道、系统IPC(包括消息队列、信号量、信号、共享内存等)、套接字socket。

1、管道

管道主要包括无名管道和命名管道:管道可用于具有亲缘关系的父子进程间的通信,命名管道除了具有所具有的功能外,它还允许无亲缘关系进程间的通信。
1. 普通管道PIPE:
①它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端;
②它只能用于具有亲缘关系的进程之间的通信(也就是父子进程或者兄弟进程之间);
③它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read和write等函数,但它不是普通的文件,并不属于其他文件系统,并且只存在于内存之中。
2. 命名管道FIFO
①FIFO可以在无关的进程之间交换数据;
②FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

2、系统IPC

1. 消息队列
消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标记。消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等特点。具有写权限的进程可以按照一定的规则向消息队列中添加新信息;对消息队列有读权限的进程则可以从消息队列中读取信息。

特点:
①消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级;
②消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除;
③消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按照消息的类型读取。
2. 信号量
信号量与消息队列的结构不同,它是一个计数器,可以用来控制多个进程共享资源的访问。信号量用于实现进程间的互斥与同步,而不是用于存储进程间的通信数据。

特点:
①信号量用于进程间同步,若要在进程间传递数据则要结果共享内存;
②信号量基于操作系统的PV操作,程序对信号量的操作都是原子操作;
③每次对信号量的操作不仅限于对信号量值加1或者减1,而且可以加减任意正整数值;
④支持信号量组。
3. 信号
信号是一种比较复杂的通信方式,用于通知接受进程某个事件已经发生。
4. 共享内存
共享内存使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种方式需要依靠某种同步操作,如互斥锁或者信号量等。

特点:
①共享内存是最快的一种IPC,因为进程是直接对内存进行存取;
②因为多个进程可以同时操作,所以需要进行同步;
③信号量和共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。

3、socket

socket也是一种进程间的通信机制,与其他通信机制不同的是,它可以用于不同主机之间的进程通信。

3、进程的特征

进程是由多程序的并发执行引起的,它和程序是两个截然不同的概念。进程的基本特征是对比单个程序的顺序执行提出的,也是对进程管理提出的基本要求。

1. 动态性: 进程是程序的一次执行,它有着创建、活动、暂停、终止等过程,具有一定的生命周期,是动态地产生、变化和消亡的。动态性是进程最基本的特征;
2. 并发性: 指多个进程实体,同存于内存中,能在一段时间内同时运行,并发性是进程的重要特征,同时也是操作系统的重要特征。引入进程的目的就是为了使程序能与其他进程的程序并发执行,以提高资源利用率;
3. 独立性: 指进程实体是一个能独立运行、独立获得资源和独立接受调度的基本单位。凡未建立PCB的程序都不能作为一个独立的单位参与运行;
4. 异步性: 由于进程的相互制约,进程具有执行的间断性,即进程按各自独立的、不可预知的速度往前推进。异步性会导致执行结果的不可再见性,为此,在操作系统中必须配置相应的进程同步机制;
5. 结构性: 每个进程都配置一个PCB对其进行描述。从结构上看,进程实体是由程序段、数据段和进程控制段三部分组成的。

4、进程的状态与转换

1、五种状态

进程在其生命周期内,由于系统中各进程之间的相互制约关系及系统的运行环境的变化,进程的状态也在不断发生变化(一个进程会经历若干种不同状态)。进程通常有以下五种状态,前三种是进程的基本状态:

1. 运行状态: 进程正在处理器上运行,在单处理器环境下,每一时刻最多只有一个进程处于运行状态;
2. 就绪状态: 进程处于准备运行的状态,即进程获得了出处理器之外的一切所需资源,一旦得到处理器即可开始运行;
3. 阻塞状态: 又称为等待状态。进程仔仔等待某一事件而暂停运行,如等待某资源为可用(不包括处理器)或者等待输入/输出完成,即使处理器空闲,该进程也不能运行;
4. 创建状态: 进程正在被创建,尚未转到就绪状态。创建进程通常需要多个步骤:首先申请一个空白的PCB,并向PCB中填写一些控制和管理进程的信息,然后由系统为该进程分配运行时所需的资源,最后将该进程转入就绪状态;
5. 结束状态: 进程正在从系统中消失,这可能是进程正常结束或其他原因中断退出运行。当进程需要结束运行时,系统首先必须设置该进程为结束状态,然后再进一步处理资源释放和回收等工作。

2、状态转换

在这里插入图片描述

1. 就绪状态→运行状态: 处于就绪状态的进程被调度后,获得处理器资源(分派处理器时间片),于是进程由就绪状态转换为运行状态;
2. 运行状态→就绪状态: 处于运行状态的进程在时间片用完后,不得不让出处理器,从而进程由运行状态转换为就绪状态。此外,在可剥夺的操作系统中,当有更高优先级的进程就绪时,调度会将正在运行的进程转为就绪状态,让更高优先级的进程运行;
3. 运行状态→阻塞状态: 当进程请求某一资源(如外设)的使用和分配或者等待某一事件的发生(如I/O操作的完成)时,进程就从运行状态转换为阻塞状态。进程以系统调用的形式请求操作系统提供服务,这是一种特殊的、由运行用户态程序调用程序内核过程的形式;
4. 阻塞状态→就绪状态: 当进程等待的事件到来时,如I/O操作的结束或中断结束时,中断处理程序必须把相应进程的状态由阻塞状态转换为就绪状态。

5、进程号(PID)

每个进程都有一个进程号来进行标识,其类型为pid_t(整型),范围是0~32767。进程号总是唯一的,但进程号可以重用,当一个进程终止后,其进程号就可以再次使用。
获取进程号的函数getpid():

#include<sys/types.h>
#include<unistd.h>
pid_t getpid(void);//功能为获取本进程号,无参数,返回值为进程号,类型为pid_t

父进程号(PPID):任何进程(除int进程)都是由另一个进程创建,该进程被称为创建进程的父进程,对应的进程号就是父进程号。
获取父进程号的函数getppid():

#include<sys/types.h>
#include<unistd.h>
pid_t getppid(void);//功能为获取调用此函数的父进程号,无参数,返回值为父进程号,类型为pid_t

进程组号(PGID):进程组是一个或多个进程的集合。他们之间相互关联,进程组可以接收同一终端的各种信号,关联的进程有一个进程组号。
获取进程组号的函数getpgid():

#include<sys/types.h>
#include<unistd.h>
pid_t getpgid(pid_t pid);//功能为获取进程组号,参数为0时返回当前进程组号,可在参数中指定某一进程,返回指定进程的进程组号,类型为pid_t

6、进程的创建

1. fork()函数创建进程
系统允许一个进程创建新的进程,新进程即为子进程,子进程还可以继续创建新的子进程,形成进程数结构模型。

#include<sys/types.h>
#include<unistd.h>
pid_t fork(void);//从已存在的进程中创建一个新的进程,无参数,若创建成功,父进程返回子进程的进程号,子进程返回0

创建进程失败的两个主要原因:
①当前的进程数已经到了系统规定的上限,这时erno的值被设置为EAGIN;
②系统内存不足,这时erno的值被设置为ENOMEM。
2.创建子进程的过程
使用fork函数得到的子进程是父进程的一个复制品,他从父进程处继承了整个进程的地址空间。包括:进程上下文、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等。子进程所独有的只有它的进程号,计时器等,因此,使用fork函数的代价是很大的。
3.fork函数创建的父子进程的运行顺序
在fork之后,父子进程的运行顺序取决于内核所使用的调度算法。

二、线程

1、线程的概念

线程是进程的子任务,是CPU调度和分派的基本单位,用于保证程序的实时性,实现进程内部的并发;线程是操作系统可识别的最小执行和调度单位。每个线程都独自占有一个虚拟处理器:独立的寄存器组。指令计数器和处理器状态。每个线程完成不同的任务,但是共享同一地址空间(即同样的动态内存,映射文件,目标代码等),打开的文件队列和其他内核资源。

2、线程间通信的方式

线程间主要有四种通信方式:临界区、互斥量synchronized/lock、信号量、事件(信号)。

  1. 临界区:通过多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问;
  2. 互斥量:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问;
  3. 信号量:为控制具有有限数量的用户资源而设计的,它允许多个线程去同时访问同一资源,但一般需要限制同一时刻访问此资源的最大线程数目;
  4. 事件(信号):通过通知操作的方式来保持多线程同步,还可以方便地实现多线程优先级的比较操作。

三、进程与线程的区别

  1. 一个线程只能属于一个进程,而一个进程可以拥有多个线程,但至少有一个线程,线程依赖于进程而存在;
  2. 进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。(资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程的多个线程共享代码段(代码和常量)、数据段(全局变量和静态变量)、扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫做运行时段,用来存放所有局部变量和临时变量。)
  3. 进程是资源分配的最小单位,线程是CPU调度的最小单位;
  4. 系统开销:由于在创建或撤销进程时,系统都要为之分配或者回收资源,如内存空间、i/o设备等。因此,操作系统所付出的开销将显著地大于正在创建或撤销进程时的开销。类似地,在进行进程切换时,涉及到整个当前进程CPU环境的保存以及新被调度运行的进程的CPU环境的设置。而线程切换只须保存和设置少量寄存器的内容,并不涉及到存储器管理方面的操作,可见进程切换的开销远大于线程切换的开销。
  5. 通信:由于同一进程的多个线程具有相同的地址空间,致使它们之间的同步和通信的实现变得比较容易。进程间通信可以直接读写进数据段(如全局变量)来进行通信——需要进程同步和互斥手段的帮助,以保证数据的一致性。在有的系统中,线程的切换、同步和通信都无须操作系统内核的干预。
  6. 进程编程调试简单可靠性高,但是创建和销毁的开销大;线程与其相反,开销小,切换速度快,但是编程调试相对复杂;
  7. 进程间不会相互影响;对于线程,如果一个线程挂掉,将会导致整个进程挂掉;
  8. 进程适用于多核、多机分布;线程适用于多核分布。
  • 22
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值