一、冯诺依曼体系结构:进程运行的物理基础

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

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