3.1 术语

        介绍了本章用到的一些概念,比如操作系统、内核、进程等。

3.2 背景

        本章描述了通用的操作系统和内核概念,有助于读者对各类操作系统的实现原理有个基本的理解。

3.2.1 内核

        操作系统,其实就是在硬件设备和应用软件之间的传令官和翻译官,它将对磁盘、CPU、内存等硬件设备的操作指令封装成用户可以直接调用的系统接口,而内核就是操作系统基于硬件的第一层软件扩展。

                ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        linux大神Brendan Gregg 性能之巅 第二版(systems performance)阅读心得(第三章 操作系统)-LMLPHP

        内核之上的系统调用和系统库为用户提供了简单的调用接口,比如linux中的 lib,lib64路径下的库文件。在实际生产运维过程中经常会遇到运维工程师误删这两个路径下的库文件导致的故障,甚至会导致系统命令无法执行,系统无法启动。还有强制安装rpm包,导致安装的库文件与系统其他库文件版本不兼容的异常。

        内核的执行

        执行频繁的IO操作,比如磁盘读写、网络交互,主要在内核中进行,主要与硬件设备打交道;而计算密集型的负载通常在用户态运行。但内核很多情况下也会影响,最明显的是CPU调度场景。

3.2.2 内核态和用户态

        内核态和用户态是通过CPU的特权环来控制的,所谓运行态的切换也是CPU安全级别的切换。特权环可以理解为CPU不同的权限级别,分为0,1,2,3 四个级别,实际只用到两个或三个,权限级别从0到3依次降低。等级越高内操控越底层的设备,用户程序运行在用户态,它想往磁盘里写一条数据,需要CPU切换到内核态再执行,这就涉及到CPU的上下文切换。需要先保存用户态的现场,再切换到内核态,在内核态执行完需要的操作后再切换回用户态,并恢复之前保存的现场。

        因为频繁的上下文切换会增加CPU开销,所以对于IO频繁的业务系统不能只关注磁盘性能,往往CPU也会出现瓶颈。

        相对应的,在内存中也区分了用户空间和内核空间,CPU只有在内核态时才有权限对内核空间的内存进行操作。

3.2.3 系统调用

        用户程序执行一些内核态的特权指令是通过系统调用来传递的,可用的系统调用有几百个,常用的有read、write、open、fork等,这些调用尽可能简单,更为复杂的接口作为系统库构建在用户空间中。操作系统通常包含C语言的标准库,其为许多常见的系统调用提供更容易使用的接口(libc、glibc库)。

        下面介绍了几个常用但比较复杂的系统调用:

        ioctl(2):用于设备输入输出操作的系统调用,执行的功能取决于传入的请求码,可以操作的设备包括网络套接字、文件、接口、ARP、路由、流,在linux中,所有的要素都被视为文件,并且分配了唯一的标识fd。

        mmap(2):将可执行文件和库文件以及内存映射文件映射到进程的地址空间。

        futex(2):处理用户空间的锁。

3.2.4 中断

        中断是向处理器发出的信号,即当发生了一些高优先级需要马上处理的事件时,要中断处理器当前的执行来实施处理。要处理中断,处理器需要进去内核态。保存当前执行的线程,运行一个中断服务例程(ISR)来处理该事件。

        有外部硬件产生的异步中断和软件指令产生的同步中断。

        异步中断(硬件设备主动发出中断信号):

        磁盘设备发出磁盘I/O完成的信号

        硬件显示有故障情况

        网络接口发出有数据包到达的信号

        外接设备输入:键盘、鼠标

        同步中断(软件指令主动中断):

        自陷:主动调用内核,例如通过int(中断)指令

        异常:执行错误指令,比如除0

        故障:内存缺页故障(内存中没有需要的数据,要从磁盘加载)

        中断线程ISR

        用于快速处理中断.

        中断屏蔽:内核可以通过设置CPU的中断屏蔽寄存器来暂时屏蔽中断。(时间尽可能要短),一些高优先级的事件可以被实现为不可屏蔽中断(NMI)。

3.2.5 时钟和空闲

        计时器中断:每秒执行次数。功能:更新系统时间,线程调度时间片,维护CPU统计数据,以及执行内核调度例程。

        空闲线程:CPU没有工作可做时,内核会安排一个占位线程。

3.2.6 进程

        进程保存了用户程序执行的环境信息,包含内存地址空间、文件描述符、线程栈和寄存器。PID唯一标识一个进程。一个进程包含一个或多个线程,同一个进程的线程共享进程的地址空间及文件描述符。

        线程包括栈、寄存器以及指令指针(程序计数器),多线程让单一进程可以在多个CPU上并发执行。

        内核启动的第一个进程是 init,在/sbin/init,PID为1,用于启动用户空间。其他的进程都是fork该进程。

        进程的创建

        进程的创建通常使用fork(2)和clone(2)创建一个进程的副本,然后再调用exec(2)来开始执行一个不同的程序。

linux大神Brendan Gregg 性能之巅 第二版(systems performance)阅读心得(第三章 操作系统)-LMLPHP

        fork和clone可以用写时拷贝(copy on write COW)策略提高性能。原理是fork一个新进程时,会添加对原有地址空间的引用而非把所有内容都复制一遍,如果进程要修改被引用的内存空间才会建立一个单独的副本。推迟甚至取消了内存拷贝的需要。

        进程的生命周期linux大神Brendan Gregg 性能之巅 第二版(systems performance)阅读心得(第三章 操作系统)-LMLPHP

        进程环境

        进程环境分为用户地址空间和内核上下文,内核上下文保存了进程切换时需要保存或换入的进程信息,而用户地址空间是内存中的用户数据和用户栈。

        linux大神Brendan Gregg 性能之巅 第二版(systems performance)阅读心得(第三章 操作系统)-LMLPHP

3.2.7 栈

        栈是存储临时数据的内存区域,函数被调用时,返回地址保存到栈中,函数需要的一些寄存器的值也可以被保存在栈里面。函数执行完后,恢复所有需要的寄存器,并从栈中获取返回地址。

12-15 17:00