从0开始掌握Linux进程(1)
一、冯诺依曼体系结构:进程运行的物理基础
1.1 为什么需要冯诺依曼体系?
历史背景:在计算机诞生初期,ENIAC等早期计算机采用"存储程序"概念之前,每次计算都需要重新接线,效率极低。1945年,冯诺依曼提出了"存储程序"概念,将程序和数据统一存储在内存中,彻底改变了计算机的工作方式。
解决的问题: - 程序可重复执行,无需每次重新接线 - 数据和指令统一管理,提高效率 - 实现自动执行,解放人力
1.2 冯诺依曼体系的核心组成
输入设备 → [存储器(内存)] → CPU(运算器+控制器) → [存储器(内存)] → 输出设备
关键特性: - 存储器指的是内存,不是硬盘等外存 - CPU只能直接访问内存,不能直接读写外设 - 所有设备都通过内存交互,内存是数据交换中心
1.3 实际应用:QQ聊天的数据流
以QQ聊天为例,理解冯诺依曼体系的数据流:
你的输入(键盘) → 内存 → CPU处理 → 内存 → 网卡发送
↓
对方网卡接收 → 内存 → CPU处理 → 内存 → 显示器输出
实践启示:理解数据流有助于优化程序性能,减少不必要的内存拷贝。
二、操作系统:进程的管理者
2.1 操作系统的诞生与定位
历史起源:早期计算机需要人工管理硬件资源,效率低下且容易出错。1960年代,操作系统诞生,目的是自动化管理软硬件资源,为用户提供统一的执行环境。
核心定位: - 向下:管理所有硬件和软件资源 - 向上:为应用程序提供良好执行环境
2.2 管理的本质:描述与组织
操作系统管理硬件的方法,也是管理进程的方法:
管理哲学:
1. 描述:用struct结构体描述对象属性
2. 组织:用链表等高效数据结构组织对象
实例:学校管理学生的类比 - 描述学生:姓名、学号、成绩等属性(struct) - 组织学生:按班级、年级组织(链表/树)
2.3 系统调用与库函数
设计初衷:操作系统需要保护硬件安全,不能让用户直接操作硬件。因此提供有限的接口——系统调用。
演进过程:
系统调用(基础) → 封装 → 库函数(易用) → 应用程序
实际应用:
// 系统调用:getpid() - 获取进程ID
#include <unistd.h>
pid_t getpid(void);
// 库函数:printf() - 封装了write系统调用
#include <stdio.h>
printf("Hello World\n");
三、进程概念:从程序到执行实体
3.1 进程概念的诞生
问题背景:早期计算机一次只能运行一个程序,资源利用率极低。多道程序设计出现后,需要同时运行多个程序,如何区分和管理这些正在运行的程序?
解决方案:引入"进程"概念——程序的执行实例。
定义演进: - 课本定义:程序的一个执行实例 - 内核视角:分配系统资源的实体 - 现代定义:进程 = 内核数据结构(task_struct) + 程序代码和数据
3.2 PCB:进程控制块
设计目的:操作系统需要"描述"进程,因此设计了PCB(Process Control Block)。
Linux实现:task_struct结构体
// task_struct包含的关键信息
struct task_struct {
// 标识符:唯一标识进程
pid_t pid;
// 状态:运行、睡眠、停止等
volatile long state;
// 优先级:决定调度顺序
int prio;
// 程序计数器:下一条指令地址
unsigned long eip;
// 内存指针:代码、数据位置
struct mm_struct *mm;
// 上下文数据:寄存器内容
struct thread_info thread_info;
// I/O状态:打开的文件等
struct files_struct *files;
// 记账信息:CPU时间等
cputime_t utime, stime;
};
组织方式:所有进程以双链表形式存在于内核中。
3.3 进程查看与操作
查看进程:
# 方法1:通过/proc文件系统
ls /proc/[pid]
# 方法2:使用ps命令
ps aux | grep process_name
# 方法3:使用top命令(动态)
top
获取进程ID:
#include <stdio.h>
#include <unistd.h>
int main() {
printf("进程ID: %d\n", getpid());
printf("父进程ID: %d\n", getppid());
return 0;
}
3.4 fork:进程创建
设计初衷:Unix系统需要创建新进程执行新任务,fork是最基础的进程创建机制。
核心特性: - fork有两个返回值:父进程返回子进程PID,子进程返回0 - 父子进程代码共享,数据各自私有(写时拷贝)
示例代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
perror("fork失败");
return 1;
}
else if (pid == 0) {
// 子进程
printf("我是子进程,PID: %d\n", getpid());
}
else {
// 父进程
printf("我是父进程,PID: %d,子进程PID: %d\n", getpid(), pid);
}
return 0;
}
输出示例:
我是父进程,PID: 1234,子进程PID: 1235
我是子进程,PID: 1235
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)