首页 » 通讯 » 进修笔记丨《图解系统》_内存_内核

进修笔记丨《图解系统》_内存_内核

萌界大人物 2025-01-21 01:44:13 0

扫一扫用手机浏览

文章目录 [+]

冯诺伊曼体系,CPU 读写内存时 「总线」交互的流程

首先通过「地址总线」来指定内存的地址然后通过「掌握总线」掌握读或写命令末了通过「数据总线」来传输数据

位宽分「线路位宽」和「CPU」位宽

进修笔记丨《图解系统》_内存_内核 进修笔记丨《图解系统》_内存_内核 通讯

线路位宽 = cpu 能寻址最大的内存地址32 位 CPU 最大只能操作 4GB 内存,纵然装 8 GB 内存条也没用64 位 CPU 寻址范围理论最大为 2^64cpu 位宽 = cpu 能运算最大的数字长度

CPU 指令周期

进修笔记丨《图解系统》_内存_内核 进修笔记丨《图解系统》_内存_内核 通讯
(图片来自网络侵删)
掌握单元将程序计数器通过「地址总线」发给内存内存通过数据总线发回指令指令存到寄存器,程序计数器自增根据指令 = 打算还是存储,决定由打算单元还是掌握单元处理

指令周期的四个阶段

取指:掌握器从存储器拿到后放到寄存器译码:掌握器实行实行:运算器从寄存器拿到后打算回写

指令段 (正文段) 和 数据段 分别存 指令 和 数据

图中指令以汇编为样例 ,实际为二进制:下面以 MIPS 指令编码办法为例指令类型:传输 (eg. store mov)、运算、跳转、旗子暗记 (trap 中断)、闲置 (nop 空转)大多数指令不能在一个时钟周期完成,常日须要多少个时钟周期

程序实行效率

实行韶光 = CPU 时钟周期数 CPU Cycles 时钟周期韶光 Clock Cycle Time时钟周期数 = 指令数 每条指令均匀时钟周期数 Cycles Per Instruction (简称 CPI)

32位/64位

硬件 64 位和 32 位指「CPU」的位宽软件 64 位和 32 位指「指令」的位宽32 位软件兼容后可以跑在 64 位机器上,反之弗成:指令位宽超过寄存器位宽64 位机器单条指令打算更大数据,但条件是确实存在大位宽数据的需求64 位的地址总线位宽是 48,支持的寻址空间更大磁盘/内存内存数据存储在电容中,须要不断刷新对冲泄电问题查看 CPU 缓存的命令;查看 cache line 的大小

CPU 提速

内存地址和 cacheline 对照关系 「CPU」

根据地址中「索引部分」找到映射的 line根据 line「valid 部分」得知是否有效根据地址「tag 部分」和「line」的 tag 比对得知是否是须要的那个行:多个地址可映射到同一 line根据地址「偏移部分」拿到 line 里的对应数据:即 word 字

提升缓存命中率

Linux 供应 sched_setaffinity 方法支持将线程绑定在某个核心提升 cache 命中率提升数据缓存命中率:充分利用连续分布的内存 (经典两层 for 循环的例子)提升指令缓存命中率:if 预测更加准确 (先 sort 再 for 判断比反过来要好)CPU 缓存同等性

办理缓存同等性

写传播:CPU A 写的数据 CPU D 也能感知事务串行:CPU A 和 CPU B 同时在写CPU C 收到的是 A 先 B 后CPU D 收到的是 B 先 A 后那么 C 和 D 实例存的就不一样了CPU 任务实行

伪共享

由于 cache line 实例存的数据量可能是所需量的超集,两个核心分别须要地址连续的变量 a 和 b虽然两者的数据绝不干系,也涌现相互两对方设置对方的 cache line 为不生效通过 __cacheline_aligned_in_smp 可以避免伪共享用空间换韶光:强行将在 cache line 后面添补零,摧残浪费蹂躏部分缓存空间换来性能提升

进程调度

进程和线程都是用 task_struct 类,不同调度类有不同的 run queue同一「调度类」可能存在多个调度策略,例如 realtime 类有 fifo 和 rr

每个 CPU 都有自己的 run queue,详细包含三个运行行列步队Deadline 运行行列步队 dl_rqRealtime 运行行列步队 rt_rqCFS 运行行列步队 cfs_rqLinux 选择下个任务实行时,会按照此优先级顺序进行选择即先从 dl_rq 里选任务,然后从 rt_rq 里选任务,末了从 cfs_rq 里选任务

优先级和权重

