内核
- 进程调试: Linux 是抢占式多任务操作系统. 多个进程可同时驻留于内存, 每个进程都能获得对 CPU 的使用; 每个进程能使用多长时间, 由内核进程调度程序决定
- 内存管理: 物理内存 RAM 属于有限资源, 内存必须公平, 高效地在进程间共享这一资源. Linux 采用了虚拟内存管理机制, 具有以下两方面的优势:
- 进程与进程之间、进程与内核彼此隔离, 一个进程无法读取或修改内核或其他进程的内存内容
- 只需将进程的一部分保持在内存, 不但降低了每个进程对内存的需求量, 而且还能在 RAM 中同时加载更多的进程
- 文件系统: 内核在磁盘上有文件系统, 允许对文件执行创建, 获取, 更新和删除等操作
- 创建和终止进程: 内核可以将新程序载入内存, 为其提供运行所需的资源, 一个运行中的程序称之为”进程”, 一旦进程执行完毕, 内核要确保释放其占用的资源, 供后续重新使用
- 联网: 内核以用户进程的名义收发网络消息 (数据包). 该任务包括将网络数据包路由至目标系统
内核态(核心态, 监管态)和用户态
- 在用户态下运行时, CPU 只能访问用户空间内存, 试图访问属于内核空间的内存会引发异常
- 运行于核心态时, CPU 既能访问用户空间内存, 也能访问内核空间内存
- 仅当处理器在核心态运行时, 才能执行某些特定操作. 这样的例子包括:
- 执行停机(halt)指令去关闭系统,
- 访问内存管理硬件
- 以及设备 I/O 操作的初始化
- 实现者们利用这一硬件设计, 将操作系统置于内核空间. 确保了用户进程不能访问内核指令和数据结构, 也无法执行不利于系统运行的操作
- 某进程可以请求内核创建另一个进程
Shell
- 用于读取用户输入的命令, 并执行相应的程序以响应. 也叫 “命令解释器”
- 术语登陆 Shell (login shell) 是指用户刚登陆系统时, 由系统创建, 用以运行 Shell 的进程
- Shell 只是一个用户进程. 登入同一台计算机的不同用户同时可使用不同的 Shell
文件 I/O 模型
- UNIX 系统 I/O 模型最显著的特性之一是其 I/O 通用性的概念. 同一套系统调用(open(), read(), write(), close()等) 所执行的 I/O 操作, 可施之于所有文件类型, 包括设备文件在内
- 就本质而言, 内核只提供一种文件类型: 字节流序列, 在处理磁盘文件时, 可通过 lseek() 系统调用来随机访问
命令行参数
- 程序的 main() 函数需要做如下声明: int main (int argc, char* argv[]);
- argc: 命令行参数的总个数
- argv: 指针数组的成员指针则逐一指向每个命令行参数字符串. 首个字符串 argv[0], 标识程序名本身
进程
- 正在执行的程序实例. 执行程序时, 内核会将程序代码载入虚拟内存, 为程序变量分配空间, 建立内核记账数据结构, 以记录与进程有关的各种信息
- 在内核看来, 进程是一个个实体, 内核必须在它们之间共享各种计算机资源
- 对于像内存这样的受限资源来说, 内核一开始会为进程分配一定数量的资源, 并在进程的生命周期内, 统筹该进程和整个系统对资源的需求, 对这一分配进行调整
- 程序终止时,内核会释放所有此类资源,其他进程重新使用
- 其他资源(如CPU、网络带宽等)都属于可再生资源,但必须在所有进程间平等共享
- 逻辑上将一个进程划分为以下几部分(也称为段)
- 文本: 程序的指令
- 数据: 程序使用的静态变量
- 堆: 程序可从该区域动态分配额外内存
- 栈: 随函数调用、返回而增减的一片内存,用于为局部变量和函数调用链接信息分配存储空间
创建进程和执行程序
- 使用系统调用 fork() 来创建一个新进程. 调用 fork() 的进程被称为父进程, 新创建的进程则被称为子进程.
- 内核通过对父进程的复制来创建子进程. 子进程从父进程处继承数据段, 栈段以及堆段的副本后, 可以修改这些内容, 不会影响父进程的 “原版” 内容
- 子进程要么去执行与父进程共享代码段中的另一组不同函数, 或者, 更为常见的情况是使用系统调用 execve() 去加载并执行一个全新程序
- execve() 会销毁现有的文件段、数据段、栈段和堆段,并根据新程序的代码, 创建新段来替换它们
进程 ID 和父进程 ID
- 每一个进程都有一个唯一的整数型进程标识符 PID. 此外, 每一个进程还具有一个父进程标识符(PPID)属性, 用以标识请求内核创建自己的进程
终止进程
- 使用 exit() 系统调用, 或相关的 exit() 库函数
- 向进程传递信号, 将其 kill
- 无论以任何方式退出, 进程都会生成 “终止状态”
init 进程
- 系统引导时, 内核会创建一个名为 init 的特殊进程, 即”所有进程之父”, 该进程的相应程序文件为 /sbin/init
- 系统的所有进程不是由 init (使用 fork())“亲自”创建, 就是由其后代进程创建. init进程的进程号总为1, 且总是以超级用户权限运行
- 谁 (哪怕是超级用户) 都不能”杀死” init 进程, 只有关闭系统才能终止该进程
- init 的主要任务是创建并监控系统运行所需的一系列进程
进程间通信与同步机制
- 信号 (signal), 用来表示事件的发生
- 管道 (即shell中的 “|” 操作符) 和 FIFO, 用于在进程间传递数据
- 套接字 (SOCKET), 供同一台主机或是联网的不同主机所运行的进程之间传递数据
- 文件锁定, 为防止其他进程读取或更新文件内容, 允许某进程对文件的部分区域加以锁定
- 消息队列, 用于在进程间交换消息(数据包)
- 信号量 (semaphore), 用来同步进程动作
- 共享内存, 允许两个及以上进程共享一块内存. 当某进程改变了共享内存的内容时, 其他所有进程会立即了解这一变化
线程
- 每个进程都可执行多个线程, 可将线程想象为共享同一虚拟内存及一干其他属性的进程
- 每个线程都会执行相同的程序代码, 共享同一数据区域和堆 但是, 每个线程都拥有属于自己的栈, 用来装载本地变量和函数调用链接信息
- 线程之间可通过共享的全局变量进行通信
- 借助于线程 API 所提供的条件变量和互斥机制, 进程所属的线程之间得以相互通信同步行为, 尤其是在对共享变量的使用方面
- 此外, 线程间也能彼此通信