嵌入式之程序与进程

一、形象比喻:程序是菜谱,进程是做菜过程

程序就像是一本菜谱,而进程则是按照菜谱实际做菜的过程

(一)程序:静态的菜谱

  1. 存储位置与形态菜谱通常被印刷在纸上或保存在电子设备里,就像程序文件存储在电脑的硬盘(或其他存储设备)中。它们都是静态的、不会自己运行的 “死物”。比如你下载的 Chrome 浏览器安装包(.deb 或.rpm 文件),或者用 C 语言编写并编译好的可执行文件(如 a.out),这些都是程序,静静地躺在磁盘上等待被调用。
  1. 可复用性一本菜谱可以被无数次使用,不同的人可以用同一本菜谱做出菜来。类似地,同一个程序可以被多次启动,生成多个不同的进程。比如你同时打开多个 Chrome 浏览器窗口,每个窗口都是 Chrome 程序的一个进程实例。
  1. 不占用运行资源菜谱本身不会占用厨房的灶台、锅碗瓢盆等资源,只有当你开始做菜时才会用到这些工具。程序也是如此,未运行时不占用内存、CPU 等系统资源,仅仅是磁盘上的一堆二进制数据。

(二)进程:动态的做菜过程

  1. 从静止到运行当你拿起菜谱开始做菜时,整个烹饪过程就启动了 —— 你需要打开燃气灶、准备食材、控制火候,这一系列动作构成了一个动态的 “进程”。对应到计算机中,进程就是程序被操作系统加载到内存并执行的过程。例如,当你双击 Chrome 图标时,操作系统会将 Chrome 程序的代码和数据从硬盘读取到内存,并为其分配 CPU 时间、文件句柄等资源,这时一个 Chrome 进程就诞生了。
  1. 生命周期做菜的过程有开始和结束:从你打开第一个食材包装到最后一道菜装盘上桌,这个过程是 “存活” 的;一旦结束,厨房会回归平静,灶台、锅碗等资源也会被释放。进程同样有明确的生命周期:从创建(如通过fork()系统调用)、运行(占用 CPU 执行指令)、暂停(等待用户输入或资源)到终止(正常退出或强制杀死)。你可以通过ps或top命令查看系统中正在运行的进程,就像观察厨房里正在进行的烹饪任务。
  1. 资源占用与独立性每做一道菜都需要独立使用部分厨房资源:比如炖排骨时需要占用高压锅,炒青菜时需要占用炒锅。进程也是如此,每个进程都有自己独立的虚拟内存空间文件描述符表进程控制块(PCB)等资源,彼此之间互不干扰(除非通过进程间通信机制主动交互)。如果某个进程出错(比如炒菜时锅糊了),通常不会影响其他进程(其他菜还能正常做),这体现了进程的独立性和隔离性

(三)关键区别总结

维度

程序(菜谱)

进程(做菜过程)

本质

静态的可执行文件(磁盘存储)

动态的运行实例(内存中活动)

数量关系

一个程序可对应多个进程

一个进程只能对应一个程序

资源占用

不占用内存、CPU 等运行资源

占用内存、CPU、文件句柄等资源

生命周期

永久存在(除非手动删除)

有明确的起止时间(创建 - 运行 - 终止)

独立性

无(程序本身不涉及资源隔离)

强(每个进程有独立的资源空间)

通过这个比喻,你可以简单记住:程序是 “菜谱”,是静态的 “原料”;进程是 “做菜”,是动态的 “行动”。接下来,我们将从专业角度深入解析程序与进程的技术细节,帮助你建立更系统的认知。

二、专业解析:程序与进程的技术本质

(一)程序的本质:从源代码到可执行文件

1. 程序的定义与组成

程序(Program)是一组预定义的、有序的指令集合,用于完成特定任务。在 Linux 系统中,程序通常以可执行文件的形式存在,其本质是经过编译、链接后的二进制代码,包含以下关键部分:

  • 代码段(Text Segment):存储程序的可执行指令(机器码),通常为只读,防止运行时被意外修改。
  • 数据段(Data Segment):存储已初始化的全局变量和静态变量(如int a=5;)。
  • BSS 段(Block Started by Symbol):存储未初始化的全局变量和静态变量,运行前由操作系统自动清零。
  • 文件头(如 ELF 头):包含程序的元数据,如入口地址、段表信息、依赖的共享库等,供操作系统加载程序时解析。