任务「优先级」一样平常是指任务落到那个调度类的意思vruntime 通过 nice 值调度的叫「权重」中断关中断:不相应中断:类比为有人给你电话时其他人给你电话会由于占线打不进来硬中断/软中断,以网卡为例上半部分:中断当前实行的程序,关网卡中断,触发软中断下半部分:从内存读数据过协议栈,每个 CPU 都有个专门处理软中断的内核线程比如 ksoftirqd/0 便是 0 号 CPU 那个线程除了硬中断的下半部分,软中断还包括:内核调度等、RCU 锁等软件触发的情形查看软中断和硬终端/proc/interrupts;/proc/softirqs每个 CPU 都有自己累计的不同类型软中断的次数理论上每个 CPU 软中断次数会比较均衡

浮点数精度补码:采取补码的上风是可使得加减法操作不须要特判,像正数一样按位加即可转换:十进制整数转二进制整数的办法是除以 2,小数转二进制小数的办法是乘 2

精度问题

小数受限于单个数字的字节长度限定,无法准确表达而不得不丢失精度,比如 0.1 就无法准确表示「浮点数」:对固定字节长度来说,小数点点的位置是不固定的符号位 + 指数位 (确定小数点的位置,即尾数位向左或者向右移动几位,永久是正数,以127为界,小于127则往左移大于则往右移) + 尾数位由于精度问题终极 0.1+0.2 的结果不完备即是 0.3

操作系统

linux 设计的核心点

MultiTask 多任务:CPU 并发和并行SMP 对称多处理:每个 CPU 的行为同等且对称ELF 可实行文件链接格式:ELF 加载到内存后实行Monolithic Kernel 宏内核:所有驱动和文件系统等模块是内部的一部分,可动态加载

内核实现办法的差异

微内核是指内核只包含少量核心功能,上述模块事情在用户态上风是扩展性可插拔,劣势是用户态到内核态切换:折中是稠浊内核

Linux VS Windows

内核模式:Linux 内核采取宏内核,Window 内核采取稠浊内核可实行文件格式:Linux 可实行文件格式叫 ELF,Windows 可实行文件格式叫 PE内存管理虚拟内存

单片机没有操作系统,必须把程序烧进去运行,每次改程序都要重新烧

分段机制

分段机制下的虚拟地址由两部分组成:「段选择因子」和「段内偏移量」分段里最关键便是段表,段表里保存:段基地址、段界线、特权等级平分段机制的核心在于物理内存是按照段的大小按需分配的连续空间没有内部碎片会有外部碎片:须要 swap out 外 swap in 达到重新整理的效果

分页机制

分页的核心是页表:页表本身存储在内存中每个进程有自己的页表:进程越多页表占用空间越大;每个进程所需的页表大小上完备相同多级页表:第一级页表肯定都要存在的 (覆盖全部的地址),但后续的几级页表都可以按需加载64 位系统页表分为 4 层,都有各自的名字

Intel 为兼容历史上的「段式内存」,采取的是 [段页式内存管理] 模式

逻辑地址是「段式内存管理」转换前的地址线性地址是「页式内存管理」转换前的地址

Linux 操作系统兼容 [段页式内存管理] 模式,但实际生效的只有「页式内存管理」

系统中每个段都是从 0 地址开始的全体 4GB 虚拟空间,所有段的起始地址都一样即操作系统本身的代码和运用程序的代码,所面对的都是线性地址空间 (虚拟地址)相称于屏蔽处理器中的逻辑地址观点,段只被用于访问掌握和内存保护

我们平时常说的进程内存分布是指虚拟内存分布

查看命令

/proc/pid/maps 或者 pmap pid:查看进程用户态虚拟内存空间的实际分布cat /proc/iomem:查看进程内核态虚拟内存空间的的实际分布Mallocmalloc 是用户态函数库,申请的是「虚拟内存」而不是「物理内存」如果须要内存小于 128 KB:用 brk 系统调用申请「堆」内存否则:用 mmap 系统调用申请「匿名文件映射」内存

malloc(1) 不但分配 1 字节小于 128 KB,brk 系统调用向堆空间申请cat /proc/pid/maps 最右边有 [heap] 标识

malloc 分配内存的前 16 字节用于存当前申请的内存块的大小free 时根据改前 16 字节可以知道须要开释多少内存

brk/mmp

brk 申请:free 开释内存时不把内存归还给操作系统,而缓存在 malloc 内存池 (用户态) 待下次利用减少系统调用带来的高下文切换开销减少缺页非常次数开销mmap 申请:free 开释内存时把内存归还给操作系统,内存得到真正开释不全部用堆内存是避免堆中涌现过多碎片 (虚拟内存地址碎片)内存满操作内存满的处理步骤:后台内存回收 -> 直接内存回收 -> oom/proc/meminfo:生动/不生动 的 匿名页 (堆栈) /文件页 (磁盘数据和文件数据)

回收

回收内存都会发生磁盘 I/O,如果回收内存操作频繁则会导致磁盘 I/O 次数很多,影响系统性能swappiness 设置得越大越方向于回收文件页/proc/sys/vm/swappinesssar -B 1 可以看内存回收的情形pgscank/s:kswapd 每秒扫描的 page 个数pgscand/s:运用程序在内存申请过程中每秒直接扫描 (直接管受接收) 的 page 个数pgsteal/s: pgscank+pgscandpgscand 数值很大大概率由于直接内存回收导致

/proc/sys/vm/zone_reclaim_mode 设置 NUMA 架构下的回收策略

0 默认值:回收本地内存前,在其他 Node 探求空闲内存1:只回收本地内存2:只回收本地内存,在本地回收内存时可将脏页写回硬盘4:只回收本地内存,在本地回收内存时可用 swap 办法回收内存预读/污染

PageCache 和 BufferCache 参照如下示意图

[预读失落效] 导致缓存命中率低落通过冷热两个链表 (即 active 和 inactive) 办理预读但没用的只会放到冷链表头,热链表被淘汰的会先放到冷链表头[缓存污染] 导致缓存命中率低落提升进入热链表的门槛,访问两次以上才从 inactive 提升到 active虚拟内存管理虚拟内存地址空间不同段的 (虚拟内存) 地址存在内核数据构造中全局变量和静态变量在程序编译后也存储在二进制文件中

不同区的解释

保留区:C 措辞中将无效指针设置为 NULL,指向的便是保留区文件映射与匿名映射区:动态链接库中代码段+数据段+BSS 段;通过 mmap 系统调用映射的共享内存区栈区:start_brk 标识堆起始位置,brk 标识堆当前结束位置堆区:start_stack 标识栈起始位置,RSP 寄存器保存栈顶指针,RBP 寄存器保存栈基地址

cat /proc/pid/mapspmap pid 查看某进程的虚拟内存布局

[canonical addres] 是 64 位机器中 [内核态地址] 和 [用户态地址] 间的空洞通过前置位是否全 0 或者全 1 快速定位到地址是属于用户态还是内核态

数据构造用户进程数据构造 task_struct 里包含 mm_struct,专门记录虚拟内存信息:copy_process() -> copy_mm()新建线程的 task_struct 和父进程共享 mm_struct 指针vfork/clone新建进程的 task_struct 的 mm_struct 是父进程的 deep copyfork内核线程的 mm_struct 为 nil内核在调度内核线程时将上个线程的 mm_struct 直接复制给内核线程由于所有线程的内核虚拟地址空间完备一样,以是任意复制一个就行总结父进程/子进程的差异,进程/线程的差异,内核线程/用户态线程的差异:都环绕 mm_struct 展开

mm_structmm_struct 中 task_size 定义用户态地址空间与内核态地址空间之间的分边界

mm_struct 构造体中记录用于划分虚拟内存区域的变量 (即各个段的起始地址)

mm_struct 构造体中定义虚拟内存与物理内存映射内容干系的统计变量total_vm:进程虚拟内存空间中与物理内存映射的页总数 (不代表真正分配内存)locked_vm:被锁定不能换出的内存页总数pinned_vm:既不能换出,也不能移动的内存页总数data_vm:数据段中映射的内存页数目exec_vm:代码段中存放可实行文件的内存页数目stack_vm:栈中所映射的内存页数目

vm_area_struct每个 vm_area_struct 构造对应虚拟内存空间中的虚拟内存区域 VMA描述 [vm_start,vm_end) 左闭右开的虚拟内存区域vm_area_struct 里定义每个虚拟内存区域的权限vm_flags:定义全体虚拟内存区域的访问权限以及行为规范 (截图+理解范例的 flag)

vm_area_struct 完全的内存干系数据构造的关系图vm_area_struct 同时包含红黑树和双向链表两种组织办法,分别运用查找和遍历的场景pmap pid 是通过遍历 vma_area_struct 双向链表实现的二进制文件在磁盘中通过 section 的办法组织磁盘中的多个 section 会映射成内存中同一 segmenttext,.rodata 等只读 section 被映射到内存的一个只读可实行的代码段.data,.bss 可读写 section 会被映射到内存具有读写权限的数据段,BSS 段...

虚拟内存操作

malloc

申请小块内存 (< 128K) 用 do_brk 系统调用,调度堆 brk 指针增加或回收堆内存大块内存则调用 mmap 在虚拟内存空间中的文件映射与匿名映射区创建出一块 VMA 内存区 (即匿名映射)该匿名映射区域用 struct anon_vma 构造表示

mmap 文件映射

vm_file 属性用来关联被映射的文件,vm_pgoff 表示映射进虚拟内存中的文件内容在文件中的偏移

svm_operations_struct 中定义对虚拟内存区域 VMA 的操作函数指针;

同理,file/io 都是如此,和 interface 的思路比较类似

内核态虚拟内存空间

进入内核态后操作的仍旧是虚拟地址空间

直接映射区3G 到 3G+896m 这块 896M 大小的虚拟内存会直接映射 0 到 896M 这块 896M 大小的物理内存上虽然是直接映射,内核访问时同样还是走虚拟内存查询页表直接映射区存什么数据前 1M 在启动时被占用后面存内核代码段+数据段+BSS段:系统启动时加载从 ELF 文件到内存进程干系的数据构造进程的内核栈:固定大小放进程的调用链...动态映射区[VMALLOC_START, VMALLOC_END)通过 vmalloc 申请:其申请的虚拟内存是连续的,但是映射后的物理内存不连续 (以 page 为单位)长期映射区[PKMAP_BASE, FIXADDR_START)许可建立虚拟内存和物理内存间的永久映射:kmap固定映射区[FIXADDR_START, FIXADDR_TOP)和直接映射区类似:供应类似延迟的机制虚拟内存地址已经固定,但在内核加载过程中不想等内存管理模块加载完临时映射区将用户缓冲区的数据拷贝到 page cacheZONEZone 的划分是针对物理内存而言的,896M 以上区域被为 ZONE_HIGHMEM 即高端内存分类ZONE_DMA:直接映射区的部分,DMA 只能对物理内存的前 16M 寻址ZONE_NORMAL:直接映射区的部分,剩下的 16M-896M 是 zone_normalZONE_HIGHMEM:非直接映射区的部分,896M 以上区域空洞:ZONE_HIGHMEM 上有一段 8M 大小的内存空洞

内存物理构造

物理构造

内存 -> 存储器模块 -> DRAM 芯片 -> supercell (8bit 数据)每个 DRAM 芯片有两个 addr 引脚 (索引 supercell 的行和列),八个 data 引脚 (传输 8 bit 数据)

总线

IO Bridge 将不同总线中的电子旗子暗记相互通报

数据读写

CPU 以 word size (64 字节) 为单位从内存中读取数据,以是须要 8 个 DRAM 芯片 8 个 存储器模块

物理内存管理

虚拟内存的碎片会导致 malloc 失落败,物理内存的碎片会导致大页分配不出来

内存模型

PFN (Page Frame Number)

和表示物理页构造的 struct page 逐一对应page_to_pfnpfn_to_page 实现相互转化每个物理页的 PFN 全局唯一的:不但是其所在 NUMA 节点内唯一

平坦模型

mem_map 全局数组保存 PFN

连续内存模型

内存空间拆分成多个 node每个 node 内部用 node_mem_map,node 间用 pglist_data 组织

稀疏内存模型

将 node 的观点缩小到更小的 section物理页 = 4k 则 section = 128M;物理页 = 16k 则 section = 512M

内存热插拔

将 section 置为 online/offline 然后进行内存迁移业务进程只感知虚拟内存以是无感知像直接映射区标记为不支持热插拔即可

NUMA-概述

UMA 的问题:随着核数变多,总线带宽急急+长度变长

numa 内存分配策略

数据构造

node_data 管理所有 numa 节点:以数组办法组织每个 numa 节点用 pglist_data 管理:pglist_data 核心字段及含义如图

NUMA-Zone只有第一个 numa 可以包含 zone_dma所有 numa 都可以包含 zone_normal 和 zone_high

NUMA 节点内按照功能不同划分身分歧的内存区域内核为每个内存区域分配一个 [伙伴系统] 管理该内存区域下物理内存的分配和开释[伙伴系统] 管理的是 [物理内存] 的分配

NUMA-内存挤压高位内存区域 (内存不足时可以) 对低位内存区域进行挤压每个内存区域可按照一定的比例来打算自己的预留内存的cat /proc/sys/vm/lowmem_reserve_ratio

NUMA-内存回收每个 NUMA 节点分配一个 kswapd 进程用于回收不常常利用的页面kswapd 指向内核为 NUMA 节点分配的 kswapd 进程kswapd_wait 用于 kswapd 进程周期性回收页面时利用到的等待行列步队每个 NUMA 节点分配一个 kcompactd 进程用于内存的规整避免内存碎片kcompactd 指向内核为 NUMA 节点分配的 kcompactd 进程kcompactd_wait 用于 kcompactd 进程周期性规整内存时利用到的等待行列步队

水位线

关于更多 [文件页]/[匿名页] 的回收 (swap ...) 在后续 IO 章节展开先容

cat /proc/zoneinfo 查看 [不同 NUMA] 中 [不同内存区域] 中的水位线

NUMA-冷热页热页:已经加载进 CPU 高速缓存中的物理内存页冷页:还未加载进 CPU 高速缓存中的物理内存页

数据构造

冷热页的管理封装在 struct per_cpu_pageset 中每个 CPU 对应一个 per_cpu_pageset 构造zone 构造中的 pageset 数组包含的是系统中所有 CPU 的高速缓存页

count :凑集中包含的物理页数量如果是热页凑集,则表示加载进 CPU 高速缓存中的物理页面个数list_head list :双向链表保存当前 CPU 的热页或者冷页batch:每次批量向 CPU 高速缓存添补或者开释的物理页面个数。
high:如果 count 值超过 high,内核从高速缓存中开释 batch 页面到物理内存区域的 [伙伴系统]

内存分配

内核内存页分配两种办法

以页为单位分配利用:向相应内存区域 zone 里的 伙伴系统 申请/开释分配小块内存 (几十个字节):内核利用 slab allocator 分配器分配slab = 工具池,基本事理是从伙伴系统中申请整页内存,划分成多个大小相等的小块内存被 slab 所管理据。
进程管理进程/线程进程状态壅塞:进程在等待事宜挂起:进程由于没在实行,其状态被 swap 到磁盘中 (比如二进制)壅塞挂起:进程在外存 (硬盘) 并等待某个事宜涌现就绪挂起:进程在外存 (硬盘) 且只要进入内存即刻急速运行父子进程父进程清理退出子进程时须要清理子进程 PCB 干系数据构造进程切换须要保存内容:虚拟内存、栈、全局变量等 “用户空间资源”,内核堆栈、寄存器等 “内核空间资源”切换机遇:韶光片耗尽,等待资源,主动就寝,硬件中断,高优抢占线程切换须要保存内容:虚拟内存共享 (切换时保持不动),只需切换线程私有数据、寄存器等线程实现

用户级线程

库函数掩护:切换也由线程库函数完成 -> 无需用户态与内核态切换速率特殊快操作系统不感知:用户线程不支持抢占,壅塞后可能会导致卡住

内核线程

内核掩护:线程高下文信息,线程创建、终止和切换都通过系统调用开销比较大如果某内核线程发起系统调用而壅塞不会影响其他内核线程,内核会卖力平衡内核线程的韶光片

lwp 轻量级线程

轻量级线程实在便是用户线程,只不过用户线程可以直接映射到内核线程 (可能存在 m 对 n 的关系)

线程上限同一个进程可创建的线程上限虚拟内存大小:相同进程里的线程共享虚拟内存空间,每个线程都会独立分配栈空间 (即下图 stack-size)

系统的配置:最大线程数 && 最大 pid 数/proc/sys/kernel/threads-max:系统支持的最大线程数,默认 14553/proc/sys/kernel/pid_max:系统全局 PID 数值限定,默认 32768/proc/sys/vm/max_map_count:限定单进程可拥有 VMA (虚拟内存区域) 数量,默认 65530线程崩溃

线程崩溃时会给进程 (父线程) 发旗子暗记

线程共享虚拟内存,如果虚拟内存搅散了可能会影响其他线程,为保险不如退出如果没捕获就会导致进程退出:类似 JVM 这种自己处理旗子暗记的可能就不会退出进程通信管道事理:内核里的缓存,从管道拿着 fd 从一端写入数据后缓存在内核中,另一端拿着 fd 从内核中读取数据没人读会导致写卡主;管道数据无格式

父子:管道在父子间通信的事理是 fork 可以 copy fd为避免混乱,担保父子进程可以 (分别只留读 fd 和 写 fd) 确保到同时写入和读出

shell:通过 | 通报信息也是匿名管道,实际上便是创建多个子进程编写 shell 脚本时,能用一个管道搞定的就不要多用个管道,减少创建子进程系统开销

行列步队事理:可以通报构造化数据;生命周期一贯存在,而匿名管道的生命周期随进程参数:内核中两个 MSGMAXMSGMNB 以字节为单位分别定义一条的最大长度和一个行列步队的最大长度开销:行列步队通信中存在用户态与内核态间的数据拷贝开销进程写入数据到内核中的行列步队时,会发生从用户态拷贝数据到内核态,同理进程读也是共享内存事理:不同虚拟地址空间映射到相同物理内存开销:行列步队个管道须要用户态内核态数据拷贝,共享内存不须要

旗子暗记量/旗子暗记旗子暗记量:办理利用共享内存中的互斥问题旗子暗记:给进程的关照机制

socketsocket 走协议栈既支持同主机又支持跨主机多线程同步锁忙等待锁:即是 "自旋锁",必须合营抢占式调度 (不然不会主动 yield)无等待锁:当没获取到锁时把当前哨程放到锁的等待行列步队,实行调度程序把 CPU 让给其他线程旗子暗记量利用旗子暗记量可实现临界区的互斥访问利用旗子暗记量实现同步:a ready 了才可以做 b经典问题:生产者/消费者经典问题:哲学家就餐经典问题:读者/写者锁去世锁去世锁条件 (四元素)互斥访问 + 持有然后等待 + 不可抢占 + 环路等待去世锁检测 (工具)jstack (java) / pstack(c) 可以看到线程等锁的情形 (多次查询都在等锁大概率由于去世锁)在疑惑去世锁后通过 gdb 可以详细查看持有锁/等待锁的详细线程

锁分类根本锁互斥锁:获取失落败则主动开释 CPU,会陷入内核态做线程切换自旋锁:获取失落败则忙等,依赖内核抢占实现上,"自旋" 借助 CPU CAS 指令,"等待" 借助 CPU Pause 指令读写锁:根据唤醒机遇可分为 "读优先" (下图 1),"写优先" (下图 2),"公正"

乐不雅观/悲观锁"根本锁" 和 "读写锁" 都是 "悲观锁":所有操作前必须先加锁"乐不雅观锁" 是先操作然后在后置进行冲突检讨,范例的比如 MVCC 和 CAS阐明下:只用 CAS 是乐不雅观锁,合营 pause while 检讨的自旋锁实现则是悲观锁调度算法调度须要考虑的成分利用率;吞吐;周转韶光;等待韶光;相应韶光进程调度算法:前辈先出;短作业优先;高相应比优先;韶光片轮转;优先级;多级反馈优先级高相应比优先问题:相应比须要知道进程实例运行韶光,但是不好预估磁盘寻道算法算法:先来先做事,最短寻道韶光,电梯算法 (look 改进),循环电梯算法 (c-look 改进)

页面置换算法算法:前辈先出,最久未用(计访问时候),最不常用 (计访问次数),时钟置换 (带衰减的访问次数)页表项:有该页在磁盘上的地址 (常日是物理块号)

缺页:从磁盘加载缺页时会保存当前 CPU 状态 (内核态实行加载操作)文件系统inode vs dentryinode:物理上存储在磁盘dentry:内核掩护的数据构造 (既能表示目录又能表示文件)硬链接的实现是对同一文件 (对应唯一的 inode) 布局了多个 dentry磁盘块区超级块:在文件系统挂载时加载到内核索引块:在访问时加载到内核数据块:存储文件或目录数据进程:系统为每个进程掩护独立的打开文件表,表中的每项便是文件描述符利用:用户进程以字节为单位操作文件,系统以 块 (多个扇区组合成块) 为单位操作文件

文件存储

文件在磁盘存储办法:系统以 “块” (即多个扇区) 为单位操作文件

连续存储:将连续的块组合起来,会产生碎片,inode 记录起始块和块长度

隐式连接表:inode 记录起始块,每个块里留空间存下个块的指针图中的 "文件头" 便是就等同于 inode

显式连接表:inode 记录每个块的位置和隐式的差异是:查找第 n 个块,前者要读 n 次盘,后者是内存操作索引表:inode 记录索引块的位置,索引块里记录每个数据块的位置一级索引不足就用多级索引

实现比拟

