《深入Linux内核架构》摘要

前言

本文为《深入Linux内核》一书部分内容摘要。

简介和概述

地址空间与特权级别

  • 32位系统,每个进程的地址空间是3GB,1Gb内核空间。这种划分与内存的数量无关,当然这种比例可以通过配置来修改
  • 64位系统情况更复杂,它们倾向于使用小于64位的理论最大虚拟地址空间,这样管理有效地址空间所需的位数较少。
  • intel处理器分四种特权级别,linux只有使用了两种,核心态和用户态。从用户态道核心态需要通过系统调用来切换。
  • ps fax查看的结果中,用方括号扩起来的是内核线程。
  • 多数情况下,虚拟地址空间要比可用的物理内存要大。
  • 物理内存页常常被称为页帧,页则专指虚拟地址空间中的页。
  • 用来将虚拟地址空间映射到物理地址空间的数据结构称为页表。
  • 由于页表通常是4KB这样导致记录页表的数据也非常大,因此采用个多级分页。linux采用了四级页表。32位两级,64位三级或四级
  • 内存映射,映射方法可以将任意来源的数据传输到进程虚拟地址空间中。

物理内存的分配

  • 内核中很多时候要求分配连续页。为快速检测内存中的连续区域,内核采用了一种古老而历经检验的技术:伙伴系统
  • 由于内核无法使用标准库的函数,因而必须在伙伴系统基础上自行定义额外的内存管理层,将伙伴系统提供的页划分为更小的部分。-slab缓存。

进程管理和调度

进程优先级

  • 进程可分为实时进程和非实时进程
  • 通常有,硬实时进程,软实时进程,普通进程

进程生命周期

  • 运行态:进程正在执行
  • 等待态(就绪):进程能够运行,但是没有许可,需要等待CPU时间
  • 睡眠态(阻塞态):进程阻塞无法运行,它正在等待一个外部事件
  • 进程无法直接从阻塞态直接变为运行态
  • 从用户态切换到核心态,通常方法有系统调用,中断(例如进入系统的网络数据包)
  • 普通进程总是可能被抢占
  • 如果系统处理核心态,那么系统中的其他进程无法夺取CPU时间,但中断可以中止系统调用
  • 中断可以暂停处于用户态和核心态的进程。中断具有最高优先级

进程类型

  • 新进程是使用fork和exec系统调用产生的
  • fork生成当前进程的一个相同副本,该副本称之为子进程。原进程的所有资源都以适当的方式复制到子进程。注意,fork采用了写时复制技术
  • exec从一个可执行的二进制文件加载另一个应用程序,来代替当前运行的进程。

命名空间

  • 全局资源可以通过命名空间抽象起来,这使得可以将一组进程放置到容器中,哥哥容器彼此隔离。

进程ID号

  • UNIX进程总是会分配一个号码用于在其命名空间中唯一地标识它们。该号码称作进程ID号
  • 分配一个空闲的piu,本质上就等同于寻找位图中第一个值位0的比特,释放一个pid可通过将对应的比特从1切换为0来实现。

进程管理相关的系统调用

  • fork是重量级调用,因为它建立了父进程的一个完整副本,然后作为子进程执行。
  • vfokr类似于fork,但并不创建父进程数据的副本。相反,父子进程之间共享数据。它用于子进程形成后,立即执行execve系统调用加载新程序的情形。但是由于fork使用了写时复制技术,vfork在速度方面不再有优势。
  • clone产生线程,可以对父子进程之间的工农乡,复制进行精确控制
  • 写时复制技术。fork时,并不复制进程的整个地址空间,而只复制其页表,这样就建立了虚拟地址空间和物理内存页之间的联系。只要一个进程师徒像复制的内存页写入,处理器就会像内核报告访问错误。如果该页是可读可写,则进行复制。写时复制机制使得内核可以尽可能延迟内存页的复制,更重要的是,在很多情况下不需要复制,这节省了大量时间。
  • 依赖于execve的实现,释放原进程使用的所有资源,将应用程序映射到虚拟地址空间,设置进程指令指针和其他特定于体系结构的寄存器。
  • 进程必须用exit系统调用终止,这使得内核有机会将该进程使用的资源释放回系统。

