ver 0.1
前言
作为一名非著名的虚拟化技术的研究人员,肯定是要写一写vCPU线程调度策略方面的文章。在调研的过程中发现还是要从单系统的调度聊起来比较好,这也是我们很冒昧的写了前文Linux内核简介那篇文章。这里要聊Linux内部的调度机制,主要的思路是从Linux的基础组件开始逐步的深入下去先扫清外围的阵地,然后再向中心主攻。突破外围的阵地的方向之一就是搞清楚在Linux内部调度的对象-进程(任务),以及Linux内核对其内部的进程是管理模式。阅读本文之前还是需要一些背景知识作为铺垫,请大家读一读我们的一些前序文章:
(1)[[V-00] 虚拟化概论-思想篇](https://mp.weixin.qq.com/s?__biz=MzkwNDY5NzM0NA==&mid=2247483660&idx=1&sn=587aa3603240b89eb4a2e5bc80f27a6d&chksm=c0824263f7f5cb75bab76cba9d8a2c02420eba23b971f050ec9fb4a91a6e032023763ff0fef1&scene=178&cur_album_id=3465892385727528966&search_click_id=#rd)
(2)[[LX-00]Linux-内核架构简介(Kernel Architecture Overview)](https://mp.weixin.qq.com/s?__biz=MzkwNDY5NzM0NA==&mid=2247484482&idx=1&sn=4d344e4847c48c3c6b63ae2dccef0a44&chksm=c1968311840935b7e67c68375c87cba3a6371bbd897d2450c0bce928c9fb13de3d24d0dabb0d&mpshare=1&scene=1&srcid=0425xxFb5w9jvCkXXkFDDeV7&sharer_shareinfo=8f79a5ce709413329241262e0ac43607&sharer_shareinfo_first=8f79a5ce709413329241262e0ac43607&exportkey=n_ChQIAhIQxUKMB9CbAeN57bucBVHy0BKfAgIE97dBBAEAAAAAADtlLwDfJaAAAAAOpnltbLcz9gKNyK89dVj0g%2BHCtxYEL%2BSIqVXwIsSX%2BFHC6Okb7uj7GQGHL0ROnEXOibzbtgwkPAhVrW6%2FLCjSpwDtWcB8x7QDR%2F%2BbXyD115SplYTizlK%2Fj%2FdAUv5fs4yVSyVrcl9kUC9V0tVGnPbpnDKyE4UH9iGhewpvVO6pAfeBvwg3Vxu9Sw7YljS%2Fb0M5%2F400yXs4Al2wUbZbG4odXyCeAlGifsMnPVjjiTj%2Flszc1i6wExcU4shHeW6wjwoS%2FlaGbj5mFzd%2FSpWQaLEUOoVOTrDAZvtoPtLpjfa5pUflGZsozs3Na4uqolwLArHpf62qV9VBRf0leWophEv3XARB78%2Bd1%2F4w&acctmode=0&pass_ticket=4S2BLPWpfHAAa1fAw7vefLRuNvrXPoDcf77hf4lYOw0KTaNnpLjz4QtWORIqzvvN&wx_header=0#rd)
(3)[[A-07]ARMv8/ARMv9-Memory-内存管理单元(MMU)](https://mp.weixin.qq.com/s?__biz=MzkwNDY5NzM0NA==&mid=2247483891&idx=1&sn=d3edffed0dd6847814adba4f374e7092&chksm=c082429cf7f5cb8a140fee624ae8fab9b8d1ef87a5885be056e59558d2666c3677f4202a56ba&scene=178&cur_album_id=3521355675752595456&search_click_id=#rd)
(4) [[V-05] 虚拟化基础-异常模型(Exception)(AArch64)](https://mp.weixin.qq.com/s?__biz=MzkwNDY5NzM0NA==&mid=2247483766&idx=1&sn=76f40e40e4fbe2fe2cb38fdb4cb8d62e&chksm=c0824219f7f5cb0f3e78024da9d2ed354e2f636888964c15a34485666174ea44b76a1ade7135&cur_album_id=3465892385727528966&scene=190#rd)
正文
作为一名标准的中登,经过多年的生活磨炼和职场的捶打,对人生多少也有些体会和感悟。这里稍微油腻一下发出人生的终极三问:我是谁? 我从哪儿来?我要到哪儿去?。搞清楚了这三个问题也就搞清楚了自己的身份地位,以及如何在生活和工作中塑造打磨自己的学识、能力、性格,最后是以一种什么样的方式融入这个精彩而又充满诱惑与挑战的世界,最终找到自己心灵、灵魂和肉体的归宿。联系到本文讨论的主题,如果把进程看成计算机世界里的一个生命体。那么它一样可以问自己三个问题:到底是啥是一个进程;这个进程是怎么来的;进程是如何在计算机的世界里运行并找到自己的归宿。
1.1 进程的概念
按照我们一直以来的习惯,首先要在一个宏观的视角下看一下要介绍的内容,比如图1-1中内核架构中的Task。

图1-1 Linux Kernel ARCH Overview
支撑浩瀚的Kernel代码是一套复杂而又严密的架构,经过了高度的抽象和简化留下来的Kernel架构中能够留下来的模块肯定是重中之重。Tasks就是这些重要的模块中的一份子,请注意它是复数的形式,说明Linux的内核中有好多个Task。本文要讨论的进程在内核中就会被映射成一个又一个的Task接受管理。搞清楚这个事实和背景之后,让我们看一下进程的概念:
A process is a program (object code stored on some media) in the midst of execution. Processes are, however, more than just the executing program code (often called the text section in Unix).They also include a set of resources such as open files and pending signals, internal kernel data, processor state, a memory address space with one or more memory mappings, one or more threads of execution, and a data section containing global variables. Processes, in effect, are the living result of running program code.The kernel needs to manage all these details efficiently and transparently.
A program itself is not a process; a process is an active program and related resources. Indeed, two or more processes can exist that are executing the same program. In fact, two or more processes can exist that share various resources, such as open files or an address space.
这里我们引用了LKD这本经典的研究内核原理的书籍中的一段原文来描述进程的概念:
(1) 首先进程必须要和程序做一个区分,程序是以一定的组织形式存放在一定媒介(ROM\Disk等)上的二进制文件,进程是程序中的代码在PU上执行起来的一个形态。可以说程序中的那些架构和码农构建起的功能就是进程的灵魂,进程就是这个灵魂的有机生命体。
这里我们稍微展开一下程序的概念,虽然不是我们研究的重点,为了为了文章的完整性,还是决定花费一点笔墨。程序就是有组织的二进制文件,组织是有标准可寻的,Linux遵循的就是LEF。ELF 的全称是 Executable and Linking Format,即“可执⾏可连接格式”,就是我们常说⼆进制程序。ELF的规范中规定了它所支持的二进制文件的类型,如图1-2所示:

图1-2 ELF文件类型
ELF 文件主要的三种类型:
• 可重定位文件(relocatable file),用于与其它目标文件进行连接以构建可执行文件或动态链接库(.o)。
• 共享目标文件(shared object file),即动态连接库文件(.so)。
• 可执行文件(executable file),经过连接的,可以执行的程序文件(.bin)。
不同的CPU类型可以根据ELF文件头中的Machine配置配合其他字段的配置后被内核打开后布局在内存中然后在对应的平台上(ARM\X86\Risc-V等)跑起来。这些二进制文件是分段形式组织内容存放在磁盘上,如图1-3所示:

图1-3 ELF文件格式
其中主要的段类型为:
.bss 节主要保存了未初始化的变量,这⾥的未初始化起始也包括初始化为 0 的变量。
.text 节实际就是我们说的代码段,保存了⼀系列的可执⾏⼆进制指令。
.data 节主要保存了已经初始化的变量,通常是指⽤来存放程序中已初始化的全局变量和已初始化的静态变量的⼀块内存区域。
(2) 前面一个小节,我们搞清楚一个事实就行,程序不等于进程,因为程序就是为了特定任务的一个有序指令集合或者一个可执行文件,包含可运行的 CPU 指令和相应的数据等信息,它不具有 “ 生命力 ”。进程是一段执行中的程序,是一个有 “ 生命力 ” 的个体,它必须是活着的,而承载这个身体的肉身就是内存,如图1-4所示:

图1-4 ARM体系下的虚拟地址空间与物理地址空间映射
以ARM为例,Kernel让每一个进程都活在自己的世界里(即便是内核也一样,在虚拟化的架构下内核自己也是活在Hypervisor的控制的世界中),这个世界就是ARM体系在内核的配合下构建的虚拟地址空间,这里是进程肉身的行动之地。
(3) 我们给进程找到了灵魂也找到了灵魂的栖息之地,下面看看进程的内部还有些什么元素,进程本身还是一个资源的集合,这些资源都是进程在执行任务的时候所要消耗的必要条件,总要给人家一个名分。这些资源包括打开的文件、关心的信号、处理器的状态、存贮运行中数据的堆栈等等,但是这些资源也是进程的一部分,是内核帮助进程工作和管理进程的重要抓手。
(4)最后一点就是要思考一下,Kernel这么设计出进程这个概念的目的:并发。
On modern operating systems, processes provide two virtualizations: a virtualized processor and virtual memory.The virtual processor gives the process the illusion that it alone monopolizes the system, despite possibly sharing the processor among hundreds of other processes.
The kernel and the processor create the illusion of multitasking — the ability to perform several operations in parallel — by switching repeatedly between the different applications running on the system at very rapid intervals. Because the switching intervals are so short, users do not notice the intervening brief periods of inactivity and gain the impression that the computer is actually doing several things at once.
This kind of system management gives rise to several issues that the kernel must resolve, the most important of which are listed below.
❑ Applications must not interfere with each other unless this is expressly desired.
❑ CPU time must be shared as fairly as possible between the various applications, whereby some programs are regarded as more important than others.
Kernel作为硬件资源(CPU\内存\Disk\aDSP\GPU\NPU\Modem....)的直接控制者,需要做的就是把资源相对公平的分配给上层的任务使用,以达到用户并发使用硬件的效果。这些任务在内核中就会被抽象成一个又一个进程,他们相互独立,被公平的分配时间去使用硬件资源。所以进程是用于实现多进程并发执行的一个实体,实现对 CPU (核心硬件)的虚拟化,让每个进程都认为自己独立拥有一个 CPU 。实现这个 CPU 虚拟化的核心技术是上下文切换( context switch )以及进程调度( schedule )。进程(线程)是操作系统分配内存、 CPU 时间片等资源的基本单位。具体到我们研究的Linux系统,进程是分配内存的基本单位,而线程是分配CPU的基本单位。
我们不是讨论操作系统的理论,文绉绉的讨论进程的概念。搞清楚它和程序的区别,搞清楚Linux设计这样一个组件的目的作为我们进一步研究的背景足矣。
1.2 核心数据结构(task_struct)
前面讨论了进程的基本概念,我们清楚操作系统为了让真实用户的任务(听歌、打字、收发邮件...)等并发的在一个硬件平台上跑起来,将这些任务做了隔离与资源使用时长的划分抽象出了进程的概念。而进程在Linux的内核中这些任务(进程)通过一个账本集中管理,这个账本的每一页都是是一个数据非常大的数据结构(800多行代码)-task_struct。如图1-5所示:

图1-5 The process descriptor and task list
通过上图可以清晰的看出进程和task_struct的逻辑关系:
❑ 用户进程和task_struct是一一对应关系。
❑ 用户进程中的线程和task_struct是一一对应关系。
❑ 内核中的线程和task_struct是一一对应关系。
需要注意的是在内核中,进程的组织形式并非只有上面一种,因为这种双向链表虽然能遍历所有的task,但是效率不高不能满足全场景的需要。因此在内核中会根据需要在多个维度将这些task组成其他的一些形式,我们尝试的列举几个经典的进程link在一起的场景:
(1) 在需要调度的上下文,为了能够快速的找到下一个可执行的task,就需要把所有可立即投入执行状态的进程组织在一起,如图1-6所示:

图1-6 task的红黑树组织形式
(2) 家庭式的组织形式,目的是为了进程的创建与消亡等场景的工作的开展,如图1-7所示:

图1-7 Family relationships between processes
(3) 进程需要等待资源可用的场景,内核也提供了等待队列的形式将有相同诉求的进程组织在一起:
Wait queues are used to enable processes to wait for a particular event to occur without the need for constant polling. Processes sleep during wait time and are woken up automatically by the kernel when the event takes place.
(4) 命名空间的形式(Namespaces)将相关的进程组织在一起,这个我们会在后面介绍进程标识(PID)的时候会展开说一下。
Namespaces provide a lightweight form of virtualization by allowing us to view the global properties of a running system under different aspects.
内核不管从何种维度去组织Linux系统中的进程,都直接或者间接的和task_struct做关联,因为它承载着一个进程在内核中生存的所有信息。具体的我们可以把task_struct 数据结构包含的内容可以简单归纳成如下几类:
(1) 进程属性相关信息
❑ 例如进程的标识符、进程的状态、进程的名称、进程相关的一些Flags、退出码、退出信号等。
(2) 进程间的关系
❑ 主要是父进程、子进程、兄弟进程、进程组长等信息。
(3) 进程调度相关信息
❑ 当前进程的优先级、静态优先级、Normal优先级、实时优先级、调度类信息等。
(4) 内存管理文件管理相关信息
❑ 进程使用的一切内存相关的信息,例如堆、栈、代码段、数据段等相关的信息。
(5) 文件管理相关信息
❑ 保存当和前进程相关联的所有文件信息。
(6) 信号相关信息。
❑ 主要用于管理进程的信号处理函数、阻塞信号集和挂起信号队列。
上面我们列出来task_struct直接管理的一些信息,实际上通过其内的成员变量间接关联信息和详细的信息更多。我们对进程的介绍也主要围绕着这些信息展开,面面俱到不可能,尽量做到把核心的方面给讲清楚,就比如我们心心念念的进程调度。
1.3 进程的生命周期
这个小节我们来介绍下进程的状态,在长期的解析Bug的生涯中,我们养成了“Log不够,状态来凑”的好习惯,哈哈。进程从出生到消亡要经历不同的状态阶段,如图1-8所示:

图1-8 Flow chart of process states(Lifecycle)
通常情况下进程是需要一直执行的,但是现实中当一个进程需要等待某个资源或者指令的时候,它是需要耐住性子等待的。这里我们直接引用手册中对进程各个状态的介绍:
TASK_RUNNING —The process is runnable; it is either currently running or on a runqueue waiting to run .This is the only possible state for a process executing in user-space; it can also apply to a process in kernel-space that is actively running.
TASK_INTERRUPTIBLE —The process is sleeping (that is, it is blocked), waiting for some condition to exist.When this condition exists, the kernel sets the process’s state to TASK_RUNNING .The process also awakes prematurely and becomes runnable if it receives a signal.
TASK_UNINTERRUPTIBLE —This state is identical to TASK_INTERRUPTIBLE except that it does not wake up and become runnable if it receives a signal.This is used in situations where the process must wait without interruption or when the event is expected to occur quite quickly. Because the task does not respond to signals in this state, TASK_UNINTERRUPTIBLE is less often used than TASK_INTERRUPTIBLE .
__TASK_TRACED —The process is being traced by another process, such as a debugger, via ptrace.
__TASK_STOPPED —Process execution has stopped; the task is not running nor is it eligible to run.This occurs if the task receives the SIGSTOP , SIGTSTP , SIGTTIN , or SIGTTOU signal or if it receives any signal while it is being debugged.
都是很经典的状态了,大家自行阅读或者大模型介绍应该都能够理解,这里我们贴出几个状态之间迁移的条件,如图1-9所示:

图1-9 进程的状态迁移
从上面的状态迁移中可以看出,进程从Running状态让出CPU其实有主动和被动两种情况:
(1) 主动的情况就是进程自己的下一条指令执行的条件不满足了,就比如等待某些资源等,会主动通知内核重新调度一下选择一个新的task在当前CPU上执行。
(2) 被动的情况,我们可以理解为自身还想继续使用CPU但是因为有其他的条件触发了自己不得不让出CPU,这种情况可以称之为被抢占。比如一个优先级更高的进程变成running状态或者调度器分配给当前进程的时间片已经耗尽等等。这里需要说明的是,因为有了抢占机制的存在才能让操作系统实现多任务系统和良好交互体验。
抢占这么重要,来简单的聊聊抢占的工作原理,分为两个阶段:
(1) 首先是标记阶段:
当内核判断当前进程应该被抢占时(如时间片耗尽、有更高优先级任务唤醒),就会给当前进程设置此标志(thread_info:TIF_NEED_RESCHED)。 相当于给系统“提出请求”,告诉系统“未来某个安全时刻需要执行调度”。
(2) 第二个阶段就是内核要找一个合适的时机点把当前task调度出去,选择下一个task执行,这个就稍微复杂一些:
User Preemption
User preemption occurs when the kernel is about to return to user-space, need_resched is set, and therefore, the scheduler is invoked. If the kernel is returning to user-space, it knows it is in a safe quiescent state. In other words, if it is safe to continue executing the current task, it is also safe to pick a new task to execute.
In short, user preemption can occur
❑ When returning to user-space from a system call
❑ When returning to user-space from an interrupt handler
Kernel Preemption
It is now possible to preempt a task at any point, so long as the kernel is in a state in which it is safe to reschedule.
Kernel preemption can occur
❑ When an interrupt handler exits, before returning to kernel-space
❑ When kernel code becomes preemptible again
❑ If a task in the kernel explicitly calls schedule()
❑ If a task in the kernel blocks (which results in a call to schedule() )
复杂的原因是为了安全,不能因为抢占印象系统所承载的功能和稳定性,尤其是在多处理器单元(SMP)的CPU上,内核执行抢占策略之前需要检查的状态除了标注位之外还要检查抢占锁(thread_info:preempt_count)的状态是否运行。抢占又分为用户空间抢占和内核抢占(2.6之后支持抢占式内核),我们看下它们的抢占安全点,也可以分成两种情况:
一种是自己不争气,在内核中主动调用了schedule()(被block和使能抢占也都属于此种类型)。第二种类型就是要依赖体系的力量了,比如在ARM架构下那就是异常处理返回时,如图1-10所示:

图1-10 ARM异常处理流程
ARM的异常分为同步异常和异步异常,如上面描述的系统调用就是同步异常,而中断就属于异步异常。不论是用户空间的抢占还是内核抢占,在异常的返回的时候都会按照我们上面描述的流程检查抢占标志位和抢占锁是否满足抢占的要求,如果满足那么内核就会执行抢占,选择下一个task执行,那么伴随着这个过程,当前task的状态也会随之发生变化。
1.4 进程的上下文(Context)
通过前面的描述,我们知道进程本身就是一个运行态的概念,内核把进程在运行时的所有信息都记录在一个task_struct中,我们可以把这些信息称之为进程的上下文,不过这个上下文是一个广义的上下文。这些信息都存贮在内存中,即便是缓存在处理的各级Cache中,当CPU去执行其他进程的时候,task_struct中记录的信息还在。而有一些信息就比较危险了,那就是CPU上的寄存器中保存的当前执行进程的一些状态就危险了,如图1-11所示的ARM架构下通用寄存器:

图1-11 ARM通用寄存器
除了通用寄存之外还有特殊类型的寄存器,如图1-12所示:

图1-12 AArch64 special registers
当然大家要注意权限的区别,以ARM为例,不同异常等级下要保存的内容也不一样主要是特殊寄存器也不同。当进程陷入内核或者进程被抢占,都涉及到对当前CPU上寄存器状态的保存,保存的目的是为了能够在该进程重新获取CPU的时候能够迅速恢复之前被抢占的状态,继续执行。这个保存和恢复的过程也通常被称为切换进程的上下文。
用户态寄存器一般保存在 内核栈 的 pt_regs 中,如图1-13所示:

图1-13 pt_regs
内核态寄存器一般保存在 task_struct->thread 中,如图1-14所示:

图1-14 cpu_context
结语
我们从一个中年油腻的中登开始讲起了进程的概念,特别指出了进程与程序的联系与区别。然后讲述了内核中用于记录进程执行所关联的所有信息的核心数据结构task_struct,并对这个数据结构中记录的信息进行了归类。后半段我们讲述了进程的状态,以及促使状态变化中最重要的条件之一抢占的一些概念。最后我们介绍了进程的上下文的一些概念,讲述了内存上下文和CPU硬件上下文区别。这些概念都是我们继续研究内核工作原理和内核其他组件的基石。虽然我们不是专业的计算机科学理论专家,但是还是希望大家能够深了的理解这些基本概念的精髓。当然围绕着Kernel中的进程管理相关的概念还有很多,后面会慢慢的逐渐讲解。今天就到这里,希望大家点赞、评论、转发,谢谢。
Reference
[01]<LKD/79-LX-LK-LKD-en-3rd.pdf>
[02]<79-LX-LK-m0001-Professional_Linux_Kernel_Architecture_EN_3rd.pdf>
[03]<79-LX-LK-m0001-深入Linux内核架构-EN.pdf>
[04]<79-LX-LK-O0001-Understanding_the_Linux_Kernel_EN_3rd.pdf>
[05]<LKD/79-LX-LK-xl0001_图解Linux系统.pdf>
[06]<79-LX-LK-z0002_奔跑吧Linux内核-V-2-卷1_基础架构.pdf>
[07]<LKD/80-LX-LK-OVW-yk0001_一张图看尽Linux内核运行原理.pdf>
[08]<80-LX-LK-OVW-yk0002_一文看懂Linux内核.pdf>
[09]<79-LK-L0001_深入分析Linux内核源代码.pdf>
[10]<BlackBerry QNX OS & Virtualization Technical Overview.pdf>
[11]<79-LX-LD-c0001-深入Linux设备驱动程序内核机制.pdf>
[12]<LDD/79-LX-LD-LDD-EN-3rd.pdf>
[13]<79-LX-LD-s003-Linux设备驱动开发详解4_0内核-3rd.pdf>
[14]<80-LX-LD-DTS-xy0001_设备树语法.pdf>
[15]<80-LX-LD-wx0002_Linux设备模型之总线bus.pdf>
[16]<80-OS-OVW-wx0001_75页PPT-彻底看懂操作系统.pdf>
[17]<80-OS-OVW-cl0001_操作系统导论.pdf>
[18]<QNX-Neutrino-Product-Brief-v7.pdf>
[19]<79-LX-LK-m0002-Professional_Linux_Kernel_Architecture_CN_3rd.pdf>
[20]<80-LX-ProcM-yk0001_Linux进程是如何创建出来的.pdf>
[21]<79-LX-ELF-z0001_Understanding_ELF.pdf>
Glossary
dts - device tree source
dtc - device tree compiler
dtb - device tree blob
dtsi - device tree source includefile
TLB - Translation lookaside buffer(地址变换高速缓存)
ACPI - Advanced Configuration and Power Interface
APM - Advanced Power Manager
FDT - Flatted Device Tree 扁平设备树
UFS - Universal flash storage 通用闪存标准
LUN - Logical Unit 逻辑单元,UFS中使用的术语
ISR - Interrupt Service Routines
IRQ - Interrupt ReQuest
NUMA - Non Uniform Memory Access
PCI - The Peripheral Component Interconnect
AHB - Advanced High performance Bus,高级高性能总线
APB - Advanced Peripheral Bus,这是一种外围总线.
jiffies - 一会儿; 瞬间
OF - OpenFirmware
GRUB - GRand Unified Bootloader
BIOS - Basic Input Output System
POST - Power-OnSelf-Test硬件⾃检(Power-OnSelf-Test)”,简称POST
MBR - Master Boot Record
AMP - Aymmetrical Multi-Processing ⾮对称多处理器
SMP - Symmetrical Multi-Processing 对称多处理器
HMP - Heterogeneous Multi-Processing 异构多处理器
CFS - Completely Fair Scheduler 完全公平调度
IPI - Inter-Processor Interrupt 处理器间中断
PIC - Programmable Interrupt Controller 中断控制器
APIC - Advanced PIC 高级中断控制器
RCU - Read Copy Update 读复制更新
GIC - Generic Interrupt Controller
PGD - Page Global Directory
VMA - virtual memory area
GKI - Generic Kernel Image
KMI - Kernel Module Interface
UMA - Uniform Memory Access 一致性内存访问
NUMA - Non-Uniform Memory Access 非一致性内存访问
pfn - page frame number
PSCI - Power State Coordination Interface
DRM - Direct Rendering Manager
OF - OpenFirmware
PMIC - Power Management IC 电源管理芯片
BSP - Board Support Package(Board Support Processor)
POSIX - Portable Operating System Interface
EAS - Energy Aware Scheduling
IPA - Intelligent Power Allocation
PCSA - power control system architecture
VIP - Vehicle Interface Processor(VIP)
AP - Application Processor(AP)
GPIO - General Purpose I/O(GPIO)
ASIC - Application Specific Integrated Circuit(专用集成电路)
APA - AutoParkingAssist 自动泊车辅助系统
LRU - Least Recently Used 最近最少使用
DMB - Data Memory Barrier
POSIX - Portable Operating System Interface of UNIX
IEEE - Institute of Electrical and Electronics Engineers
ext2 - Linux second Extended file system
MBR - Master Boot Record 主引导记录
GDT - Group Descriptor Table
ELF - Executable and Linkable Format,可执⾏与可链接格式
LXC - Linux Containers
SATA - Serial ATA
SAS - Serial Attached SCSI
SCSI - Small Computer System Interface
NVMe - Non-Volatile Memory Express
SSD - Solid-state disk
DVFS - Dynamic Voltage Frequency Scaling 动态电压频率调整
LDO - Low Dropout Regulator 低压差线性稳压器
OPP - Operating Performance Point
MLME - MAC sublayer management entity
PLME - PHY sublayer management entity
DSAP - DestinationServiceAccessPoint 服务访问点
SSAP - SourceServiceAccessPoint 源服务访问点
vDSO - virtual dynamic shared object
COW - Copy-on-write
NPTL - Native POSIX Threads Library