unix (ext 2/3) 实现unix 文件系统综合了连接表和索引表,前者用在小文件,后者用在大文件存放文件所需数据块 < 10 块:直接查找 (连接表)存放文件所需数据块 > 10 块:采取一级间接索引还不足存放大文件:采取二级间接索引还不足存放大文件:采取三级间接索引上风小文件:直接查找可减少索引数据块开销大文件:多级索引的办法在访问数据块时须要大量查询空间管理空闲表 / 空闲链表:两者都不适宜大文件,由于表和链表本身的空间都很大空闲链表还须要遍历链表拿指针效率更低

位图估算每块 4K,每个数据块 1bit,共可表示 410248=2^15 空闲块每个数据块 4K,最大可表示的空间为 2^1541024=2^27 byte = 128M办理:文件系统都由大量块组组成;每块分类超级块:文件系统信息,eg. inode 总数、块总数、每块组的 inode 数、每块组的块个数 ...块组描述符:文件系统中各块组的状态,eg. 块组中空闲块和 inode 的数目 ...每个块组都包含文件系统中 [所有块组的组描述符信息]数据 / inode 位图:对应的数据块或 inode 是空闲的还是被利用中inode 列表:块组中所有的 inode数据块:文件的有用数据解释每块组有很多重复的信息:比如超级块和块组描述符表 (冗余)

目录存储目录里头的数据hash 表:当目录里的文件过多时,先哈希再遍历可增加查找的效率软/硬链接

硬链接

多个 dentry 中的 inode 指向一文件inode 不可超过文件系统:硬链接不可跨文件系统只有删除文件的所有硬链接以及源文件时,系统才会彻底删除该文件

软链接

软链接文件有独立 inode:但其内容是另一个文件的路径访问软链接时实际相称于访问到其余的文件:软链接可以跨文件系统目标文件被删除后链接文件还在,只是指向的文件再也找不到

文件 IO缓冲/非缓冲:库函数自身的缓存直接/非直接:内核的缓存,差异是 "用户态 <-> 内核态" 是否发生数据交流而 "内核态 -> 磁盘" 的数据更新是异步的同步 IO壅塞:“磁盘->内核态” 和 "内核态->用户态" 都须要同步壅塞非壅塞:“磁盘 -> 内核态“ 立时返回靠轮询,“内核态->用户态“ 仍旧同步壅塞多路复用:可以在一个线程里等待多个 socket,壅塞情形和非壅塞一样异步 IO:两步都是异步的

Page Cache进程写文件然后奔溃,文件内容不会丢,由于会有 page cachepagecache 由内核掩护;/proc/meminfo 可查看其信息page-cache = buffers + cached + swap-cached

pagecache 有预读机制

swappiness参数范围是 0-100高数值:较高频率的 swap,进程不生动时主动将其转换出物理内存低数值:较低频率的 swap,确保交互式不由于内存空间频繁地交流到磁盘而提高相应延迟VS buffer-cachepage-cache 缓存文件的页数据,buffer-cache 缓存块设备的块数据页是逻辑观点:page-cache 与 "文件系统" 同级块是物理观点:buffer-cache 与 "块设备驱动程序" 同级低版本内核中 pagecache 和 buffer 是完备正交的这样会导致数据被缓存两次 (以 1k block 和 4k page 为例)新版本内核中 pagecache 也包含了 buffer 的语义

脏页回写pagecache 回写干系系统调fsync(fd):将 fd 文件的 [脏数据] 和 [脏元数据] 全刷新至磁盘fdatasync(fd):将 fd 文件的 [脏数据] (和必要的 [元数据]) 刷新至磁盘例如:文件大小是必要的,文件修正韶光是不必要的sync():将所有脏文件的 [元数据] 刷新至磁盘脏页回写时运用程序主动调用 (上述) 回系统调用管理线程周期性唤醒设备回写线程某些运用程序/内核任务创造内存不敷,而回收部分缓存页面回写线程管理线程全局唯一,刷脏页线程内核设备一个管理线程监控设备脏页面情形:一段韶光没有脏页就销毁设备的刷新线程;若监测有脏页面则创建刷新线程设备管理IO 掌握流程CPU -> DMA:奉告从哪儿读到哪儿DMA -> 磁盘掌握器: 同上磁盘掌握器 -> 内存:发送数据磁盘掌握器 -> DMA:发送旗子暗记DMA 产生中断

设备层级"设备掌握器" 属于硬件"设备驱动程序" 属于操作系统:"设备驱动程序初" 始化的时候须要注册 "中断处理程序"

通用块层

处于 “文件系统” 和 “磁盘驱动程序” 之间用场:供应标准接口 + IO 要求的排队和调度

IO 分层架构

