操作系统进程管理-进程实现原理
进程实现原理
进程是一个程序运行时的实例, 一个程序要运行起来, 需要硬盘, 内存, CPU, 网络等资源, 如果这些部分都有用户手动来管理, 开发一个程序会变成一个极其繁琐和困难的事情, 操作系统针对这些程序运行时需要的资源抽象出来了进程这个概念. 进程持有并统一管理所有一个程序要运行时需要的资源
对于资源的集合, 在概念中被称为PCB(Process Control Block), 而在Linux中对应的内核对象就是task_struct这个数据结构
1 | struct task_struct { |
进程的特性
接下来更进一步地讲解进程中的中的各个特性, 也基本是围绕进程持有的资源展开
进程状态
1 | // 1. 进程的状态 |
通过top命令查看, 其中的S一列就是进程的State
进程的简单的状态转化图
其他的状态
PID和进程树
1 | // 2. 进程的pid |
这里的tgid是实际的进程的PID, 调用getgid()返回的也是tgid, pid在单线程的时候等于tgid, 在多线程的时候, 属于同一个进程的每个线程的tgid都是一样的, pid不同.
通过ps -ef
命令能看到所有正在运行的进程
其中的PID一栏就是该进程的PID, PPID是其父进程的PID (PID是0的进程是systemd进程)
1 | //3. 和进程树的关系 (父进程, 子进程, 兄弟进程) |
1 | #pstree |
进程通过task_struct中的parent和children两个task_struct对象, 将进程按照父子关系组合成了一棵树, 其中所有的进程的最终父进程都是systemd (PID=0)
进程调度
1 | // 4. 进程优先级 |
调度器分成实时调度器和完全公平调度器, 前者常用于内核task, 后者常用于用户态的task, 运行的时间会收到prio的影响, 这一部分内容会在后面详细说明
进程内存地址空间
1 | // 5. 进程地址空间 |
1 | struct mm_struct { |
所有的进程共用相同的内核内存区域, 并且内核内存是通过物理内存直接映射分配的
同时对于内核线程, 它的task_struct中的mm是null, 因为它没有用户态的虚拟地址空间
进程文件系统
1 | // 6. 进程文件系统信息 (当前目录...) |
1 | struct fs_struct { |
不同于接下来要介绍的打开的文件, 这个属性记录的是pwd(当前工作目录), root(根目录)等进程在文件系统的位置
进程文件打开列表
1 | // 7. 进程打开的文件描述符 |
在内核中实际上是以一个数组的形式存在的, 我们大名鼎鼎的socket就是在这个位置存储的
其中前三个打开的文件就是我们的标准输入, 标准输入, 标准错误, 这也是为什么这些std对应的数字是0,1,2
进程的命名空间
这块的内容其实是服务于容器技术的, 通过命名空间来提供可见性, 但是并不保证隔离性. 和CPP中的命名空间的概念差不多, 本质上还是提供可见性上的区别
1 | // 8. namespace |
对于本机非容器环境来说, 就一个固定的命名空间