调度器的实现

  • 调度器的任务是在程序之间共享CPU时间,创造并行执行的错觉。
  • 该任务分为两个不同部分,一个是调度策略,一个是上下文切换
  • 内核必须提供一种方法,在各个进程之间尽可能地公平地共享CPU时间,而同时又要考虑不同的任务优先级。
  • linux调度器的一个杰出特性是,它不需要时间片概念,至少不需要传统的时间片。而只考虑进程的等待时间,即进程在就绪队列中已经等待了多长时间。

内存管理

  • 如果物理内存比可以映射到内核地址空间中的数量要多,那么内核必须借助高端内存方法来管理多余的内存

进程虚拟内存

  • 每个应用程序都有自身的地址空间,与其他所有应用程序分隔开
  • 在巨大的线性地址空间中,只有很少的段可用于各个用户空间进程
  • 地址空间只有极小的一部分与物理内存页直接关联
  • 无论当前哪个用户进程处于活动状态,虚拟地址空间内核部分的内容总是相同的
  • 按需分配和填充页称之为按需调页法

锁与进程间通信

控制机制

  • 几个进程在访问资源时彼此干扰的情况通常称之为竞态条件
  • 只要没有其他进程进入临界区,那么在临界区中执行的进程完全是可以中断的
  • 信号量只是受保护的特别变量,其初始值位1.down操作将值减1,变为0,如果发现信号量的值为0,则导致后面的进程在该信号量上睡眠。
  • 虽然信号量初看起来容易实现,但其开销对内核来说过大。
  • 如果处理器同时处于核心态,则理论上它们可以同时访问一个数据结构,这就刚好造成了竞态条件
  • 内核使用了由锁组成细粒度网络,来明确地保护各个数据结构
  • 原子操作 ,不中断地执行
  • 自旋锁,用于短期保护某段代码,内核进入睡眠状态,直到被唤醒。在此期间会重复检查能够获取锁,而不会进入睡眠状态
  • 信号量 等待释放信号量是,内核进入睡眠状态,直至被唤醒,直到被唤醒后,才重新尝试获取信号量。互斥量是信号量的特例。
  • 读写锁。任意数目的处理器都可以对数据结构进行并发读访问,但只有一个处理器能进行写访问,在进行写访问时,读访问页无法进行。
  • 自旋锁不应该长期持有
  • 在单处理器系统上,自旋锁定义为空操作,因为不存在几个CPU同时进入临界区额情况,但如果启动了内核抢占,这种说法就不适用了。
  • 信号量适合于保护更长的临界区。以防止并行访问
  • RCU保护 read-copt-update

虚拟文件系统

  • VFS的任务不简单,它用来提供一种操作文件,目录以及其他对象的统一方法,另一方面,它必须能够与各种方法给出的具体文件系统的实现达成妥协。
  • 文件系统一般可以分为三种,基于磁盘的文件系统,虚拟文件系统,网络文件系统
  • 对用户程序来说,一个文件由一个文件描述符标识
  • 内核处理文件的关键是iNode
  • 在硬连接建立事,创建的目录项使用了一个现存的inode编号
  • 普通文件支持随机访问,但是命名管道,字符设备等文件不支持这种做法。

页缓存和块缓存

  • 数据并非每次在修改后都立即写回,而是在一定的时间间隔之后才进行回写,时间间隔的长度取决于多种因素,如空闲物理内存的容量,物理内存中数据的利用率等等。
  • 用于缓存的内存区不能分配和普通应用程序,这减少了实际可用的物理内存容量。
  • slab是一个内存到内存的缓存,其目的不是加速对低速设备的操作,而是对现存资源进行更简单,更搞笑的使用。
  • 何时回写?pdflush进程周期性激活;短期内快速增加;用户调用sync

页面回收和页交换

  • 只有少量几种页可以换出到交换区,类型为MAP_ANONYMOUS的页,没有关联到文件
  • 进程的私有映射用于映射修改后不向底层块设备回写的文件
  • 所有属于进程堆以及使用malloc分配的页
  • 用于实现某种进程间通信的页
  • 内核本身使用的内存页绝不会换出
  • 用于将外设映射到内存空间的页也不能换出
守望 wechat
关注公众号[编程珠玑]获取更多原创技术文章
出入相友,守望相助!