2. 程序的编译与链接过程

以 C 语言程序为例,从源代码到可执行程序需经过以下步骤:

// 示例代码:hello.c

#include <stdio.h>

int main() {

printf("Hello, Linux!\n");

return 0;

}

  • 预处理(Preprocessing):gcc -E hello.c -o hello.i处理#include、#define等预处理指令,展开头文件,生成预处理后的文本文件hello.i。
  • 编译(Compilation):gcc -S hello.i -o hello.s将预处理后的代码编译为汇编语言hello.s。
  • 汇编(Assembly):gcc -c hello.s -o hello.o将汇编语言转换为目标二进制文件hello.o(包含机器码,但未解决外部依赖)。
  • 链接(Linking):gcc hello.o -o hello解析目标文件中的符号引用(如printf函数来自libc.so共享库),生成可执行文件hello。
3. 程序的存储与执行条件
  • 存储介质:程序通常存储在硬盘、U 盘等非易失性存储设备中,可长期保存。
  • 执行前提:程序必须被操作系统加载到内存(RAM)中才能运行。这一过程由 ** 加载器(Loader)** 完成,加载器会根据程序的 ELF 头信息,将代码段、数据段等映射到进程的虚拟地址空间,并建立页表映射(涉及虚拟内存管理)。

(二)进程的本质:操作系统管理资源的基本单位

1. 进程的定义与核心特征

进程(Process)是程序的一次动态执行实例,是操作系统进行资源分配和调度的基本单位。从技术层面看,进程包含以下核心要素:

  • 进程控制块(Process Control Block, PCB):由操作系统维护的结构体,存储进程的元数据,包括:
    • 进程 ID(PID):唯一标识进程的整数(如ps -aux输出中的 PID 列)。
    • 状态(State):运行(R)、就绪(R)、阻塞(D/S)、僵尸(Z)等。
    • 优先级(Priority):决定 CPU 调度顺序(如nice值调整优先级)。
    • 资源指针:指向内存空间、打开的文件描述符、信号处理函数等。
    • 上下文(Context):CPU 寄存器值、程序计数器(PC)等,用于进程切换时恢复执行状态。
  • 虚拟内存空间:每个进程拥有独立的 4GB 虚拟地址空间(32 位系统),分为:
    • 用户空间(0~3GB):包含代码段、数据段、堆(Heap,动态分配内存)、栈(Stack,函数调用栈)等。
    • 内核空间(3~4GB):存储内核代码和数据,所有进程共享,用户态程序需通过系统调用进入内核态访问。
  • 执行上下文:进程运行时的环境,包括当前执行的指令位置、变量值、打开的文件等,体现了进程的 “动态性”。
2. 进程的生命周期与状态转换

进程从创建到终止经历以下阶段,状态转换图如下:

创建(New) → 就绪(Ready) → 运行(Running) ↘

↗ 阻塞(Blocked) ←--------- 等待事件(如I/O完成)

↘ 终止(Terminated) ← 正常退出或强制杀死

  • 就绪态:进程已具备运行条件,但因 CPU 资源不足而等待调度。
  • 运行态:进程正在占用 CPU 执行指令,同一时刻单 CPU 系统中只有一个进程处于运行态。
  • 阻塞态:进程因等待某个事件(如用户输入、文件读写、信号量)而暂停执行,此时不占用 CPU 资源。
  • 终止态:进程执行完毕或被强制终止(如kill -9 PID),内核释放其资源,但 PCB 可能暂时保留(僵尸进程状态),等待父进程读取退出状态。
3. 进程的创建与控制

在 Linux 中,进程通过fork()系统调用创建:

#include <unistd.h>

