在计算机系统中,程序和进程是两个密切相关但又有本质区别的概念,尤其在内存中的表现上有显著不同:
在这张图中可以直观地看出程序和进程在内存中的结构区别。
基本定义
程序
程序 是一个 静态实体,表示一组写好的指令和数据的集合,一般存储在磁盘上(如 .exe
、.out
文件)。程序本身不占用运行时的系统资源(如 CPU、内存等),它是一个静态文件,它仅包含代码和数据的固定内容,表示完成某种任务的一组指令集合。
- 程序的特性:
- 静态性:程序只是存储在磁盘中的文件,不会动态变化(程序是静态的文件,不占用运行时资源)。
- 无运行环境:程序没有运行时需要的栈、堆、寄存器等概念(程序没有运行时动态分配的内存(如堆和栈))。
- 程序本身不包含对操作系统资源(如 CPU、内存)的使用能力。
进程
- 进程是程序运行后的动态实例,是操作系统分配资源的基本单元;进程是程序的一次执行实例,是一个动态实体。
- 进程是程序运行后的产物,程序一旦被加载到内存并运行,就变成了进程。
- 进程需要操作系统的支持,每个进程都有独立的虚拟地址空间,并占用实际的运行时资源(如内存、CPU、文件描述符等)。
程序是静态代码,进程是动态运行的程序实例。 一个程序可以对应多个进程(如多次运行同一个程序)。
程序和进程在内存中的区别
程序的内存布局:一个程序在磁盘上是静态的,存储的内容包括:
- 代码段:
- 包含程序的指令集(如函数实现、逻辑代码)。
- 代码段是只读的,防止运行时修改。
- 多个进程可以共享同一份代码段,节省内存资源。
- 数据段:
- 包含程序中的全局变量和静态变量的初始值。
- 数据段是可写的,但内容固定,程序编译时就固定下来,程序静态存在时不会动态变化。
进程的内存布局:
当程序运行后,进程会被加载到内存中,成为一个动态实体。操作系统为每个进程分配一个独立的虚拟地址空间,通常包括以下部分(如图所示):
- 代码段:
- 与程序中的代码段一致,存储程序的指令集(通常是只读的)。
- 多个运行同一程序的进程可以共享该段,节省内存。
- 数据段:
- 存储进程运行时的全局变量和静态变量。
- 每个进程的这部分独立存在,不同进程之间互不影响。
- 堆区:
- 堆是运行时动态分配的内存区域,通过 malloc 或 new 等函数分配。
- 堆区是进程运行时动态分配的,大小不固定。
- 堆的大小可以动态增长或缩减,主要用于存储动态对象或数据。
- 栈区:
- 栈用于存储函数调用的局部变量、返回地址以及函数调用的上下文信息。
- 栈的大小在运行时动态变化(通常从高地址向低地址增长)。
- 内核空间:
- 进程虚拟地址空间的高地址部分通常保留给操作系统内核使用(如图中 1GB:内核空间位于进程的虚拟地址空间的高地址部分)。
- 用于存储操作系统内核的数据和代码,用户进程无法直接访问内核空间,必须通过系统调用与内核交互。
图示中已经表明了二者的关键区别:
- 程序:
- 静态结构,存储在磁盘中。
- 没有动态分配的堆和栈。
- 包含固定的代码段和数据段。
- 程序是进程的模板,只有加载到内存并运行后,程序才变成进程。
- 进程:
- 动态结构,在内存中运行。
- 包含动态分配的堆和栈,用于运行时的内存管理。
- 进程的每一部分(如堆、栈、数据段)都是独立的,其他进程访问不到。
- 有操作系统分配的虚拟地址空间(如 4GB),包括用户空间和内核空间。
内存分布的对比
内存区域 | 程序(静态) | 进程(动态) |
---|---|---|
代码段 | 包含程序的指令集,内容固定。 | 包含程序的指令集,可能被多个进程共享。 |
数据段 | 包含全局变量和静态变量的初始值,固定。 | 包含全局变量和静态变量的实际值,独立分配。 |
堆区 | 无堆区。 | 动态分配的内存区域,大小运行时变化。 |
栈区 | 无栈区。 | 存储局部变量、函数调用信息,动态变化。 |
内核空间 | 无操作系统内核相关内容。 | 包含与操作系统交互的内核数据。 |
特性区别:
特性 | 程序 | 进程 |
---|---|---|
静态/动态 | 静态实体,存储在磁盘中 | 动态实体,是程序在运行时的实例 |
文件类型 | 可执行文件(如 .exe、.out) | 运行时的实例,操作系统分配 ID(PID)标识 |
内存结构 | 只包含代码段和数据段 | 包含代码段、数据段、堆、栈和内核空间 |
资源使用 | 不占用系统资源 | 占用内存、CPU、文件描述符等运行时资源 |
并发性 | 程序本身不可并发 | 一个程序可以对应多个进程(不同的运行实例) |
生命周期 | 静态存储在磁盘上,永久存在 | 动态创建和销毁,随运行状态变化 |
共享性 | 所有进程共享同一个程序文件 | 每个进程有独立的虚拟地址空间 |
实际代码:假设有一个程序 example.c
:
#include <stdio.h>
#include <stdlib.h>int global_var = 10; // 全局变量int main() {int local_var = 20; // 局部变量int *dynamic_var = malloc(sizeof(int)); // 动态分配的变量*dynamic_var = 30;printf("global_var: %d\n", global_var);printf("local_var: %d\n", local_var);printf("dynamic_var: %d\n", *dynamic_var);free(dynamic_var);return 0;
}
程序的内存结构:
当程序编译完成后,生成一个可执行文件 example.out
,它在磁盘上的结构如下:
- 代码段:包含 main 函数和 printf 的指令。
- 数据段:包含 global_var 的初始值 10。
进程的内存结构:
当运行 ./example.out
时,操作系统为其创建一个进程,分配内存结构如下:
- 代码段:包含 main 函数的指令,多个进程共享该段。
- 数据段:存储 global_var 的值 10,每个进程有独立的副本。
- 堆区:malloc 分配的内存,存储 dynamic_var 的值 30。
- 栈区:存储局部变量 local_var 的值 20 和函数调用信息。
- 内核空间:包含内核相关的信息(如文件描述符、上下文切换等)。
程序和进程的区别进一步体现了两者的动态行为在内存使用的表现上:
程序的运行流程:
- 程序最初是存储在磁盘上的静态文件(如
example.out
)。 - 操作系统将程序加载到内存,分配代码段和数据段。
- 程序在未运行时,不涉及堆和栈的动态分配。
进程的运行流程:
- 操作系统为进程分配独立的虚拟地址空间(如 4GB 空间)。
- 代码段加载到内存中,多个进程可以共享代码段。
- 数据段初始化全局变量和静态变量。
- 运行时,进程会动态请求堆内存(如
malloc
)。 - 每次函数调用时,进程为局部变量和返回地址分配栈空间。
程序和进程的运行流程
程序的生命周期:
- 编写源代码(
.c
文件)。 - 编译生成可执行文件(如
.out
)。 - 程序静态存储在磁盘中,等待被加载运行。
进程的生命周期:
- 创建:程序被加载到内存中,操作系统分配 PCB(进程控制块)(想了解这个东西详细查看文章:进程控制块)和 虚拟地址空间。
- 运行:进程获得 CPU 执行权,进入运行状态。
- 等待:进程可能因 I/O 操作或资源不足进入等待状态。
- 销毁:进程执行完毕或被操作系统终止,释放资源。
简单的记忆:
- 程序是静态的文件,存储在磁盘上,不占用运行时资源。
- 进程是程序运行时的实例,有独立的虚拟内存空间,包含动态分配的堆、栈和运行时上下文。
- 内存结构:
- 程序只包含代码段和数据段。
- 进程包含代码段、数据段、堆、栈和内核空间。
通过程序和进程的对比,可以理解程序是代码逻辑的载体,而进程是操作系统管理的运行实体。两者结合才能实现计算任务的执行。更简单的说:程序是模板,进程是运行实例。 程序的静态结构在加载后被操作系统动态扩展为进程的完整内存结构。
以上。仅供学习与分享交流,请勿用于商业用途!转载需提前说明。
我是一个十分热爱技术的程序员,希望这篇文章能够对您有帮助,也希望认识更多热爱程序开发的小伙伴。
感谢!