一、冯诺依曼体系结构
绝大多数计算机都遵守冯诺依曼体系,硬件被划分为五个部件:
输入设备:键盘、鼠标、扫描仪、手写板输出设备:显示器、打印机存储器:即内存CPU:内含运算器 + 控制器
图示 / 数据流动:
规则:
存储器特指内存(外存:磁盘等- 不考虑缓存时,
CPU只能对内存进行读写(无法直接访问外设 - 外设输入/输出数据,也只能通过内存读写
总结:所有设备都只能直接和内存打交道
以 QQ 聊天为例:敲键盘(外设 → 数据进内存 →
CPU从内存取走处理 → 结果写回内存 → 经网卡发出
对方的数据则反向流经内存再送到显示器。
整条链路里,内存始终是中转站。
二、操作系统(Operator System)
操作系统的概念:任何计算机系统都包含一个基本程序集合OS
由两部分构成:
内核:进程管理、内存管理、文件管理、驱动管理外壳:函数库、shell等
设计目的:
- 对下:与硬件交互,
管理所有软硬件资源 - 对上:为用户程序
提供良好的执行环境
核心定位:在整个软硬件架构中,操作系统是一款纯正的搞管理的软件。
理解“管理”:本质只有两步——先描述被管理对象(类/结构体),再组织(数据结构);进程管理就是它的直接套用
系统调用与库函数:操作系统对外表现为一个整体,但会暴露部分接口供上层开发使用,这些接口即系统调用;系统调用功能基础、对使用者要求较高,于是被开发者适度封装成库,方便二次开发。
三、进程与 PCB:概念和常用操作
进程= 内核数据结构(task_struct)+ 自己的程序代码和数据
PCB(process control block):存储进程信息,是进程属性的集合。Linux 的PCB是task_struct,它会被装载到内存中。
task_struct的主要内容:
| 分类 | 含义 |
|---|---|
标识符 | 描述本进程的唯一标识符(PID),区别于其他进程 |
状态 | 任务状态、退出代码、退出信号 |
优先级 | 相对于其他进程的优先级 |
程序计数器 | 即将执行的下一条指令的地址 |
内存指针 | 代码、数据及共享内存块的指针 |
上下文数据 | 进程执行时处理器寄存器中的数据 |
I/O 状态信息 | I/O 请求、分配的设备、使用的文件列表 |
记账信息 | 处理器时间总和、时钟数总和、时间限制等 |
组织方式:双向链表
3.1 查看进程
- 查看进程:
/proc目录(如PID为 1 的进程看/proc/1)、用户级工具ps aux和top - 获取标识符:
getpid()取PID,getppid()取PPID
例子:查看进程/proc目录:
显示两个进程:程序+grep指令top指令:可以看到死循环时一直在运行
例子:获取父子进程的标识符
打印:
3.2 通过系统调用创建进程
fork()有两个返回值,父进程返回子进程PID,子进程返回0,因此常配合if分流;父子进程代码共享、数据各自私有一份(写时拷贝
创建示例:fork测试:
补充要点:
- 子进程的起点是
fork()的调用点 fork()的返回值:告诉父子进程我是谁写时拷贝:初始共享,父/子进程修改时才拷贝(一部分->页
Q&A:
- 为什么fork()返回给父子进程的信息不同?->父 : 子 = 1 : n,父管理子需要获得子的信息,子无需获得
- 如何将返回值给对应进程?->各返回一份给父子进程
- 一个变量如何让if / else if同时成立?->详见后续文章,物理地址&虚拟地址
3.3 补充:cwd与exe
在进程中,cwd是可执行程序的所在路径,exe显示可执行程序的完整路径
运行时,在另一个窗口删除子进程,程序不会被终止,但会报警告:
结论:内存中的运行程序是磁盘中的拷贝
另外,cwd是支持更改的,需要用到chdir函数:
在另一个窗口查看进程信息,显示cwd已被更改。前后对比如下:
四、进程状态
内核源码中的任务状态数组是关键:
| 状态 | 含义 |
|---|---|
R(running) | 运行中,或在运行队列里(不等于正在跑) |
S(sleeping) | 可中断睡眠,等待事件完成 |
D(disk sleep) | 不可中断睡眠,通常在等待 I/O 结束 |
T(stopped) | 收到SIGSTOP而暂停,SIGCONT可继续运行 |
t(tracing stop) | 被调试追踪而暂停 |
X(dead) | 死亡状态,只是返回状态,任务列表中看不到 |
Z(zombie) | 僵尸状态 |
查看:
僵尸进程(Z):子进程退出后,父进程未读取其退出码(如未调用wait()),子进程就以终止状态留在进程表中等待被读取。原因是退出状态本身需要数据来维护,属于进程基本信息,保存在PCB中。
示例:子进程结束后未被回收,进入Z状态
危害:Z状态不退出,PCB就要一直维护;父进程创建大量子进程却不回收,就会造成内存泄漏。
孤儿进程:父进程提前退出、子进程后退出,该子进程被称为孤儿进程,会被 1 号init/systemd进程领养并回收,因此不会造成资源泄漏。
示例: