# 从Kernel的角度谈进程
在前面学习了内存与系统的相关知识,这些是系统提供的环境,与我们平时编写的程序貌似也没有啥关系。今天要分享的进程就与我们程序员平时相关了。
## **进程 or 程序**
这里先区分“进程”与“程序”两个概念。程序是我们编写的代码经过编译生成的二进制可执行文件,而“进程”是执行中的一个程序。有别于存储在硬盘中的文件。
有了进程的概念后,进程并发执行也就便于解释了。当然,目前的进程并行仍然是“伪命题”,实际是分时复用。
## **进程描述符**
进程是操作系统中调试的一个实体,需要对进程所拥有的资源进行抽象。这个抽象模块称为进程模块Process Control Block, PCB。在Linux内核中采用一个名为task_struct的结构体(定义在include/linux/sched.h头文件中)。
附源代码如下:
```c
struct task_struct {
volatile long state; // 进程状态
void *stack; // 进程的堆栈指针
atomic_t usage; // 引用计数器
unsigned int flags; // 进程标志位
unsigned int ptrace; // 进程追踪
int links; // 链表链接
int mm; // 进程的内存描述符
struct mm_str str; // 内存描述符的字符串表示
struct list_head thread_group;// 线程组链表
struct task_struct *children; // 子进程链表
struct task_struct *parent; // 父进程链表
int child_ COLOR_SGI; // 子进程的链表颜色
void *security_state; // 安全状态
void *files; // 文件描述符表
void *fs; // 文件系统信息
void *subscribers; // 订阅者链表
void * namespaces; // 命名空间链表
/* 更多字段... */
};
```
我查看了一下源代码,这个结构体有362行。一行一行读下来,肯定忘得比记得多。于是我们将数据结构中成果变量归纳如下几类:
- 进程属性相关信息,如state, pid, flags等成员
- 进程间的关系,如real_parent, children, sibling等成员
- 进程调试相关信息,如prio, static_prio, sched_class等成员
- 内存管理相关信息,如mm成员,指向mm_struct结构体
- 文件管理相关信息,如fs, files成员,
- 信号相关信息
- 资源限制相关信息
## **进程生命周期**
典型的操作系统中进程状态如下图所示,即包含创建态,就绪态,运行态,阻塞态,终止态
Linux内核也为进程定义了5种状态,如下图所示:
## **Linux内核进程O(1)调试算法**
在Linux 2.16内核中采用了RedHat公司Ingo Molnar设计的O(1)调试算法,其核心思想仍是Corbato等人提出的多级反馈队列算法。每个CPU各自维护一个属于自己的就绪队列,这样减少了锁的竞争。
就绪队列由两个优先级数组组成,即活跃(active)组和过期(expired)组。每个优先级数组包含MAX_PRIO(140)个优先级队列,其中前100个对应实时进程,后40个对应普通进程。使用位图来判断给定优先级队列中是否有可运行的进程。于是,选择下一个被调度进程的时间变成了查询位图操作,而且和系统中就绪进程数量不相关,时间复杂度为O(1),这也是其名字的得来。
## **总结**
进程的概念,进程的状态轮转图是非常基础的概念,也是技术面试时常考的问题。通过本章的学习,我觉得我对进程的概念的了解还非常少,比如,进程的组织形式是链表,子进程被clone后的写时复制技术,红黑树等技术点等等。
有目标,看到差距,继续努力!