pid_t fork(void); // 返回值:子进程中为0,父进程中为子进程PID,出错为-1

  • 写时复制(Copy-on-Write, COW):fork()创建子进程时,并不会立即复制父进程的全部内存,而是让父子进程共享相同的物理内存页,只有当其中一方尝试修改内存时,才会触发内核分配新的物理页。这一机制提高了进程创建效率,减少内存占用。
  • exec 系列函数:子进程创建后,通常通过execve()等函数加载并执行新的程序(如execl("/bin/ls", "ls", NULL)),用新程序的内容覆盖当前进程的地址空间,实现 “进程替换”。

(三)程序与进程的核心区别与联系

1. 核心区别对比

特性

程序

进程

存在形式

静态文件(磁盘)

动态实体(内存 + CPU + 资源)

数量关系

单例(一个程序文件)

多例(一个程序可生成多个进程)

资源占用

无(仅磁盘空间)

有(内存、CPU 时间、文件句柄等)

并发性

无(程序本身不运行)

有(多个进程可同时运行或并发调度)

独立性

无(程序文件可被共享读取)

强(进程间默认资源隔离)

状态

永久不变(除非修改文件)

动态变化(运行 / 阻塞 / 终止等)

2. 内在联系
  • 进程是程序的运行载体:没有程序,进程就没有执行的内容;程序必须通过进程才能 “活起来”。
  • 多进程共享程序代码:同一程序的多个进程实例共享磁盘上的程序文件(代码段为只读),但各自拥有独立的数据段、堆、栈等内存空间。例如,多个vi编辑器进程都基于同一vi程序文件,但每个进程编辑的文件内容不同。
  • 程序版本影响进程行为:更新程序文件后,新启动的进程会使用新版本代码,而正在运行的旧进程不受影响(除非重启)。

(四)Linux 进程管理实战

1. 查看进程状态
  • ps命令:显示当前进程快照

ps -aux # 显示所有用户的进程,以BSD格式输出

ps -eLf # 显示所有进程的详细信息,包括线程(LWP列)

关键列说明:

    • USER:进程所有者
    • PID:进程 ID
    • %CPU:CPU 占用率
    • %MEM:内存占用率
    • VSZ:虚拟内存大小(KB)
    • RSS:常驻内存大小(KB)
    • TTY:终端设备(?表示无终端)
    • STAT:进程状态(如R运行,S睡眠,Z僵尸)
    • COMMAND:启动进程的命令
  • top命令:动态监控进程(类似 Windows 任务管理器)

top -d 2 # 每2秒刷新一次

交互操作:

    • P:按 CPU 占用率排序
    • M:按内存占用率排序
    • K:输入 PID 杀死进程
2. 管理进程生命周期
  • 创建进程:直接运行程序(如./hello)或通过脚本启动(如sh script.sh)。
  • 终止进程

kill PID # 发送SIGTERM信号(默认,允许进程优雅退出)

kill -9 PID # 发送SIGKILL信号(强制终止,可能导致数据丢失)

  • 暂停与恢复进程

kill -STOP PID # 暂停进程(状态变为T)

kill -CONT PID # 恢复进程运行

3. 分析进程依赖与资源占用
  • lsof命令:列出打开的文件描述符

lsof -p PID # 查看指定进程打开的文件、网络连接等

  • strace命令:跟踪进程的系统调用

strace -p PID # 监控进程正在执行的系统调用及参数

  • pmap命令:查看进程的内存映射

pmap PID # 显示进程各内存段的地址、大小、权限等信息

(五)深入理解:进程与线程的区别

在现代操作系统中,为了提高并发效率,引入了 ** 线程(Thread)** 概念,作为进程内的执行单元。线程与进程的区别如下:

特性

进程

线程

资源分配单位

是(拥有独立的虚拟内存、文件句柄等)

否(共享所属进程的资源)

调度单位

是(传统操作系统)

是(现代操作系统以线程为调度单位)

创建开销

高(需分配独立内存、复制 PCB 等)

低(仅需创建线程控制块 TCB)

并发性

进程间并发

线程间并发(同一进程内的线程可并行执行)

数据隔离

强(进程间默认不共享数据)

弱(同一进程内的线程共享全局变量、堆等

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值