Linux内核实现机制概述_第1页
Linux内核实现机制概述_第2页
Linux内核实现机制概述_第3页
Linux内核实现机制概述_第4页
Linux内核实现机制概述_第5页
已阅读5页,还剩8页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Linux2 6 内核分析内核分析 Linux 内核主要由内核主要由 5 个模块构成个模块构成 分别是 进程调度模块 内存管理模块 虚拟文件系 统模块 进程间通信模块 Linux 经常使用散列表散列表来实现高速缓存 高速缓存是需要快速访问的信息 一 进程一 进程 进程的模型包括进程控制块 PCB 程序部分和数据集合三部分 1 进程控制块 进程控制块 PCB PCB 是进程存在的唯一标识 PCB 按功能分主要包含以下四部分 进程标示符 处理机状态 进程调度信息 进程四部分 进程标示符 处理机状态 进程调度信息 进程 控控 制信息 制信息 1 进程标示符 唯一标识一个进程 2 处理机状态 有处理机的各种寄存器中的内容组成 寄存器包括通用寄存器 指 令寄存器 程序状态字 PSW 和用户栈指针 当初立即被中断时 进程运行信息必须保存 在 PCB 中 以便运行时从断点继续执行 3 进程调度信息 存放进程状态 进程优先级 进程调度所需其他信息 如调度算 法 进程已运行时间 等待 CPU 时间 时间或阻塞原因 4 进程控制信息 包括程序和数据的内存或者外存地址 进程同步和通信机制 资 源清单 除 CPU 以外进程所需的全部资源以及已经分配的资源 链接指针 下一进程 PCB 地址 Linux 的进程控制块 PCB 使用一个成为 task struct 的结构体来描述 该结构体中定义了 进程的几种状态进程的几种状态 1 TASK RUNNING 状态 Linux 的进程运行状态包括实际的运行和就绪状态 对两 者的区分是根据当前是否占有 CPU 结构体中 current 变量可以区分两者 2 TASK INTERRUPTIBLE 状态 即可中断的等待状态 当进程在等待某个事件和某 个资源 可中断等待状态的进程可以被信号唤醒而进入就绪状态等待调度 3 TASK UNINTERRUPTIBLE 状态 即不可中断等待状态 该状态进程由于硬件不能 满足 不能被信号唤醒 必须等到得到所等待的资源之后才能被唤醒 4 TASK ZOMBIE 状态 即僵死状态 终止进程所占有的资源全部释放之后 还保 存着 PCB 信息 这种占有 PCB 但已被撤销的进程处于僵死状态 如僵死进程 5 TASK STOPPED 状态 即暂停状态 一般都是有运行状态转换来 正等待某种特 殊处理 如调试跟踪的程序 6 TASK DEAD 状态 新增加的状态 指已经退出但是不需要父进程回收的进程 Linux 内核创建一个进程时 首先会新建一个空的 task struct 结构体 并将相应信息填 入结构体中 然后将该结构体的指针添加进 task 数组 这个数组大小由 NR TASK 默认一 般为 512 指定 调度程序一直维持着一个 current 指针 它指向当前正在运行的程序 Task 0 必须指向 init task 进程 0 号进程 Linux 中 内核将所有 struct task 结构体以两种方式组织 1 哈希表 将进程的 PID 作为哈希算法的输入 可以用一个给定 PID 快速查找到进 程 通过 find task pid 来定位相应进程 2 双向循环链表 这样可以使系统很容易遍历所有的进程 通过调用 for each task 来实现遍历 task struct 结构体中的变量 list head 的作用就是将进程通过双 向链表将进程连接起来 链表的首部和头部都是 init task 进程 2 进程的创建 进程的创建 Linux 提供了三种创建新进程的方法 提供了三种创建新进程的方法 fork vfork clone 三者分别对应系统调用的 sys fork sys vfork sys clone 最终三者都是通过 do fork 调用完成的 目前 Linux 在创建进程时 采用 写时拷贝 技术 即在创建进程时并不将父进程所 有的资源都复制给子进程 而是需要时才进行资源的拷贝 可以大大提高 Linux 的性能 1 fork 函数 调用 fork 后 系统会创建一个子进程 子进程和父进程不同的只有它的进程 ID 和父进 程 ID 其他都一样 地址空间不共享 由于采用 写时拷贝 技术 子进程并不完全拷贝 父进程的数据段和栈 堆等的复制 这些区域作为父子进程的共享区域 而且内核将他们 访问权限设置为只读 如果父子进程任何一个试图修改此区域 内核就为那块内存拷贝制 作一个副本 之所以采用 写时拷贝 是因为一般 fork 后会调用 exec 调用其他的执行体 父子进程的执行顺序不确定 fork 函数被调用一次 但是返回两次值 两次返回值的区别是 子进程的返回值是 0 父进程返回值是子进程的进程 ID 调用失败的话返回 1 2 vfork 函数 该函数与 fork 基本一致 只不过父子进程共享父进程的地址空间 对于 vfork 创建新进程后 父进程会阻塞 子进程借用父进程的地址空间运行 直到 子进程退出或者调用 exec exec 函数族的作用是启动另一个程序的执行 父进程才可以 运行 vfork 和 fork 返回值相同 3 clone 函数 clone 函数和 fork vfork 不同 它接受一个指向函数的指针和该函数的参数 在创建 子进程成功时就调用这个函数执行 3 进程终止 进程终止 分为自愿终止和被动终止 1 自愿终止 a 显式自愿终止 在进程中调用 exit 函数 b 隐式自愿终止 进程从某个程序的主函数退出 2 被动终止 a 当进程接收到一个它既不能处理也不能忽略的信号和异常 b 进程接收到 SIGABRT 或者其他终止信号 上述进程终止主要分为两步来完成 1 首先通过调用 do exit 函数释放掉与进程相关的大部分资源 并使进程处于僵 死状态 但是进程描述符不释放 2 然后对进程的处理应看子进程与父进程谁先终止 子进程先终止的话 则子进 程一直处于僵死状态 直到父进程调用 wait 或者 waitpid 调用完成后则完 全释放 父进程先终止 则内核必须为子进程找到新的父进程 方法是首先给 子进程在当前组内找一个线程最为父进程 不行就让 init 做父进程 wait 函数的两个作用 获取内核发送来的子进程终止消息和清除子进程的所有独享资源 wait 函数会首先挂起调用它的进程 知道该进程的一个子进程终止 此时函数会返回该子 进程的 PID 给父进程 4 线程的实现 线程的实现 Linux 内核中没有专门的实现线程的机制 而是通过用户级程序库来实现的 例如 pthread 库 以便将所有的线程映射到一个单独的内核级进程中 Linux 提供的一种不区分提供的一种不区分 进程和线程的方案进程和线程的方案 通过使用一种类似于 Solaris 轻量级进程的方法 用户级线程被映射到 内核级进程上 组成一个用户级进程的多个用户级线程被映射到共享同一个 ID 的多个 Linux 内核级进程上 这使得这些进程可以共享文件和内存等资源 使得同一组中的进程调 度切换时不需要切换上下文 5 Linux 进程调度进程调度 Linux 是一个抢占式多任务系统 高优先级的可以抢占低优先级的 CPU 运行 Linux 优 先级分为静态优先级和动态优先级 Linux 进程分为普通进程和实时进程两类 实时进程创建时静态优先级就已经分配而且 不会改变 不为实时进程计算动态优先级 实时进程的优先级范围为 0 99 都高于普通进程 100 139 普通进程优先级同样有静态优先级 但是没有作用 内核为普通进程计算动态优 先级 并根据优先级分配时间片 来调度进程 Linux 提供了三种调度策略三种调度策略 1 SCHED NORMAL 面向普通进程的时间片轮转策略 时间片用完后再选择一个优 先级相对较高的进程进程调度 2 SCHED FIFO 面向对响应时间要求比较高 运行所需时间较短的实时进程 3 SCHED RR 面向对响应时间要求比较高 运行所需时间较长的实时进程 总结调度 根据进程的分类调度可分为实时调度和非实时调度 1 实时调度 实时调度 针对实时进程静态优先级 针对实时进程静态优先级 对于实时进程 静态优先级决定了对 CPU 的抢占 当高优先级的进程到达时 会抢占 低优先级进程的 CPU 同样可以知道实时进程总是能抢占普通进程的 CPU 对于同一优先 级的实时进程则又可采用两种调度算法 FIFO 先来先服务 和 RR 时间片轮转 例如 当前进程有 A 30 B 20 C 20 D 5 且 B 早于 C 到达 括号内为进程的静 态优先级 则采用 FIFO 为 D 优先级最高先执行 B 然后是 B 和 C 优先级相同 由于 B 早 到达 所以先执行 B 再 C 最后是优先级最低的 A 执行顺序为 D B C A 采用 RR 则仍 然是先运行 D 完毕后则交换运行 B 和 C 运行完毕后是 A 顺序为 D B C B C A 2 非实时调度 非实时调度 普通进程动态优先级 普通进程动态优先级 内核为普通进程计算动态优先级 根据此优先级为进程分配不同的时间片 RR 此 优先级只作为分配时间片的基础 不能够通过动态优先级高低抢占 CPU 每次当进程的时 间片使用完后都会为其重新计算动态优先级及分配的时间片 二 系统调用二 系统调用 Linux 的每个系统调用都是通过一些宏 一张系统调用表 一个系统调用入口来完成 的每个系统调用都是通过一些宏 一张系统调用表 一个系统调用入口来完成 1 宏 Linux 为每个系统调用定义了一个唯一的编号 成为系统调用号 通过宏定义方式定义 例如 define NR setup 0 Linux 中系统调用号一旦分配就不可以再进行更改 否则已经编译好的木块将不能正常 使用 即使删除的系统调用 也不可以把之前已经分配的系统调用号重新分配 删除的系 统调用有相应的空处理 2 系统调用表 系统调用表是一个函数指针数组 跳转时以系统调用号作为数组下表 找到相应的函 数指针 3 系统调用入口 系统调用入口其实是由系统调用入口函数实现 功能是将系统调用号放入 eax 寄存器 后移用 int 0 x80 使处理器转向系统调用入口 查找系统调用表 进而执行内核调用真正的 函数 Linux 系统调用实际是软中断软中断 系统调用过程中 Linux 首先通过执行相应的机器代码 指令 int 0 x80 产生一个软中断的异常处理信号 使系统自动从用户态切换到内核态 三 中断机制三 中断机制 Linux 中断主要分为硬中断 IRQ 和软中断两类 IRQ 主要分为 短类型 IRQ 和长类型 IRQ 短类型 IRQ 需要很短的时间 在此期间机器 的其他部分被锁定 而且不能发生其他中断被处理 长类型 IRQ 需要较长的时间 期间可 能发生其他中断 当用户程序被来自外部信号中断后 立即保存现场工作 包括保存返回地址和用户寄 存器等数据 然后查找中断向量表 找出相应的中断处理程序 系统将中断分为三种 捕系统将中断分为三种 捕 俘 系统调用和外中断 俘 系统调用和外中断 捕俘 通过捕俘处理程序入口表查找到用户编写的处理程序执行 系统调用 软中断 通过系统调用表找到操作系统核心提供的服务例程 外中断 直接调 用核心提供的外中断处理程序运行 1 硬中断过程 硬中断过程 Linux 中 若一个硬件想向 CPU 发送中断信号 必须首先获得一个可用的 中断请求 线 即中断前必须获得一个可用的 IRQ 号 产生一个中断信号后以电信号发送给中断控 制器 硬件芯片 接着 CPU 根据中断控制器的状态位判定中断的来源 获得中断号 根 据中断号查找中断向量表 从表中获得中断处理函数的地址 然后跳转到中断函数入口地 址处 执行这个函数 2 中断处理程序 中断处理程序 硬中断硬中断 中断处理程序主要做的工作 a 保护未被硬件保护的一些必须的寄存器 b 识别各个中断源 分析产生中断的原因 c 处理发生的中断事件 d 恢复正常的工作 Linux 规定中断处理程序是不可重入的 指的是同一中断线上不可以再发生新的中断 因为所有的处理器都将原中断所在的中断线已经屏蔽 Linux 中同样规定了同一中断程序不能够并行 这样同一个中断处理程序不可以被同时 调用来处理嵌套的中断 Linux 中将中断处理程序分为两部分 上半部和下半部 中将中断处理程序分为两部分 上半部和下半部 上半部主要用来处理那些具有严格时限要求的任务 上半部可以看做是一个用来 登 记中断 功能的函数 将中断例程的下半部挂到下半部执行队列中 上半部要求执行很快 主要是因为上半部完全屏蔽中断下执行 即不可中断 下半部主要用于处理那些可以稍后执行的任务 下半部是可中断的 当发生其他中断 时 下半部可中断等待另外一个中断的上半部执行完毕后再继续执行 3 下半部机制 下半部机制 Linux 中提供了三种机制来实现下半部机制 1 软中断 软中断 软中断是一组静态定义静态定义的下半部结构 使用数组来组织软中断结构体 共有 32 个 两 个相同的软中断可以同时执行 必须在编译期间进行静态注册 软中断机制一般都保留给系统中对时间要求最严格以及重要的下半部来使用 Linux2 6 中只有两个子系统是通过软中断来实现的 网络子系统和 SCSI 2 tasklet tasklet 要比软中断机制方便且简单 而且它本身也是基于软中断实现 属于软中断 既可以静态的创建 tasklet 也可以动态动态的创建 tasklet Linux 中 tasklet 分为两类 HI SOFTIRQ 和 TASKLET IRQ 前者比后者的优先级要高 优 先调用前者 在中断数组 irq desc 中会分配两项给 tasklet 即两种类型各占数组中一项 两者分别以一个链表来组织 3 工作队列 工作队列 work queue 工作队列与前两者最大的不同之处是它是唯一一个能在进程上下文中运行的下半部机唯一一个能在进程上下文中运行的下半部机 制 意味着它能允许睡眠 制 意味着它能允许睡眠 工作队列的实质实质是将推后的工作交给一个内核线程来完成 核心思想即时创建一个内 核线程 Linux 中已经默认提供了一种命名为 enents 一类工作者线程来实现工作队列 4 中断的数据结构 中断的数据结构 Linux 内核中定义了一个数组 irq desc 数组来管理中断 数组中的每一项对应一个中 断源 数组中的每个成员都为 irq desc t 结构体 即数组中的每一项对应着中断向量表中 的一项 1 irq desc t 结构体 irq desc t 结构体用来描述中断源 其中结构体中的 handler 指向 hw interrupt type 结 构体的指针 action 变量指向由 irqaction 结构体组成的单向链表的头的指针 2 irqaction 结构体 该结构体中指明内核接收到特定 IRQ 后该才去的动作 结构体中变量 handler 指向中 断处理程序 3 hw interrupt type 结构体 用来描述中断控制器 是一个抽象的中断控制器 5 中断上下文 中断上下文 当一个中断处理程序正在执行时 内核处于中断上下文中 中断上下文是不可以睡眠中断上下文是不可以睡眠 的的 与进程上下文是不同的 进程上下文即使睡眠了也可以重新调度将其唤醒 中断上下 文不可以被重新调度 中断处理程序没有自己的堆栈 它会共享被它中断的那个进程的堆栈 如果没有进程 正在执行 则占用 idle 进程的堆栈 每个处理器都有自己的运行队列 队列中都有 idle 进 程 当前运行队列都 dequeue 时则运行 idle 进程 四 内核同步机制四 内核同步机制 内核同步主要是同步各执行单元对共享数据的访问 尤其是多处理器的同步 Linux2 6 中内核同步机制主要包括以下几种 原子操作 信号量 semaphore 读写 信号量 rw semaphore 自旋锁 spinlock 大内核锁 BKL 等 1 原子操作 原子操作 原子操作就是指某一个操作在执行过程中不可以被打断 要么全部执行 要不就一点 也不执行 原子操作需要硬件的支持 与体系结构相关 使用汇编语言实现 原子操作主要用于实现资源计数 很多引用计数就是通过原子操作实现 Linux 中提供 了两种原子操作接口 分别是原子整数操作和原子位操作 原子整数操作只对 atomic t 类型的数据进行操作 不能对 C 语言的 int 进行操作 使 用 atomic t 只能将其作为 24 位数据处理 主要是在 SPARC 体系结构中 int 的低 8 为中设置 了一个锁 避免对原子类型数据的并发访问 原子位操作是针对由指针变量指定的任意一块内存区域的位序列的某一位进行操作 它只是针对普通指针的操作 不需要定义一个与该操作相对应的数据类型 2 自旋锁 自旋锁 Linux 自旋锁保证了任意时刻只能有一个执行线程进入临界区 其他试图进入临界区的 线程将一直进行尝试 即自旋 直到获得该锁 自旋锁主要应用在加锁时间不长并且不会 睡眠的情况 自旋锁的本质自旋锁的本质是对内存区域的一个整数的操作 任何线程进入临界区之前都必须检查 该整数 可用则进入 都则一直忙循环等待 自旋锁机制让试图获得该锁的线程一直进行忙循环 占用 CPU 因此自旋锁适合于断 时间内进行轻量级加锁 而且自旋锁绝对不可以递归使用 否则会被自己锁死 Linux 自旋锁主要应用与多核处理器中 单自旋锁主要应用与多核处理器中 单 CPU 中不会进行自旋锁操作 中不会进行自旋锁操作 linux 上的自旋锁有三种实现 a 在单 cpu 不可抢占内核中 自旋锁为空操作 b 在单 cpu 可抢占内核中 自旋锁实现为 禁止内核抢占 并不实现 自旋 c 在多 cpu 可抢占内核中 自旋锁实现为 禁止内核抢占 自旋 其中 禁止内核抢占只是关闭 可抢占标志 而不是禁止进程切换 显式使用 schedule 或 进程阻塞 此也会导致调用 schedule 时 还是会发生进程调度的 3 读 读 写自旋锁写自旋锁 Linux 中规定 读 写自旋锁允许多个线程同时以只读的方式访问临界资源 只有当一 个线程想更新数据时 才会互斥访问资源 读写自旋锁包括一个 24 位读者计数和一个解锁标记来实现的 4 信号量 Linux 中提供了两种信号量 a 内核信号量 由内核程序使用 b System V IPC 信号量 由用户进程使用 当一个线程去请求以不可用的信号量时 和自旋锁不同 该进程会进入睡眠 不再占 用 CPU 加入到等待队列中 直到被唤醒 所以只有可睡眠的状态才可以使用信号量 信号量实现的结构体 semphore 中有一变量 count 计数 根据 count 取值的设定 信号信号 量可以分为二元信号量和计数信号量量可以分为二元信号量和计数信号量 当 count 初值为 1 时 则为二元信号量 计数信号 量允许任意数量的锁持有者 这点和自旋锁是不同的 自旋锁只允许一个 5 读 读 写信号量写信号量 读写信号量实际上对于读者使用的是一个计数信号量 写者使用的是二元信号量 读 写信号量同读写自旋锁一样提高了内核的并发度 Linux 内核时按照先进先出 FIFO 的顺序来处理等待读写信号量的进程 具体过程是 如果一个进程试图获取一个不可用的信号量时 加入到等待队列的末尾 当信号量可用时 内核首先唤醒等待队列的第一个进程 如果该进程为写进程 那么该进程获得信号量 如 果该进程如果为一个读进程 那么其后的所有的读进程都可以被唤醒并获得信号量 但是 中间不能跳跃 6 BKL Big Kernel Lock BKL 即全局内核锁 也称大内核锁 它是一个全局自旋锁 大内核锁也是用来保护临 界区资源的 避免出现多个处理器上的进程同时访问同一区域 整个内核中只有一个大内 核锁 BKL 是一个名为 kernel flag 的自旋锁 持有该锁的进程仍可以睡眠 当睡眠时持有的 锁将被自动释放 该进程被唤醒时重新持有该锁 Linux 允许一个进程可以递归的持有 BKL BKL 是一个递归锁 它的设计思想是 一旦某个内核路径获取了这把锁 那么其他所有的内核路径都不能 再获取到这把锁 自旋锁加锁的对象一般是一个全局变量 大内核锁加锁的对象是一段代 码 里面可能包含多个全局变量 那么他带来的问题是 虽然 A 只需要互斥访问全局变 量 a 但附带锁了全局变量 b 从而导致 B 不能访问 b 了 7 屏障 屏障 屏障或称内存屏障 是用来解决内存同步问题的 具体为对由于编译器的优化和缓存 的使用 导致对内存的写入操作不能及时的反应出来 也就是说当完成对内存的写入操作 之后 读取出来的可能是旧的内容的一种解决机制 内存屏障分类 a 编译器引起的内存屏障 b 缓存引起的内存屏障 c 乱序执行引起的内存屏障 五 内存管理机制五 内存管理机制 内存管理主要负责完成当进程请求内存时给进程分配可用的内存 当进程释放内存时 回收相应的内存 同时负责跟踪系统中相应内存的使用状态 Linux 采用页式内存管理 页是物理内存管理的基本单位采用页式内存管理 页是物理内存管理的基本单位 但严格来说 Linux 采用的是 段页式内存管理 既分段也分页 内存映射的时候 先确定对应的段 确定段基地址 段 内分页 再找到对应的页表项 确定页基地址 再由逻辑地址的低位确定的页偏移量就能 找到最终的物理地址 但 Linux 中的所有段地址都是 0 即所有的段是相同的 之所以有段 的概念是因为 Linux 为了符合硬件体系 所以 Linux 实际采用的是页式内存管理实际采用的是页式内存管理 但段的概 念在内核中确实存在 1 物理内存的管理 物理内存的管理 Linux 中首先将内存分为若干个节点 每个节点下面又可分为 1 3 个区 每个区下面会 有若干个页 1 节点 内存节点主要是依据 CPU 访问代价不同而划分的 一个 CPU 对应一个节点 内核数组 node data 形式组织节点 存储的为 struct page data t 指针来描述内存分区 2 区 内核以 struct zone 来描述内存分区 内核将所有的物理页分为 3 个区 ZONE DMA ZONE NORMAL ZONE HIGHMEM ZONE DMA 区中包含的页可以用来进行 DMA 操作 即直接内存访问操作 通常为物 理内存的起始 16M ZONE NORMAL 区包含的页是可以进行正常的内存映射的页物理内存 为 16 896M ZONE HIGHMEM 区称为 高端内存 该区所包含的页不可以进行永久映射 即不可以永久映射到内核地址 物理内存 896M 以后的 高端内存的边界为 896M 的原因 32 为 Linux 系统中虚拟内存空间为 0 4G 3G 4G 为 内核态 为了应对内核映射超过 1G Linux 采取的策略 内核地址空间的 896M 采用固定 映射 映射方法 虚拟地址 3G 物理地址 只能映射 896M 即 3G 3G 896M 剩余的 128M 3G 896M 4G 采用动态映射 Linux 下以 struct zone 结构体来表示一个区 在该结构体中变量 struct page zone mem map 用来管理该区下的内存映射表 3 页 每一个物理页框都使用一个数据结构 struct page 来描述 该结构体中的 lru 变量构建 用于 LRU 页面置换的链表 在页框空闲情况下 该成员变量用于构建伙伴算法 链表同等 大小的空闲内存块 大多数 32bit 的操作系统的页大小为 4KB 2 伙伴算法 伙伴算法 Linux 采用的是伙伴 Buddy 算法对物理内存进行管理 伙伴机制是操作系统的一种 动态存储管理算法 该算法通过不断平分较大的空闲内存块来获得较小的空闲内存块 直 到获得所需的内存块 当内存释放时 该算法尽可能的合并空闲块 该算法要求内存块的 分配和合并都是以 2 的幂次方为单位 在 区 内存结构体 struct zone 中有一 struct free area 类型的数组 free area 数组 最大为 12 个元素 数组的下标 k 对应着固定大小 2 k 个页框空闲内存区域的双向链表头 当需要空闲块为 4 即 2 2 个页框时则查找 free area 2 如果没有合适的 则查找 free area 3 直到找到合适的 3 slab 分配器分配器 Linux 中引入 slab 是为了减少对伙伴算法的调用 采用 slab 分配器来减少频繁分配和 释放内存数据结构的开销 同时减少了碎片的产生 slab 分配机制是基于伙伴算法之上实 现 slab 是基于一组对象缓存 把不同对象划分为 caches 物理内存 每个 cache 保存一 种类型的对象 每个 cache 由一个或者多个 slab 组成 每个 slab 包含一个或者多个 page 组成 每个 slab 处于 3 中状态之一 即 full partial 和 empty 分别是满 部分满 空 其 中满状态的 slab 没有任何可分配的空闲对象 当请求空闲对象时则从部分满和空的 slab 中 分配 Linux 内核中的 cache 以结构体 kmem cache s 来表示 结构体中变量 lists 中存储的为 三个链表分别对应于 slab 的三种状态 总结来说 当为一对象申请内存时 首先查找到该对象的 cache 然后查找 cache 中的 slab 列表 分配空闲内存 当释放该对象内存时 则返回给该对象对应的 slab 这样伙伴 算法就不需要频繁的进行分配和合并操作 4 虚拟内存 虚拟内存 1 逻辑地址 线性地址 物理地址的转换过程 逻辑地址即程序指令的地址 线性地址指页式转换前的地址 虚拟地址 物理地址则 是物理内存中的地址 一个逻辑地址由两部份组成 段标识符 段内偏移量 段基址确定它所在的段居于整 个存储空间的位置 偏移量确定它在段内的位置 Linux 中由于段基址都是 0 所以逻辑地址 和线性地址相同 线性地址再通过 MMU 进行转换到物理地址 这个过程下面重点讲下 Linux 也是内存管理使用三级表结构 页目录 页中间目录 页表 一个活动任务都有 一个页目录 大小一般为一页 页目录必须在内存中 页中间目录可以跨越多个页 页表 同样可以跨越多个页 对应具体的页框 具体过程如下图 2 页面置换算法 Linux 中页结构体的组织方式为双向循环链表 Linux 中页面置换算法基于时钟算法机 制实现 页结构体 page 中有一变量 count 专门用来计算页面被引用的次数 每当页面被访 问一次时 count 加 1 在 Linux 后台 Linux 周期性地扫描全局页池 并且当它在内存中的 所有页间循环时 将扫描的每一页的 count 减 1 age 越大则使用频率越高 最终内核通过 最近未使用 最近未使用 LRU 算法进行页面置换 算法进行页面置换 6 高速缓存 Linux 使用了一系列的高速缓存相关的内存管理技术来提高性能 此处的高速缓存并非 是物理缓存 而是软件方法 Linux 中主要包括以下几个缓存 1 Buffer Cache 包括了用于块设备驱动程序的数据缓冲区 这些缓存区固定 一 般 512B 包括从块设备要读取的数据和要写入块设备的数据 操作时先查看缓冲区 2 Page Cache 用来加快对磁盘上映像和数据的访问 用来缓存文件的逻辑内容 一次缓存一页 3 Swap Cache 只有改动过的 或脏 页才存在交换文件中 只要交换文件没有再 次修改 下次这些页需要交换出时就不需要再写到交换文件中 4 Hardware Cache 常见方法是在处理器中 PTE 的高速缓存 这种情下处理器不需 要直接读取页表 需要时把页表放在缓存区中

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论