1. 进程的本质与描述
1.1 进程的基本概念
-
通俗定义:
-
程序的一个执行实例。
-
正在执行的程序。
-
-
内核观点 :担当分配系统资源(CPU时间,内存)的实体。
-
组成公式:
text
进程 = 内核数据结构对象 + 自己的代码和数据
进程 = PCB (task_struct) + 自己的代码和数据
- 核心理解 :操作系统中每一个正在执行的程序(包括所有命令行工具、用户程序等)都是进程。操作系统对所有被加载到内存中的程序进行管理。
1.2 进程控制块(PCB)与"先描述,再组织"
-
PCB (Process Control Block) :操作系统为每个进程维护的一个数据结构 ,用于存放该进程的所有属性信息。在Linux中具体实现为
struct task_struct。 -
管理方式 :操作系统将所有进程的PCB通过数据结构(如链表)组织起来。因此,对进程的管理,就转化为对这个PCB链表的增、删、查、改。
-
代码示例:
c
// 简化的PCB结构示意
struct task_struct {
// 标识符:唯一标识进程的ID(PID)
// 状态:进程的运行状态(运行、就绪、阻塞等)
// 优先级:进程调度的优先级
// 程序计数器:即将执行的下一条指令地址
// 内存指针:指向代码和数据的指针
// 上下文数据:CPU寄存器的数据快照(用于进程切换)
// I/O状态信息:进程使用的I/O设备和文件列表
// 记账信息:CPU使用时间等统计信息
// 链接指针:用于组织进程链表
struct task_struct *next;
// ... 其他属性
};
2. 进程标识与信息查看
2.1 getpid()与getppid()
-
getpid():获取当前进程的进程ID (PID)。 -
getppid():获取当前进程的父进程ID (PPID)。
2.2 进程信息在 /proc 文件系统中的体现
-
Linux将正在运行的进程信息 映射到
/proc/[pid]目录下。 -
重要符号链接:
-
cwd:指向进程的当前工作目录 。例如fopen("d.txt", "w")会在此目录下创建文件。 -
exe:指向进程对应的可执行程序文件的完整路径。
-
2.3 命令行解释器(Shell)与进程的关系
-
用户登录后,操作系统会分配一个命令行解释器进程(如bash)。
-
用户在Shell中输入命令执行程序时,Shell进程会调用
fork()创建子进程来运行该命令。 -
因此,我们执行的大多数命令进程的父进程ID (PPID) 通常就是当前Shell的PID。
3. 进程的创建:fork()
3.1 fork() 的基本行为
-
功能:创建一个新的子进程。
-
调用方式 :
pid_t fork(void); -
核心特点 :调用一次,返回两次。分别在父进程和子进程中各返回一次。
-
返回值含义:
-
在父进程 中:返回新创建的子进程的 PID(大于0)。
-
在子进程 中:返回 0。
-
如果创建失败(例如内存不足),则在父进程中返回 -1 ,并设置
errno。
-
3.2 为什么fork()会返回两次?
fork() 的核心功能是创建子进程,包括:
-
为子进程申请新的PCB。
-
拷贝父进程的PCB信息给子进程。
-
将子进程的PCB插入进程链表 和调度队列。
- 当执行到
return语句时,子进程已经被创建并可能已被调度 。因此,fork()会在两个独立的进程上下文(父、子)中各执行一次返回操作,从而产生两次返回.
- 当执行到
3.3 为什么同一个变量 id 能同时满足 id == 0 和 id > 0 的条件,导致 if 和 else 都能执行?
-
父进程和子进程拥有各自独立的地址空间 。
fork()之后,子进程获得了父进程数据段的副本。 -
在父进程的地址空间中,
id被赋值为子进程的PID(>0)。 -
在子进程的地址空间中,
id被赋值为0。 -
因此,两个进程根据自己地址空间中的
id值,执行了不同的代码分支 。从外部观察,就好像if和else同时成立。
4. 进程的独立性与写时拷贝
4.1 进程的独立性
-
核心原则 :进程是操作系统资源分配的基本单位,进程之间相互独立。一个进程的异常不应影响其他进程(尤其是父进程)的运行。
-
体现在数据上:子进程创建后,理论上应拥有父进程数据的独立副本。
4.2 写时拷贝
-
问题 :如果
fork()时立刻复制父进程的全部数据,会造成巨大的内存和时间开销 ,而很多情况下子进程会立刻执行exec()加载新程序,导致复制的数据被丢弃。 -
解决方案 :写时拷贝技术。
-
工作原理:
-
fork()创建子进程时,并不立即复制父进程的数据(代码段、数据段、堆栈等)。 -
内核将父子进程的地址空间映射到相同的物理内存页 ,并将这些页标记为只读。
-
当任何一个进程试图修改某块数据时,会触发一个页错误(Page Fault)。
-
此时,操作系统内核才真正地为要修改的进程复制该内存页,并修改其页表映射,使其指向新的副本。然后恢复进程执行,完成写操作。
-
-
优势:
-
延迟拷贝 :避免了不必要的复制,大大提升了
fork()的效率。 -
节约资源:如果数据不需要修改,则父子进程可以一直共享同一份物理内存。
-
保障独立:从进程视角看,每个进程都拥有自己独立的数据副本,符合进程独立性的要求。
-
5. 总结
-
进程是程序的执行实体 ,由PCB(
task_struct)和代码数据组成。操作系统通过管理PCB链表来管理进程。 -
进程有唯一的PID,可以通过
/proc/[pid]查看其详细信息。Shell是特殊的进程,负责创建其他用户进程。 -
fork()是创建进程的基本方式,其"调用一次,返回两次"的特性是实现多任务的基础。 -
写时拷贝是保证进程数据独立性的关键技术,它在效率和资源利用上达到了很好的平衡。