精华内容
下载资源
问答
  • 深入linux内核架构 笔记
    千次阅读
    2022-01-28 10:31:15

    堆是进程中用于动态分配变量和数据的内存区域,堆的管理对应用程序员不是直接可见的。因为 它依赖标准库提供的各个辅助函数(其中最重要的是malloc)来分配任意长度的内存区。malloc和 内核之间的经典接口是brk系统调用,负责扩展/收缩堆。新近的malloc实现(诸如GNU标准库提供的) 使用了一种组合方法,使用brk和匿名映射。该方法提供了更好的性能,而且在分配较大的内存区时 具有某些优点

    brk系统调用只需要一个参数,用于指定堆在虚拟地址空间中新的结束地址(如果堆将要收缩, 当然可以小于当前值)。 照例,brk系统调用实现的入口是sys_brk函数,

    该代码确保brk的新值(原值也同样)是系统页长度的倍数。换句话说,一页是用brk能分配的 最小内存区域 (因此在用户空间需要另一个分配器函数,将页拆分为更小的区域。这是C标准库的任务

    在需要收缩堆时将调用do_munmap

    如果堆将要扩大,内核首先必须检查新的长度是否超出进程的最大堆长度限制。find_vma_ intersection接下来检查扩大的堆是否与进程中现存的映射重叠。 

    更多相关内容
  • 深入Linux内核架构笔记(同步)

    千次阅读 2022-01-28 11:31:51
    内核可以不受限制地访问整个地址空间。在多处理器系统上(或类似地,在启用了内核抢占的单 处理器系统上,可参见第2章),这会引起一些问题。如果几个处理器同时处于核心态,则理论上它们可以同时访问同一个数据结构...

    内核可以不受限制地访问整个地址空间。在多处理器系统上(或类似地,在启用了内核抢占的单 处理器系统上,可参见第2章),这会引起一些问题。如果几个处理器同时处于核心态,则理论上它们可以同时访问同一个数据结构

    内核为此提供了各种锁选项,分别优化不同的内核数据使用模式。

     原子操作:这些是最简单的锁操作。它们保证简单的操作,诸如计数器加1之类,可以不中断 地原子执行。即使操作由几个汇编语句组成,也可以保证。

     自旋锁:这些是最常用的锁选项。它们用于短期保护某段代码,以防止其他处理器的访问。 在内核等待自旋锁释放时,会重复检查是否能获取锁,而不会进入睡眠状态(忙等待)。当然, 如果等待时间较长,则效率显然不高。

     信号量:这些是用经典方法实现的。在等待信号量释放时,内核进入睡眠状态,直至被唤醒。 唤醒后,内核才重新尝试获取信号量。互斥量是信号量的特例,互斥量保护的临界区,每次 只能有一个用户进入。

     读者/写者锁:这些锁会区分对数据结构的两种不同类型的访问。任意数目的处理器都可以对 数据结构进行并发读访问

    原子操作

    内核支持的所有处理器,都提供了原子执行此类操作的手段。一般说来,可使用特殊的锁指令 阻止系统中其他处理器工作,直至当前处理器完成下一个操作为止。也可以使用效果相同的等价机 制(IA-32系统上所需的指令实际上就称作lock) 内核为SMP系统提供了local_t数据类型。该类型允许在单个CPU上的原子操作。为修改此类型 变量,内核提供了基本上与atomic_t数据类型相同的一组函数,只是将atomic替换为local。

    请注意,原子变量很适合整数操作,但不适用于比特位操作。因此每种体系结构都必须定义一组 位处理操作,这些操作的工作方式也是原子的,以便在SMP系统上的各处理器之间保证一致性。

     自旋锁

    自旋锁通过spinlock_t数据结构实现,基本上可使用spin_lock和spin_unlock操纵。还有其他 一些自旋锁操作:spin_lock_irqsave不仅获得自旋锁,还停用本地CPU的中断,而spin_lock_bh 则停用softIRQ(软中断)。用这两个操作获得的自旋锁必须用对应的接口释放,分别是 spin_unlock_irqsave和spin_unlock_bh。同样,自旋锁的实现也几乎完全是汇编语言

    在使用自旋锁时必须要注意下面两点。

    (1) 如果获得锁之后不释放,系统将变得不可用。所有的处理器(包括获得锁的在内),迟早需要 进入锁对应的临界区。它们会进入无限循环等待锁释放,但等不到。这产生了死锁,从名称来看,这 是个应该避免的状况。

    (2) 自旋锁决不应该长期持有,因为所有等待锁释放的处理器都处于不可用状态,无法用于其他 工作(信号量的情形有所不同)。

    由自旋锁保护的代码不能进入睡眠状态。遵守该规则不像看上去那样简单:避免直接 进入睡眠状态并不复杂,但还必须保证在自旋锁保护的代码所调用的函数也不会进入睡眠 状态!一个特定的例子是kmalloc函数。通常将立刻返回请求的内存,但在内存短缺时, 该函数可以进入睡眠状态,在第3章讨论过这一点。如果自旋锁保护的代码会分配内存, 那么该代码大多数时间可能工作完全正常,但少数情况下会造成失败。当然,这种问题很 难重现和调试。因此读者应该非常注意在自旋锁保护的代码中调用的函数,确保这些函数 在任何情况下都不会睡眠。

    自旋锁当前的持有者无法多次获得同一自旋锁!在函数调用了其他函数,而这些函数 每次都操作同一个锁时,这种约束特别重要。如果已经获得一个锁,而调用的某个函数试 图再次获得该锁,尽管当前的代码路径已经持有该锁,也同样会发生死锁——处理器等待 自身释放持有的锁,这就有得等了……

    信号量

     除了只能用于内核的互斥量之外,Linux也提供了所谓的futex(快速用户空间互斥量,fast userspace mutex),由核心态和用户状态组合而成。它为用户空间进程提供了互斥量功能。但必须确保其使用和 操作尽可能快速并高效。

     RCU机制

    RCU(read-copy-update)是一个相当新的同步机制,在内核版本2.5开发期间被添加,并且非常顺 利地被内核社区接纳。现在它的使用已经遍及内核各处。RCU的性能很好,不过对内存有一定的开销, 但大多数情况下可以忽略。这是个好事情,但好事总伴随着一些不那么好的事情。

    下面是RCU对潜在 使用者提出的一些约束。

     对共享资源的访问在大部分时间应该是只读的,写访问应该相对很少。

     在RCU保护的代码范围内,内核不能进入睡眠状态。

     受保护资源必须通过指针访问。

    RCU的原理很简单:该机制记录了指向共享数据结构的指针的所有使用者。在该结构将要改变时, 则首先创建一个副本(或一个新的实例,填充适当的内容,这没什么差别),在副本中修改。在所有 进行读访问的使用者结束对旧副本的读取之后,指针可以替换为指向新的、修改后副本的指针。请注 意,这种机制允许读写并发进行 

    内存和优化屏障 

    现代编译器和处理器试图从代码中“压榨”出每一点性能,读者当然会认为这是好事情。但类似 于每一件好事情,我们同样需要考虑其缺点(前面也有类似的情况)。一个有利于提高性能的技术是 指令重排。只要结果不变,这完全没有问题。但编译器或处理器很难判定重排的结果是否确实与代码 原本的意图匹配,特别是需要考虑副效应的时候,因此这种事情机器很自然不是人的对手。但在数据 写入I/O寄存器时,副效应是常见且必要的

    尽管锁足以确保原子性,但对编译器和处理器优化过的代码,锁不能永远保证时序正确。与竞态 条件相比,这个问题不仅影响SMP系统,也影响单处理器计算机。

    内核提供了下面几个函数,可阻止处理器和编译器进行代码重排。

     mb()、rmb()、wmb()将硬件内存屏障插入到代码流程中。rmb()是读访问内存屏障。它保证 在屏障之后发出的任何读取操作执行之前,屏障之前发出的所有读取操作都已经完成。 wmb 适用于写访问,语义与rmb类似。读者应该能猜到,mb()合并了二者的语义。

     barrier插入一个优化屏障。该指令告知编译器,保存在CPU寄存器中、在屏障之前有效的所 有内存地址,在屏障之后都将失效。本质上,这意味着编译器在屏障之前发出的读写请求完 成之前,不会处理屏障之后的任何读写请求 

    但CPU仍然可以重排时序! 

    读写锁

    上述的各个机制有一种不利情况。它们没有区分数据结构的读写访问。通常,任意数目的进程都 可以并发读取数据结构,而写访问只能限于一个进程。 因此内核提供了额外的信号量和自旋锁版本,考虑到上述因素,分别称之为读者/写者信号量和 读者/写者自旋锁

    大内核锁

    这是内核锁遗迹之一,它可以锁定整个内核,确保没有处理器在核心态并行运行。该锁称为大内 核锁(big kernel lock),通常用缩写表示,即BKL。 使用lock_kernel可锁定整个内核,对应的解锁使用unlock_kernel。 BKL的一个特性是,它的锁深度也会进行计数。这意味着在内核已经锁定时,仍然可以调用 lock_kernel。对应的解锁操作(unlock_kernel)必须调用同样的次数,以解锁内核,使其他处理 器能够进入。 尽管BKL在内核中仍然有1 000多处,但它已经是过时的概念,内核开发者废弃了对它的使用。 因为从性能和可伸缩性的角度来看,BKL简直是个灾难。新的代码决不应该使用BKL,而应该采用上 面描述的细粒度锁。尽管如此,BKL仍然会存在若干年才能最终消失。

    互斥量

    尽管信号量可用于实现互斥量的功能,信号量的通用性导致的开销通常是不必要的。因此,内核 包含了一个专用互斥量的独立实现,它们不依赖信号量。或确切地说,内核包含互斥量的两种实现。 一种是经典的互斥量,另一种是用来解决优先级反转问题的实时互斥量。我在下文会讨论这两种方法

    1.经典互斥量

    概念相当简单:如果互斥量未锁定,则count为1。锁定分为两种情况。如果只有一个进程在使用 互斥量,则count设置为0。如果互斥量被锁定,而且有进程在等待互斥量解锁(在解锁时需要唤醒等 待进程),则count为负值。这种特殊处理有助于加快代码的执行速度,因为在通常情况下,不会有进 程在互斥量上等待。

    有两种方法定义新的互斥量。

    (1) 静态互斥量可以在编译时通过使用DEFINE_MUTEX产生(不要与DECLARE_MUTEX混淆,后者是 基于信号量的互斥量)。

    (2) mutex_init在运行时动态初始化一个新的互斥量。 mutex_lock和mutex_unlock分别用于锁定和解锁互斥量。

    此外内核也提供了mutex_trylock, 该函数尝试获取互斥量。如果互斥量已经锁定,则立即返回。最后,mutex_trylock可用于检查给定 的互斥量是否锁定。

    2、实时互斥量

    实时互斥量是内核支持的另一种形式的互斥量。它们需要在编译时通过配置选项CONFIG_ RT_MUTEX显式启用。与普通的互斥量相比,它们实现了优先级继承(priority inheritance),该特性可 用于解决(或在最低限度上缓解)优先级反转的影响

    如果高优先级进程阻塞在互斥量上,该互斥量当前由低优先级 进程持有,那么进程C的优先级(在我们的例子中)临时提高到进程A的优先级。如果进程B现在开始 运行,只能得到与进程A竞争情况下的CPU时间,从而理顺了优先级的问题。

    近似的per-CPU计数器

    如果系统安装有大量CPU,计数器可能成为瓶颈:每次只有一个CPU可以修改其值;所有其他CPU 都必须等待操作结束,才能再次访问计数器。如果计数器频繁访问,则会严重影响系统性能。

    对某些计数器,没有必要时时了解其准确的数值。这种计数器的近似值与准确值,作用上没什么 差别。可以利用这种情况,引入所谓的per-CPU计数器,来加速SMP系统上计数器的操作。基本思想 如图5-1所示:计数器的准确值存储在内存中某处,准确值所在内存位置之后是一个数组,每个数组项 对应于系统中的一个CPU。

     如果一个处理器想要修改计数器的值(加上或减去某个值n),它不会直接修改计数器的值,因为 这需要防止其他的CPU访问计数器(这是一个费时的操作)。相反,所需的修改将保存到与计数器相 关的数组中特定于当前CPU的数组项。举例来说,如果计数器应该加3,那么数组中对应的数组项为 +3。如果同一个CPU在其他时间需要从计数器减去某个值(假定是5),它也不会对计数器直接操作, 而是操作数组中特定于CPU的项:将3减去5,新值为-2。任何处理器读取计数器值时,都不是完全准 确的。如果原值为15,在经过前述的操作之后应该是13,但仍然是15。如果只需要大致了解计数器的 值,13也算得上是15的一个比较好的近似了

     

     

     

    展开全文
  • 深入Linux内核架构笔记(缺页异常)

    千次阅读 2022-01-28 10:36:11
    如果进程访问 的虚拟地址空间部分尚未与页帧关联,处理器自动地引发一个缺页异常,内核必须处理此异常。这是 内存管理中最重要、最复杂的方面之一,因为必须考虑到无数的细节。例如,内核必须确定以下情况。  ...

    在实际需要某个虚拟内存区域的数据之前,虚拟和物理内存之间的关联不会建立。如果进程访问 的虚拟地址空间部分尚未与页帧关联,处理器自动地引发一个缺页异常,内核必须处理此异常。这是 内存管理中最重要、最复杂的方面之一,因为必须考虑到无数的细节。例如,内核必须确定以下情况。

     缺页异常是由于访问用户地址空间中的有效地址而引起,还是应用程序试图访问内核的受保 护区域?

     目标地址对应于某个现存的映射吗?

     获取该区域的数据,需要使用何种机制?

    缺页处理的实现因处理器的不同而有所不同。由于CPU采用了不同的内存管理概念,生成缺页异 常的细节也不太相同。因此,缺页异常的处理例程在内核代码中位于特定于体系结构的部分。

     arch/x86/kernel/entry_32.S中的一个汇编例程用作缺页异常的入口,但其立即调用了 arch/x86/mm/fault_32.c中的C例程do_page_fault。(大多数CPU对应的特定于体系结构的源代 码中,都包含一个同名例程)

     该例程需要传递两个参数:发生异常时使用中的寄存器集合提供异常原因信息的错误代码(long error_code)。目前error_code只使用了前5个比特位(0、1、2、3、4)

     

     如果地址超出用户地址空间的范围,则表明是vmalloc异常。因此该进程的页表必须与内核的主 页表中的信息同步。实际上,只有访问发生在核心态,而且该异常不是由保护错误触发时,才能允许 这样做。换句话说,错误代码的比特位2、3、0都不能设置。 内核使用辅助函数vmalloc_fault同步页表

    在结束对缺页异常的特定于体系结构的分析之后,确认异常是在允许的地址触发,内核必须确定 将所需数据读取到物理内存的适当方法。该任务委托给handle_mm_fault,它不依赖于底层体系结构, 而是在内存管理的框架下、独立于系统而实现。该函数确认在各级页目录中,通向对应于异常地址的 页表项的各个页目录项都存在。handle_pte_fault函数分析缺页异常的原因。

    如果页不在物理内存中,即!pte_present(entry),则必须区分下面3种情况。

    (1) 如果没有对应的页表项(page_none),则内核必须从头开始加载该页,对匿名映射称之为按 需分配(demand allocation),对基于文件的映射,则称之为按需调页(demand paging)。如果vm_ops 中没有注册vm_operations_struct,则不适用上述做法。在这种情况下,内核必须使用do_ anonymous_page返回一个匿名页。

    (2) 如果该页标记为不存在,而页表中保存了相关的信息,则意味着该页已经换出,因而必须从 系统的某个交换区换入(换入或按需调页)。

    (3) 非线性映射已经换出的部分不能像普通页那样换入,因为必须正确地恢复非线性关联。 pte_file函数可以检查页表项是否属于非线性映射,do_nonlinear_fault在这种情况下可用于处理 异常

     按需分配、调页

    按需分配页的工作委托给do_linear_fault,该函数定义在mm/memory.c中。在转换一些参数之 后,其余的工作委托给__do_fault

    大多数文件都使用filemap_fault读入所需数据。该函数不仅读入所需数据,还实现了预读功能, 即提前读入在未来很可能需要的页。完成该任务所需的机制会在第16章介绍,到时会更详细地讨论该 函数。目前我们只需知道,内核使用address_space对象中的信息,从后备存储器将数据读取到物理内存页 

    (1) 使用vm_area_struct->vm_file找到映射的file对象。

    (2) 在file->f_mapping中找到指向映射自身的指针。

    (3) 每个地址空间都有特定的地址空间操作,从中选择readpage方法。使用mapping->a_ops-> readpage(file, page)从文件中将数据传输到物理内存。

    如果需要写访问,内核必须区分共享和私有映射。对私有映射,必须准备页的一份副本。 

     对于没有关联到文件作为后备存储器的页,需要调用do_anonymous_page进行映射。除了无需向 页读入数据之外,该过程几乎与映射基于文件的数据没什么不同。在highmem内存域建立一个新页, 并清空其内容。接下来将页加入到进程的页表,并更新高速缓存或者MMU

    在内核和用户空间之间复制数据 

    内核经常需要从用户空间向内核空间复制数据。例如,在系统调用中通过指针间接地传递冗长的数据结构时。反过来,也有从内核空间向用户空间写数据的需求。 有两个原因,使得不能只是传递并反引用指针。

    首先,用户空间程序不能访问内核地址;

    其次, 无法保证用户空间中指针指向的虚拟内存页确实与物理内存页关联。

    因此内核需要提供几个标准函 数,以处理内核空间和用户空间之间的数据交换,并考虑到这些特殊情况 

     

     

    展开全文
  • 因而内核必须尽力同时考虑到这两种机制,确保一种方法带来的好处不会被另一种方法的不利之处抵 消,这不是件容易事。 slab缓存是一个内存到内存的缓存, 其目的不是加速对低速设备的操作,而是对现存资源进行更...

    缓存是页交换或调页操作的逆操作(换页的相关信息,在第18章讨论)。尽管缓存牺牲了物理内 存(使得不需要在块设备上进行低速操作),而实现页交换时,则是用低速的块设备来代替物理内存。 因而内核必须尽力同时考虑到这两种机制,确保一种方法带来的好处不会被另一种方法的不利之处抵 消,这不是件容易事。

    slab缓存是一个内存到内存的缓存, 其目的不是加速对低速设备的操作,而是对现存资源进行更简单、更高效的使用。dentry缓存也用于 减少对低速块设备的访问,但它无法推广到通用场合,因为它是专门用于处理单一数据类型的。

    内核为块设备提供了两种通用的缓存方案。

    (1) 页缓存(page cache)针对以页为单位的所有操作,并考虑了特定体系结构上的页长度。一个 主要的例子是许多章讨论过的内存映射技术。因为其他类型的文件访问也是基于内核中的这一技术实 现的,所以页缓存实际上负责了块设备的大部分缓存工作。

    (2) 块缓存(buffer cache)以块为操作单位。在进行I/O操作时,存取的单位是设备的各个块,而 不是整个内存页。尽管页长度对所有文件系统都是相同的,但块长度取决于特定的文件系统或其设置。 因而,块缓存必须能够处理不同长度的块

    缓存页的回写

    为此,内核同时提供了如下几个同步方案。

     几个专门的内核守护进程在后台运行,称为pdflush,它们将周期性激活,而不考虑页缓存中 当前的情况。这些守护进程扫描缓存中的页,将超出一定时间没有与底层块设备同步的页写回。 早期的内核版本对此采用了一个用户空间守护进程,称为kudpated,通常仍然使用该名称来 描述这一机制。

     pdflush的第二种运作模式是:如果缓存中修改的数据项数目在短期内显著增加,则由内核激 活pdflush。

     提供了相关的系统调用,可由用户或应用程序通知内核写回所有未同步的数据。最著名的是 sync调用,因为还有一个同名的用户空间工具,是基于该调用的

    每个地址空间都有一个“宿主”,作为其数据来源。大多数情 况下,宿主都是表示一个文件的inode。① 因为所有现存的inode都关联到其超级块(第8章讨论过), 内核只需要扫描所有超级块的链表,并跟随相关的inode,即可获得被缓存页的列表。

    通常,修改文件或其他按页缓存的对象时,只会修改页的一部分,而非全部。这在数据同步时引 起了一个问题。将整页写回到块设备是没有意义的,因为内存中该页的大部分数据仍然与块设备是同 步的。为节省时间,内核在写操作期间,将缓存中的每一页划分为较小的单位,称为缓冲区。在同步 数据时,内核可以将回写操作限制于那些实际发生了修改的较小的单位上。因而,页缓存的思想没有 受到危害。

    块缓存

    在Linux内核中,并非总使用基于页的方法来承担缓存的任务。内核的早期版本只包含了块缓存, 来加速文件操作和提高系统性能。这是来自于其他具有相同结构的类UNIX操作系统的遗产。来自于 底层块设备的块缓存在内存的缓冲区中,可以加速读写操作。其实现包含在fs/buffers.c中。 与内存页相比,块不仅比较小(大多数情况下),而且长度是可变的,依赖于使用的块设备

    地址空间

    在Linux的发展过程中,不仅缓存由面向缓冲区演化为面向页,而且与此前的Linux版本相比,将 被缓存的数据与其来源相关联的方法,也已经演变为一种更一般的方案。尽管在Linux及其他UNIX衍 生物的初期,inode是缓存数据的唯一来源,但内核现在采用了更为通用的地址空间方案,来建立缓存 数据与其来源之间的关联。尽管文件的内容构成缓存数据的一大部分,但地址空间的接口非常通用, 使得缓存也能够容纳其他来源的数据,并快速访问。

    地址空间如何融入到页缓存的结构中呢?它们实现了两个单元之间的一种转换机制。

    (1) 内存中的页分配到每个地址空间。这些页的内容可以由用户进程或内核本身使用各式各样的 方法操作。 这些数据表示了缓存的内容。

    (2) 后备存储器指定了填充地址空间中页的数据的来源。地址空间关联到处理器的虚拟地址空间, 是由处理器在虚拟内存中管理的一个区域到源设备(使用块设备)上对应位置之间的一个映射。

    如果访问了虚拟内存中的某个位置,该位置没有关联到物理内存页,内核可根据地址空间结构来 找到读取数据的来源。

     页树

    内核使用了基数树来管理与一个地址空间相关的所有页,以便尽可能降低开销。

     writepage和writepages将地址空间的一页或多页写回到底层块设备。这是通过向块层发出 一个相应的请求来完成的。内核为此提供了若干标准函数(block_write_full_page和mpage_readpage(s) );通常会使 用这些函数,而不是直接编写代码实现

    readpage和readpages从后备存储器将一页或多个连续的页读入页帧。类似于writepage和 writepages,readpage和readpages通常也不会直接编写代码实现,而是通过内核的标准函 数执行(mpage_readpage和mpage_readpages),这些标准函数可用于大多数场合。 请注意,如果使用标准函数来实现所需的功能,readpage的file参数是不需要的,因为与目 标页相关联的inode可以通过page->mapping->host确定。

     sync_page对尚未回写到后备存储器的数据进行同步。不同于writepage,该函数在块层的层 次上运作,试图将仍然保存在缓冲区中的待决写操作写入到块层。与此相反,writepage在地 址空间的层次上运作,只是将数据转发到块层,而不关注块层中的缓冲问题。 内核提供了标准函数block_sync_page,该函数获得所述页所属的地址空间映射,并“拔出” 块设备队列,开始I/O。

     set_page_dirty容许地址空间提供一个特定的方法,将一页标记为脏。但该选项很少使用。 在这种情况下,内核将自动使用__set_page_dirty_buffers,不仅将页在缓冲区层次上标记 为脏,还将该页在基数树中的标记置为脏。

     prepare_write和commit_write执行由write系统调用触发的写操作。为迎合日志文件系统 的特点,该操作必须分为两个部分:prepare_write将事务数据存储到日志,而commit_write 执行实际的写操作,向块层发送适当的命令。

    在写入数据时,内核必须确保两个函数总是成对调用,并且顺序正确,否则日志机制不能达 到其目的。

    不同于writepage,prepare_write和commit_write并不直接发起I/O操作(换句话 说,它们不向块层发送相应的命令),在标准实现中,它们只将整个页或其中部分标记为 脏。写操作由一个内核守护进程触发,该进程专用于此,会周期性地检查现存的页。

     write_begin和write_end是prepare_write和commit_write的代替物。尽管这两组函数的 作用是相同的,但所需的参数以及对涉及的对象进行锁定的方式都发生了变化。由于Documentation/filesystems/vfs.txt已经详细描述了这些函数的运作方式,这里无须赘 述。

     bmap将地址空间内的逻辑块偏移量映射为物理块号。这对块设备通常是很简单的,但组成文 件的块在设备上通常不是连续或线性的,不提供该函数则不能确定所需的信息。 页交换代码(参见18.3.3节)、文件ioctl FIBMAP,以及某些文件系统内部,都需要bmap。

     releasepage用于日志文件系统中,准备释放页。

     如果一页将要从地址空间移除,而通过PG_Private标志可判断有缓冲区与之相关,则调用 invalidatepage。

    direct_IO用于实现直接的读写访问。这绕过了块层的缓冲机制,允许应用程序非常直接地与 块设备进行通信。大型数据库会频繁使用该特性,因为与内核的通用机制相比,它们能更好 地预测未来的输入输出情况,因而通过自行实现的缓存机制,能够达到更好的效果

      get_xip_page用于就地执行(execute-in-place)机制,该机制可用于启动可执行代码,而无 须将其先加载到页缓存。这对有些场合是有用的,例如,基于内存的文件系统如RAM磁盘, 或在内存较少的小型系统上,CPU可直接寻址ROM区域包含的文件系统。因为该机制很少使 用,无须详细讨论。

     在内核想要重新定位一页时会使用migrate_page,即将一页的内容移动到另外一页。由于页 通常都带有私有数据,只是将两页对应的物理页帧的裸数据进行复制是不够的。举例来说, 支持内存热插拔就需要对页进行移动。

     launder_page在释放页之前,提供了回写脏页的最后的机会

    大多数地址空间都没有实现所有的函数,对某些函数指定了NULL指针。在许多情况下,会调用 内核的默认例程,而不是具体地址空间所提供的特定实现。

    小结

    从外部存储设备如硬盘读取数据,比从物理内存读取数据要慢得多,因此Linux使用了缓存机制 将已经读取的数据保存在物理内存中,供后续访问使用。页帧是页缓存运作的自然单位,本章讨论了 内核如何跟踪块设备的哪些部分缓存在物理内存中。本章还介绍了地址空间的概念,它用于将缓存的 数据与其来源关联起来,还讨论了地址空间的操作和查询方式。接下来,本章讲述了在将数据读入页 缓存的过程中,Linux处理相关技术细节的算法。

    传统上,UNIX缓存使用的是比整页更小的单位,该技术直至今日仍然存在,称为块缓存。虽然 现在主要的缓存工作由页缓存处理,但块缓存仍然有一些用途,因而本章介绍了块缓存的相应机制。

    使用物理内存来缓存从磁盘读取的数据,只是物理内存和磁盘交互的一方面,二者的交互还牵涉 另一个方面:内核还必须考虑将在物理内存中修改的数据,同步到磁盘上以持久存储。

    展开全文
  • 在32位架构中,进程的虚拟地址按照3:1的比例划分为内核空间和用户空间,这样划分是为了实现CPU的不同权限,而64位架构中,因为进程的虚拟地址足够大,所以没有这个划分,都是足够的,一般是1:1,高位作为内核空间,...
  • 进程在Linux中被组织为父子关系,子进程退出时,父进程需要调用wait或waitpid函数等待回收子进程的资源,否则子进程就一直以“僵尸”状态存在。1、ptrace系统调用ptrace系统调用,可以强制让一个进程成为...
  • 深入Linux内核架构》简介和概述1。
  • 1、内核架构常见架构范式:Linux内核上下层通信方式横向系统和纵向系统横向系统如cgroup,proc,sys文件系统,系统调用的组织,调试系统,Core Dump,信号,内存管理等;纵向系统是指具体的功能模块,如USB功能,一个...
  • 深入Linux内核架构》读书笔记第一章 简介和概述
  • 版本选择2.6.24从不同视角看内核任务 从应用程序视角来看,内核是一台增强的计算机...宏内核:性能优于微内核,支持热插拔,为目前linux实现方式; 内核组成:fork: 写时复制(copy on write)写入之前,共用同一页;
  • struct list_head { struct list_head *next, *prev; };
  • 深入linux内核架构-学习笔记

    千次阅读 2014-01-12 10:39:06
    1. linux内存管理中,将虚拟空间划分为两个部分:内核空间和用户空间,内核空间占用高地址空间(>TASK_SIZE),虚拟空间地址与具体的物理内存的大小无关。内核利用页表将虚拟空间与物理内存对应起来,不同的虚拟空间...
  • 深入linux内核架构读书笔记

    千次阅读 2018-08-02 10:41:47
    通过ps 可以知道在中括号内的是内核线程 系统调用可以分为六类:进程管理/信号/文件/目录和文件系统/保护机制/定时器函数 kernel中使用register_binfmt来增加支持的二进制格式 进程用exit系统调用终止,其函数是do_...
  • 由浅入深 了解驱动 内核 是个好东西 给大家分享哈
  • 0. Linux内核启动流程 1. 获取并解压内核源码 Linux内核官方网站:www.kernel.org。 本教程中使用的是3.4.2版本,比较旧,所以我放在公众号里了,请在文末关注公众号Mculover666,回复关键词“内核源码”获取。 ...
  • 深入Linux内核架构笔记 --- 第一章 简介和概述 转载于:https://www.cnblogs.com/pengdonglin137/p/4370377.html
  • Unix的历史 ①Unix诞生于1969年,至今仍然被认为是现存操作系统中最强大和最优秀的系统。 ...Linux简介 ①1991年 芬兰赫尔辛基大学的linus因不能随心所欲的使用强大的Unix系统而苦恼,于是开发了
  • Linux内核阅读笔记

    2013-06-11 11:43:35
    这是Linux内核0.11源代码的完全注释,想学习内核必不可少的文档
  • Linux内核分析

    2018-11-28 12:03:08
    该压缩包里面是多个文档,都是关于linux内核的分析和研究。
  • 笔记深入理解Linux内核(二)

    千次阅读 2021-12-01 21:33:39
    笔记深入理解Linux内核(二) 二零二一年十月二十四日 文章目录笔记深入理解Linux内核(二)第二章:内存寻址内存地址硬件中的分段段选择符和段选择器段描述符快速访问段描述符分段单元Linux中的分段Linux ...
  • <!-- @page {margin:2cm} p {margin-bottom:0.21cm} ...vmalloc能完成高端内存到内核虚拟空间的映射,但是内核更期望一种能专门正对高端内存的映射关系,持久映射 便是将高端内存长久映射到内存虚拟地址空
  • 1)内核核心代码:包括linux内核整体架构分析笔记描述的各子系统和子模块,以及其他支撑子系统,如:电源管理、linux初始化等。 2)非核心代码:例如库文件(因为 Linux 内核是一个自包含的内核,即内核不依赖其它...
  • 启动装载程序将会把内核代码拷贝到位于内存的低端地址,其边界保存变量中:  _text 和 _etext 是代码段的起始和结束地址, _etext 和 _edata 之间是数据段, _edata 和 _end 之间是初始数据段(如初始化为0的全局...
  • 今天小编就为大家分享一篇关于Linux内核设备驱动之字符设备驱动笔记整理,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
  • 内核架构》 3.2 节): //节点(简化版) typedef struct pglist_data { struct zone node_zones[MAX_NR_ZONES];//指明该节点下包含有多少个域 //指向page实例数组的指针,用于描述节点的所有物理内存...
  • 《奔跑吧Linux内核(第二版)》第一章
  • Linux内核驱动与应用开发全套学习资料
  • linux内核升级笔记

    2021-12-31 15:03:06
    但是一开始以为是linux内核版本不够,就做了升级内核版本的动作。 试一下编译内核方案升级内核。这里做笔记: #从https://www.kernel.org/ 下载内核版本,这里我下载的是 linux-4.4.296.tar.xz sudo apt-get update...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 21,863
精华内容 8,745
热门标签
关键字:

深入linux内核架构 笔记