主要了解进程和线程在linux中是如何进行描述和管理的。
进程/线程
进程是处于执行期的程序。但进程不仅仅是一段可执行的程序代码。通常还要包含其它资源,像打开的文件,挂起的信号,映射的内存地址空间,处理器状态等。进程就是处于执行时期的程序以及相关资源的总称,也就是资源分配的基本单位。
线程是进程中活动的对象。每个线程都拥有一个独立的程序计数器、进程栈和一组进程寄存器。内核调度的对象是线程,而不是进程。在linux里没有特别区分线程和进程。对于linux而言,线程不过是一种特殊的进程(和其它进程共享某些资源的进程)。
进程控制块(PCB, process control block)
操作系统管理控制进程运行所用的信息集合。操作系统用PCB来描述进程的基本情况以及运行变化的过程。每个进程都在操作系统中有一个对应的PCB。进程的组织和管理都是通过对PCB的组织管理实现的。
在linux是用task_struct这个结构来描述PCB的。在linux系统中是通过预先分配和重复使用task_struct,这样可以避免动态分配和释放所带来的资源消耗,让进程创建的更迅速。因为task_struct还是比较大的,32位机器上,大约有1.7KB。
PCB包含什么信息,如何组织?
PCB包含的数据能完整地描述一个正在执行的程序:它的标识信息、进程的地址空间、挂起的信号、打开的文件、进程的状态以及其它更多的信息(/proc/$pid)。

PCB的组织会用到很多数据结构,因为应用场景不同,所以需要使用不同的数据结构。调度算法中会将PCB挂在链表上,不同状态的进程形成不同的链表(就绪链表、阻塞链表);父子进程的关系用树来描述(可用pstree查看),CFS调度算法会用到红黑树;通过pid查找进程则是用hash表的结构。
进程个数是否有上限?
内核通过一个唯一的进程标识值(PID)来标识每个进程。为了与老版本的UNIx和linux兼容,PID的最大值默认设置为32768(short int最大值)。若不考虑老式系统兼容,可以修改 /proc/sys/kernel/pid_max 来提高上限。linux系统里面,每个用户的pid也是有限的,可以通过 ulimit -a 查看。
进程状态
操作系统包括实时系统对应进程一般都有 3 个状态,进程在有 CPU 时对应运行态,无 CPU 时对应就绪态和睡眠态。就绪态指所有资源都准备好,只要有 CPU 就可以运行了。睡眠指有资源还未准备好,比如读串口数据时,数 据还未发送。此时有 CPU 也无法运行,需要等资源准备好后变成就绪态,然后得到 CPU 后才能变成运行态,其转换关系下图所示。

linux状态进程转换图:

TASK_RUNNING(运行):正在运行或者在就绪队列中等待的进程。
TASK_INTERRUPTIBLE(可中断):可中断阻塞状态,除等待资源到位外还可以被其它进行的信号唤醒。
TASK_UNINTERRUPTIBLE(不可中断):不可中断阻塞状态,只能被等待资源到位唤醒。
TASK_STOPPED(停止):暂停状态,这种状态发生在收到SIGSTOP、SIGTSTP、SIGTTIN、SIGTTOU等信号的时候。此外,在调试期间接收到任何信号,都会使进程进入这种状态。
TASK_ZOMBIE(僵尸):僵尸状态,表示进程结束但未消亡的一种状态。该状态下除了PCB资源未释放,不占用其它资源(一般是子进程退出,父进程执行waitpid获取子进程死亡原因之前的子进程状态)。
以上几种状态,通常对应的PS命令下的stat是:TASK_RUNNING(R)、TASK_INTER(S)、TASK_UNINTER(D)、TASK_ZOMBIE(Z)、TASK_STOP(T)。
暂停状态是进程在运行过程中,通过外部强制让进程进入的状态。通过这种方法可以指定进程的 CPU 占用率。使用ctrl+z(暂停), fg/bg(前/后台继续),cpulimit(控制cpu占用率)等命令控制。
就绪和执行在linux中都是用TASK_RUNNING 标记,进程的调度算法只关注就绪和执行这两个状态的切换。
深度睡眠和浅度睡眠区别,深度睡眠只有资源到位才醒,收到信号也不醒,浅度睡眠资源到位或收到信号都会醒。
睡眠和暂停区别,睡眠是代码中未得到资源主动进入的状态,暂停是程序外部强制进程进入的状态。