进程

虚拟内存与进程地址空间

为了方便描述模型,以32位Linux 为例,每个进程有3G的独立的地址空间,高位1G 的地址空间 – 内核空间是所有进程共享的,每个进程本身的描述符就是放在内核空间中,并且内核空间又为每个进程都分配了一个独立的内核栈,当然每个进程在自己独立的用户空间中还有另一个栈 – 用户栈,用户栈和内核栈是交替执行的,进程通过系统调用陷入内核态,此时栈指针指向内核栈,从系统调用返回时切换到用户栈。区分内核栈和用户栈的目的是防止用户空间的代码随意访问内核数据结构,对内核数据结构的访问都托管于系统调用,内核代码的执行是受CPU的 ring 机制保护的,这样就能保证系统的安全性,比如应用程序就无法访问直接硬件 IO端口地址

有一种特殊的特权进程没有用户态,一直以内核态运行在内核空间,叫做内核线程(kernel thread)。这里要注意国内很多书籍和网上的资料不知所,把kernel-level thread 也翻译成内核线程造成混淆,所以这里引用 quora 的一个回答来区分这两个完全不同的概念:

The term “kernel thread” normally refers to a thread that executes entirely in kernel mode. technically, a kernel thread’s page table has no user mode pages mapped in. The term “kernel level thread” has been used to refer to an implementation of (user) threads using support from the kernel.

进程状态

TASK_RUNNING

TASK_INTERRUPTIBLE

TASK_UNINTERRUPTIBLE

TASK_STOPPED 

TASK_TRACED

进程切换

触发:时钟中断、IO 中断、缺页异常、陷阱、系统调用均有可能触发进程切换

进程调度时机

1.当前进程(正在CPU上运行的进程)状态变为非可执行状态时会触发调度。
进程执行系统调用主动变为非可执行状态。比如执行nanosleep进入睡眠、执行exit退出、等等;
进程请求的资源得不到满足而被迫进入睡眠状态。比如执行read系统调用时,磁盘高速缓存里没有所需要的数据,从而睡眠等待磁盘IO;
进程响应信号而变为非可执行状态。比如响应SIGSTOP进入暂停状态、响应SIGKILL退出、等等;
2.进程运行时,非预期地被剥夺CPU的使用权(抢占)。这又分两种情况:进程用完了时间片、或出现了优先级更高的进程。
优先级更高的进程受正在CPU上运行的进程的影响而被唤醒。如发送信号主动唤醒,或因为释放互斥对象(如释放锁)而被唤醒;
内核在响应时钟中断的过程中,发现当前进程的时间片用完;
内核在响应中断的过程中,发现优先级更高的进程所等待的外部资源的变为可用,从而将其唤醒。比如CPU收到网卡中断,内核处理该中断,发现某个socket可读,于是唤醒正在等待读这个socket的进程;再比如内核在处理时钟中断的过程中,触发了定时器,从而唤醒对应的正在nanosleep系统调用中睡眠的进程;