文件系统层+通用块层+设备层ioctl:设备输入输出掌握接口,配置和修正特定设备的属性缓存:文件系统有缓存 (pagecache/inode) + 块设备也有缓存 (设备缓冲区)

设备硬件设备缓冲区:设备 (磁盘) 掌握器从键盘里写,设备 (磁盘) 驱动程序读CPU 收到中断后由 CPU 调用中断处理程序分类块设备:可寻址,硬盘/USB字符设备:不可寻址,鼠标块设备的数据传输量较大,以是须要缓冲区,字符设备常日只须要寄存器就够 (不须要数据缓冲区)

网络管理零拷贝

零拷贝的范例利用系统:kafka/nginx

带 DMA 和 不带 DMA:磁盘缓冲区到内核缓冲区的拷贝是不是 CPU 做的

带 DMA 的读操作:两次系统调用 + 四次高下文切换 + 四次数据拷贝

实现: mmp

[四次] 高下文切换+ [两次] 系统调用+ [三次] 数据拷贝 (内核态 内核缓冲区->网卡缓冲区)

实现: sendfile

[两次] 高下文切换 + [一次] 系统调用 + [两次] 数据拷贝 (无需 CPU 参与/都是 DMA)

page-cache/IO 模式

所谓 “内核缓冲区” 便是 page-cache 作为缓存;page-cache 还有预读功能

在传输大数据文件时 page-cache 反而有负向影响:零拷贝也没用绕开 pagecache 便是 "直接 IO";利用 pagecache 便是 "缓存 IO"磁盘的 “异步 IO” 只支持 “直接 IO”"直接 IO" 的用场用户程序已经实现用户态缓存大文件传输;page-cache 只会让热点失落效"直接 IO" 的劣势:绕过 page-cache预读逻辑失落效内核 IO 调度算法本可以合并多次 IO 要求IO 多路复用socketsocket 数据构造中有两个 sk_buff 工具:收行列步队和发行列步队sk_bff 里的指针 data 随协议栈变革而逐渐指向对应层的内容

影响单机连接数的成分:内存,文件描述符数,IP/端口数,网络 IO 模型 ...

多进程父进程卖力管理连接 (listen),子进程卖力读写

多线程父进程卖力管理连接 (listen),子线程卖力读写子线程通过 “行列步队“ + ”线程池” 减少线程 创建/删除 开销

select/poll两次遍历 (内核态/用户态各一次) 两次列表传输select 用 bitmap 固定大小,poll 用链表 变终年夜小通报的内容都是 [文件描述符凑集]epollepoll 基本操作create:创建红黑树ctrl:向红黑树添加文件描述符节点wait:内核用就绪的节点布局序列,拷贝到用户态高性能网络模式reactor 模式

IO 多路复用监听事宜,收到事宜后根据事宜类型分配给 进程/线程

单 reactor 单进程/线程模块:reactor 卖力 select+dispatch;acceptor 卖力 accept;handler 卖力 read+send缺陷:无法充分利用 CPU;某个连接的 handler 卡主可能影响别的连接案例:redis 是该方案,都是内存操作性能不在 CPU

单 reactor 多线程模块: handler 仍旧卖力 read/send缺陷:数据处理过程由独立线程处理,线程数据处理须要加锁 (效率并不高)

单 reactor 多进程须要处理父子进程间的数据通信一样平常不用:实现难度繁芜多 reactor 多进程/线程模块:主线程卖力吸收连接,子线程卖力后续的数据事宜;两者都有 select比拟:单 reactor 须要处理所有事宜的监听和相应,随意马虎成为性能瓶颈案例:netty/memcache

proactor 模式IO 分类同步壅塞 IO:等待内核数据和内核到用户拷贝数据非壅塞 IO:等待内核到用户拷贝数据异步:两者都不等待reactor:非壅塞同步;selector 收到事宜后须要主动调 readproactor:异步proactor 基于内核能力linux 是用户态仿照的且只支本地文件不支持网络windows 是内核实现的

网络命令网络配置:ifconfig/ipsocket:netstat/ss网络吞吐 pps:sar网络联通和延时:ping同等性 Hash

引入虚拟节点的上风:节点越多越随意马虎均衡数据;单点下线故障域越小

标签:

相关文章

进修笔记丨《图解系统》_内存_内核

冯诺伊曼体系,CPU 读写内存时 「总线」交互的流程首先通过「地址总线」来指定内存的地址然后通过「掌握总线」掌握读或写命令末了通过...

通讯 2025-01-21 阅读 评论0