赞
踩
接上次博客:JavaEE初阶(1)(冯诺依曼体系、CPU、CPU基本原理、如何衡量CPU的好坏?指令、操作系统、操作系统“内核”)_di-Dora的博客-CSDN博客
目录
每个应用程序运行于现代操作系统之上时,操作系统会提供一种抽象,好像系统上只有这个程序在运 行,所有的硬件资源都被这个程序在使用。进程是操作系统对一个正在运行的程序的一种抽象,换言之,可以把进程看做程序的一次运行过程; 同时,在操作系统内部,进程又是操作系统进行资源分配的基本单位。
简单来说,进程就是指一个已经运行起来的程序:
对于Windows这个程序来说,一开机就有百八十个进程,有的是系统自动创建的,有的是我们手动创建的。
而没有运行的就不叫进程,只是应用程序。
在图里,你可能会注意到后面这个部分:
每个进程想要执行,就都需要消耗一定的系统资源(硬件资源)。这个部分列出了每个进程消耗的系统资源的多少。
由此可知,每个进程都是操作系统中系统资源分配的基本单位。操作系统通过进程来管理和分配计算机的各种资源,包括:
内存:每个进程都有自己的独立地址空间,操作系统负责分配和管理内存,以确保不同进程之间不会相互干扰或越界访问。
CPU时间:操作系统通过调度算法分配CPU时间片给不同的进程,使它们能够轮流执行,从而实现多任务处理。
文件和I/O资源:每个进程可以打开、读取和写入文件,操作系统管理文件系统和I/O设备,以便进程能够访问和操作文件和外部设备。
网络连接:对于网络应用程序,操作系统负责管理网络连接和数据传输,确保进程能够进行网络通信。
设备驱动程序:操作系统提供设备驱动程序,允许进程与硬件设备进行交互,如打印机、键盘、鼠标等。
进程间通信:操作系统提供进程间通信(IPC)机制,使不同进程能够共享数据和进行通信。
安全性和权限控制:操作系统确保每个进程只能访问其被授权的资源,以维护系统的安全性。
接下来,我们详细了解一下进程在系统中如何进行管理的。
管理涉及到两个角度:
1、描述:使用类/结构体,把被管理的一个对象的各个属性都表示出来;
2、组织:使用数据结构,把这些表示出来的对象串联起来,为了方便后续的增删改查。
系统中专门有一个结构体(操作系统内核是由C/C++写的)描述进程的属性,这个结构体叫做“进程控制块”(Process Control Block , PCB)。
PCB包含了进程的重要信息,如进程ID、状态、寄存器内容、程序计数器、内存分配情况、打开的文件列表等。PCB的目的是将进程的关键属性可视化和管理化,以便操作系统能够有效地控制和维护进程。
我们使用PCB描述进程的属性,一个进程就可以使用一个或者多个PCB来表示。
系统中会使用类似于双向链表这样的数据结构来组织多个PCB。
要想更明确的认识进程的详细特性,我们还需要讨论一下PCB里面的属性,
PCB是一个非常庞大的结构体,因为它需要包含很多属性和信息,以便操作系统能够有效地管理和控制进程。(Linux这样的操作系统代码是开源的,你可以去翻一下源码,只不过它管PCB叫task_struct)
我们来看看PCB的几个主要属性:
进程标识:包括唯一的进程ID和父进程ID等标识信息。
状态信息:表示进程的当前状态,如运行、就绪、等待等。
寄存器信息:保存进程的寄存器状态,以便在上下文切换时能够恢复进程的执行现场。
程序计数器:指向进程正在执行的指令的地址。
内存管理信息:包括进程的内存分配情况、页表等内存管理相关信息。
文件描述符表:维护进程打开的文件和文件描述符的列表。
资源占用信息:表示进程当前占用的资源,如CPU时间、内存、设备等。
优先级和调度信息:包括进程的优先级和调度参数,用于操作系统的进程调度。
进程控制信息:包括进程的父进程ID、子进程列表等。
进程状态信息:用于记录进程的运行时间、创建时间等状态相关信息。
我们重点了解一下其中比较核心的几个:
PID(进程标识符)是一个唯一的数字,用于标识操作系统中每个正在运行的进程。每个进程在创建时都会被分配一个唯一的PID,这个PID在同一时刻不同进程之间是不同的(唯一性)。PID通常是一个整数,可以是正整数或非负整数。
PID的主要作用包括:
唯一标识:PID确保每个进程都具有不同的标识符,使操作系统能够准确地识别和区分各个进程。
进程管理:操作系统使用PID来管理进程,包括创建、销毁、调度和监视进程的状态。
进程通信:PID也用于进程间通信(IPC)机制,以便不同进程能够相互识别和通信。
资源分配:PID还可用于标识进程占用的资源和权限,以确保资源的合理分配和访问控制。
每个进程在运行的时候,都会分配一定的内存空间。这个内存空间通常称为进程的虚拟地址空间。进程的虚拟地址空间包括多个区域,每个区域具有不同的用途,用于存储不同类型的数据。以下是典型的进程虚拟地址空间中的一些重要区域和指针:
代码区(Text Segment):
数据区(Data Segment):
堆区(Heap):
栈区(Stack):
其他区域:
进程的虚拟地址空间是由操作系统管理的,操作系统负责分配和管理进程的内存,确保不同进程之间的内存不会互相干扰。指针是用来访问这些不同区域的关键工具,如栈指针、堆指针、全局指针等。程序的执行过程中,这些指针会根据函数调用、内存分配和释放等操作而不断地移动和更新,以便正确地访问和管理内存。
C语言程序或其他可执行程序在运行时会经历以下过程:
加载可执行文件:
执行指令:
数据加载:
动态内存分配:
栈区管理:
临时数据存储:
总之,可执行程序在运行时会将代码和静态数据加载到内存中,CPU会执行这些指令,而数据区用于存储程序的静态数据。程序还可以使用堆区和栈区来管理动态分配的数据和临时数据。这种内存管理机制允许程序在运行时有效地管理数据和执行逻辑。
综上,进程的虚拟地址空间包括多个区域,每个区域用于存储不同类型的数据。指针在进程的内存管理中起着关键作用,帮助程序访问和操作不同区域的数据。这种内存管理机制确保了进程的安全性和隔离性。
还有一句很重要的话一定要记住,内存指针描述了进程持有的“内存资源”是啥样的。
文件描述符表是用来管理和跟踪进程与文件之间关系的一种数据结构,通常采用类似于顺序表的方式来组织,有很多元素和文件有关,也就对应着和硬盘有关。文件描述符表维护了进程当前打开的文件的信息,包括文件的类型、访问权限以及文件在文件系统中的位置等。
简单来说,一个进程也需要涉及到硬盘操作,就需要按照文件的方式来操作。当前进程关联了哪些文件?都能操作哪些文件?都是通过文件描述符表来确定。
通过文件描述符表,操作系统就可以有效地管理进程与文件之间的关联,使进程能够进行文件操作而无需关心文件的实际物理位置。这种机制允许多个进程同时访问文件系统,确保了数据的完整性和安全性。
因此,文件描述符表描述了进程持有的“硬盘资源”是啥样的
那么,进程持有的CPU资源如何体现?这就更加复杂了,涉及到“进程的调度”。
早期的操作系统是一个“单任务操作系统”,同一时刻只有一个进程能够运行,运行下一个程序,就会退出上一个,这个时候不需要考虑调度的问题。
但是,当时的电脑都是能支持多个任务了,这是怎么做到的?
一个进程要执行,就需要CPU来执行上面的指令。而早期的电脑还是单核CPU,也就是说它同一时刻只能执行一个进程的指令。
在这个背景下我们又怎么实现“多个任务”同时进行呢?
这就涉及到一个词——“分时复用”,用两个字来描述就是“并发”。
分时复用是一种多用户共享计算机资源的操作系统技术,旨在实现多用户之间的公平资源分配、任务调度和交互,允许多个用户通过不同的终端或连接共享计算机系统,每个用户被分配一个时间片段来执行其任务,然后系统切换到下一个用户。这种技术的目标是在单台计算机上支持多个用户的并发访问,以提高计算资源的利用率。
这和“信道复用”很相似:
信道复用是一种通信技术,用于有效地共享通信信道或媒体以传输多个数据流或信号。它允许多个独立的通信流在相同的物理通信媒体上传输,从而提高了通信资源的利用效率。
信道复用通常用于数据通信、电话系统、无线通信和计算机网络等领域。
有两种主要的信道复用技术:时分复用(Time Division Multiplexing,TDM)和频分复用(Frequency Division Multiplexing,FDM)。
时分复用(TDM):
- TDM将通信时间分割成若干个时隙(time slot),每个时隙用于传输不同的数据流或信号。
- 不同通信流的数据在各自的时隙内传输,这样它们不会发生冲突。
- TDM常用于电话系统中,其中多个电话线路可以在同一物理线上传输声音信号,每个电话线路占用一个时隙。
频分复用(FDM):
- FDM将通信信道划分成不同的频率带宽段,每个带宽段用于传输不同的数据流或信号。
- 不同通信流的数据在不同的频率带宽段内传输,这样它们不会互相干扰。
- FDM常用于无线通信中,多个无线信号可以在同一频谱范围内传输,每个信号占用不同的频率带宽段。
码分复用(Code Division Multiplexing,CDM):
- CDM是一种数字通信中的信道复用技术,它使用不同的编码序列来区分不同的数据流。
- 每个数据流使用不同的编码序列,使其在同一时间传输,并在接收端使用正确的解码序列来还原数据。
- CDM常用于无线通信中,如CDMA(Code Division Multiple Access)手机网络。
信道复用技术的选择取决于通信系统的需求和特性。它们可以提高通信信道的利用率,允许多个用户或设备同时共享通信资源,从而实现高效的数据传输和通信。
咱们说的通俗易懂一点就是,只要这些进程轮转的速度够快,看起来就好像是在同时运行一样,毕竟几纳秒的时间对于电脑来说可能有和大区别,但对于我们人类来说没有什么差别。
你可以看到这里的速度是非常快的,一秒钟执行42.9亿条指令,足够很多进程轮转调度很多圈了!
好了,回神!
并发执行是对于单个核心CPU来说的,实际上现代的CPU都是多核心的……
如果两个进程同时在两个CPU核心上,微观上也是同时执行的,我们就把这个叫做“并行”。
而如果是只在一个CPU核心上,通过快速轮转调度的方式执行多个进程,宏观上是同时执行的,但是微观上却是有先后顺序的,这个叫做“并发”。
并行(Parallelism):
并发(Concurrency):
不管是“并行”还是“并发”,在应用程序这一层上我们都是感知不到的,这都是系统内部完成调度的。从编程角度来说,底层是并发还是并行对代码都没啥影响,所以平时我们也就会统一使用“并发”来代指“并行”和“并发”。
PCB中引入了一些属性用来支持操作系实现进程调度:
以上属性在操作系统中起着关键作用,有助于有效管理和调度进程,确保多个任务能够在共享的计算资源上协调运行。同时,它们也为系统管理员和开发人员提供了有关进程行为和性能的重要信息。
操作系统的调度器是操作系统内核的一部分,它负责管理和控制多个进程之间的执行顺序,以实现多任务处理。调度器的主要任务是决定哪个进程应该获得CPU时间,并在多个进程之间进行切换。
以下是一些常见的调度算法,用于决定进程的执行顺序:
先来先服务(First-Come, First-Served,FCFS):
短作业优先(Shortest Job First,SJF):
优先级调度(Priority Scheduling):
轮转调度(Round Robin,RR):
多级反馈队列调度(Multilevel Feedback Queue Scheduling):
最高响应比优先(Highest Response Ratio Next,HRRN):
不同的调度算法适用于不同的应用场景和需求。操作系统通常会根据系统性能和特定任务的需求选择合适的调度算法。一些操作系统甚至支持多种调度算法,以便根据不同的任务类型和工作负载动态选择最佳算法。
我们刚刚讲内存指针的时候提到了一个概念——“虚拟地址空间”。
早期的操作系统,程序运行的时候分配的内存是物理内存。
RAM:
如上图,如果B进程越界访问了A进程的内存,把A的内存写成错误的值,那么就极有可能导致A进程崩溃……
可是我们之前说了,操作系统要给进程提供稳定的运行环境啊!
因此,操作系统引入了“虚拟地址空间”概念,不是直接分配物理内存了,而是分配虚拟的内存空间。也就是说操作系统对于内存又进行了一层抽象。
虚拟地址空间将物理内存的细节抽象化了。进程不需要知道物理内存的具体位置或大小,它只需关注它自己的虚拟地址空间。这允许操作系统有效地管理物理内存,包括内存分配、释放和内存页的置换。
A操作某个内存中的数据,就需要把操作系统的虚拟地址告诉系统,系统再把虚拟地址翻译成物理地址(有一个类似于Hash表这样的映射结构,称为页表),再操作物理地址。
操作系统使用页表或段表等数据结构来执行虚拟地址到物理地址的翻译。这个翻译过程使得虚拟地址在运行时动态映射到物理地址。如果进程尝试访问一个无效或越界的虚拟地址,操作系统会检测到这种情况并终止该进程,从而保护系统的稳定性,并且不会波及到其他进程。
这样的机制就带来了一个进程很重要的特性——独立性。
隔离性和独立性: 每个运行的进程都有自己的虚拟地址空间,这个虚拟地址空间是由操作系统分配的,对其他进程来说是不可见的。这意味着一个进程不能直接访问或修改另一个进程的内存。这种隔离性和独立性是操作系统的重要特性之一,它有助于确保进程之间的互相干扰最小化。
通过上述方式,我们确实把进程给隔离开了。但是如果某个需求中,我们确实就需要让多个进程相互配合、交互数据,那该怎么办好?
此处就需要引入新的机制来实现进程之间的通信。
“为不同的进程提供一种机制,使它们能够在需要时相互协作和交换数据”有很多方式,它们的核心思想大致分为以下两种:
共享内存模型: 在这种模型下,多个进程可以访问和修改同一块共享的物理内存区域。这就意味着它们可以直接读取和写入相同的内存位置,实现了高效的数据共享。但是,这种方法需要非常小心地进行同步和互斥操作,以避免数据竞争和一致性问题。
例子:共享内存、内存映射文件等。消息传递模型: 在这种模型下,进程之间通过发送和接收消息进行通信。每个进程拥有自己的私有内存空间,而通信是通过操作系统或网络进行的。这种方法更安全,因为进程之间不能直接访问彼此的内存,但它需要额外的消息传递机制。
例子:管道、消息队列、套接字、远程过程调用(RPC)等。共享内存适用于需要高效共享大量数据的场景,但需要保证数据一致性。消息传递适用于更安全的通信,但可能会引入一些额外的开销,例如消息传递的封装和解封装。选择哪种模型取决于应用程序的需求和设计目标。
我们这里主要看看第一种思路。
具体的实现方式有很多,但是每个方式的核心思想都是一样的——“借助一个公共空间,完成数据的交互”。下面介绍两种方式:
通过文件:
核心思想: 进程可以通过读写共享文件来进行数据交互。文件是一种持久性存储介质,进程可以将数据写入文件,然后其他进程可以读取同一文件的数据。
使用场景: 通常在本地文件系统中使用,适合需要持久性数据存储和共享的场景。多个进程可以通过访问相同的文件来交换信息,但需要考虑同步和互斥问题以防止数据冲突。
通过网络(Socket):
核心思想: 进程可以通过套接字(Socket)在网络上进行通信。套接字提供了一种网络连接,允许进程在不同的计算机上进行数据交互。通信可以是基于传输层协议(如TCP或UDP)的。
使用场景: 适用于分布式系统和远程通信场景。它允许不同计算机上的进程相互通信,包括客户端-服务器通信和对等通信。Socket通信可以跨越互联网,使远程进程能够交换数据。
这两种方式都允许进程之间在一个共享的环境中进行数据交互,但它们的适用场景和使用方式有所不同。通过文件适用于本地数据存储和共享,通常用于单一计算机上的进程之间。通过网络(Socket)适用于分布式系统、远程通信和多计算机环境下的进程通信,可以在不同计算机之间传输数据。选择哪种方式取决于我们的具体需求和应用程序的设计。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。