1 用户模式
1.1 系统调用
strace -T -o output.log ./test.sh 1 2 3 # 输出程序执行调用的系统调用,-T在行末显示每个系统调用执行的时间,单位是秒
sar -P ALL 2 3 # 输出cpu核心使用情况,ALL表示所有cpu,2表示每2秒输出一次,总共采集3次
%system表示cpu在内核态运行的时间百分比
1.2 系统调用的包装函数
高级编程语言无法直接发起系统调用,系统调用是由汇编语言发起的,汇编语言与架构有关,为与架构解耦,有了对系统调用包装的包装函数提供统一接口,使架构对使用者透明,包装函数可发起系统调用。
1.3 C标准库
C对linux提供的语言库通常以glibc使用,其包含了系统调用的包装函数
ldd可查看程序依赖的c语言库,很多命令程序都依赖c语言库
1.4 os提供的程序
2 进程管理
2.1 fork
可基于现有进程分出一个新进程,流程如下
为新进程申请内存,然后将父进程内存复制到子进程内存,然后父进程分裂成两个进程。fork返回值对父进程和子进程的返回值不同,可以此区分是父进程还是子进程。对父进程fork返回子进程id,对子进程fork返回0
2.2 execve
可用来启动一个进程,运行流程如下
启动进程将读取程序内存映射信息,然后将新进程数据根据映射信息映射覆盖原进程内存,然后从入口点执行程序
linux可执行文件结构遵从名为ELF的格式,ELF信息可通过命令readelf获取
-h表示获取程序起始地址,-S可获取代码段和数据段在文件中偏移量、大小和起始地址
起sleep进程,查看进程maps,发现maps打的地址和readelf -S /bin/sleep的地址信息一致(虚拟机发现对不上)
创新进程一般用fork and exec方式,即由父进程fork,再由子进程exec
3 内存管理
常用命令 free、 sar -r
3.1 内存不足
内存不足时内存管理系统会选择合适进程kill来获取可用内存,此法不稳定,可通过设置vm.panic_on_omm=1,该作用是omm时直接关闭系统
3.2 虚拟内存
使内存访问不是直接访问物理内存,而是有个页表,实现虚拟内存地址到物理内存地址的映射,这样直接访问的就是虚拟内存地址而不是物理内存。
cat /proc/pid/maps输出的也是虚拟内存
3.3 页表
虚拟内存以页为单位对内存进行管理。页表大小取决于cpu架构,x86架构页大小为4KB
如果访问虚拟内存中没映射的虚拟内存地址,会触发缺页中断,会由缺页中断系统处理,一般会强制进程结束运行。
创建进程或动态分配内存时,linux内存分配方法为请求分页,以页为单位进行内存申请,申请后在页表中对内存进行映射
3.4 利用上层进行内存分配
用C语言malloc分配内存时,linux实际调用了底层的mmap()函数。mmap以页为单位获取内存,malloc以字节为单位获取内存。mmap获取到的内存会作为内存池,获取到后,malloc从内存池中按字节取内存。这回导致linux统计内存比程序统计内存消耗可能要高,因为linux算的是mmap整个池的大小,程序算的是池中分配的大小,没分配的可能不计算
3.5 内存碎片化
虚拟内存通过页表将虚拟内存地址和物理内存进行映射,通过页表管理了碎片化的物理内存
3.6 其他区域内存
内核内存也会映射到虚拟内存地址,但用户无法直接访问内核内存,因为内核内存在页表项中注明内核专用,会导致用户态无法直接访问内核内存,同时,进程也无法访问其他进程的内存
3.7 虚拟内存其他功能
3.7.1 文件映射
读文件时,先调mmap分配内存,再将文件内容从外部存储器映射到分配的内存
3.7.2 请求分页
直接用mmap分配内存会有内存浪费现象,即mmap分配的内存可能有的内存用的很少或基本不会用到,可用请求分页解决
请求分页是mmap分配内存时不会真实分配物理内存,只有当对页表的某个页表项真实访问时才分配物理内存,流程大概如下
未访问页表项时,页表分配了进程虚拟内存地址但其未与物理内存映射,当访问虚拟地址时,会触发缺页中断,中断处理机构此时为虚拟地址分配物理内存,更新页表。所以mmap调用成功应该表示成功获取虚拟内存。所以创建进程后内存可能不会变太多,等到访问时会分配更多。可通过sar -B查看缺页中断情况
32位电脑虚拟内存地址为4G,64位位128T
3.7.3 写时复制
fork时复制的不是实际内存,而是父进程页表,页表复制出来后会禁止表内所有地址写入权限。进程fork出来还没分配物理内存,访问时才分配。写入数据时因没写入权限会触发缺页中断处理,处理逻辑会将要写入的内存复制一份,然后更新页表将新复制的内存地址更新到子进程页表,然后将写入的物理地址在页表中重新赋予写入权限,然后开始写入
3.7.4 swap
swap可将外部存储空间一部分当作内存空间使用,这部分空间叫交换分区(swap)。当没物理内存时,新进程又需要分配内存时,内核会将一部分内存保存到交换分区腾出内存供新进程使用。加入过一段时间内存够了,不需要swap了,此时再次访问交换出去的内存地址时,内核会将swap内存重新放到系统内存。内核怎么选择哪块内存换出去还不清楚。交换以页位单位,也可以叫页面换出与换入
缺点:外存访问速度慢,如果频繁换入换出速度因外存写入速度慢导致访问速度下降
优点:扩大内存
sar -W可查看内存交换情况
3.7.5 多级页表
64位CPU虚拟地址128T,若页表加载全部虚拟地址页表占内存会很大,所以有多级页表解决这问题
多级页表实际没有映射,实际映射由最底层页表实现所以假设内存全部用到,多级页表占内存比单级页表多,但实际不会所有虚拟地址都占满,一般情况多级页表占内存比单级少
3.7.6 标准大页
为解决fork速度慢,有了标准大页。慢是因为fork时复制父进程页表,页表也占一定内存,大页增大了页大小,页表项就会减少,内存就会减少,从而提高fork速度
如何使用:可通过给调用mmap传入MAP_HUGETLB实现,或为现有程序开启大页开关
透明大页:虚拟空间存在多个4K连续页时,自动将其合并为一个大页,当不满足条件时会分解大页,如果频繁合并分解会降低性能
如何实现透明大页:改此文件即可 /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never
4 存储
存储层次:外存 -> 内存 -> 高速缓存 -> 寄存器
4.1 高速缓存
内存和寄存器间读写时,提高速度,内存与高速缓存通,高速缓存再与寄存器通。
当写数据时,寄存器先写给高速缓存,高速缓存再写给内存。当高速缓存还没写给内存时,此时高速缓存的这块内容被改写,会加个标记表示此处内容被改写,通常成为此块缓存脏了,脏数据写到内存后会清楚脏标记
如果高速缓存空间不够导致频繁换入换出将引起抖动降低性能,需考虑此情形触发条件与解决方法
4.2 多级缓存
类似于页表,高速缓存也有多级。缓存信息可从/sys/devices/system/cpu/cpu0/cache/index查看
4.3 转译后备缓冲区
也叫快表、页表缓冲、TLB。访问内存时会将虚拟地址转化为物理地址,当用高速缓存时,地址转化成为瓶颈,快表是解决此瓶颈的。快表作用是缓存虚拟地址和物理地址的转化结果,作用也类似于缓存,空间换时间
4.4 页面缓存
内存和外存访问存在速度差,为缓解速度差,有了页面缓存
作用,优缺点和高速缓存类似,也有抖动现象,也能提升性能
4.5 同步写入
还存在脏页情况下,系统忽然断电,开电后怎么处理?怎么解决这现象?可在调用open系统调用时传入flag O_SYNC,这会让给页面缓存写数据时,同步给外存写数据
用dd命令时,加oflag=direct表示不给页面缓存写数据,直接给外存写
sar -d -p 1 1可显示每个外存储器IO情况
4.6 调优参数
脏页回写周期可通过修改sysctl vm.dirty_writeback_centisecs,单位是0.01秒
vm.dirty_background_ritio 单位百分比 表示脏页内存量与系统内存搭载量百分比,到了这个百分比开始对脏页回写
vm.dirty_background_bytes 同上,单位不同
vm.dirty_ratio 脏页内存占比达到此百分比时停止脏页回写,当占比下降继续写入
vm.dirty_bytes 同上,单位不同
4.7 超线程
将一个cpu模拟成两个cpu
/sys/devices/system/cpu/cpuID/topology/thread_siblings_list可查看哪几个超线程cpu是成对的
time make -j8 > /dev/null 2>&1 # 没用超线程
time make -j16 > /dev/null 2>&1 # 用了超线程
开超线程应该比不开快,make命令是构建
5 文件系统
5.1 容量限制
为避免某一文件过大导致磁盘空间占用过多,可对每个分区设置磁盘配额,表示每个分区最多可占多大。配额有几种类别:
1 用户配额:限制某个目录下用户创建文件总大小ext4和XFS均可设置
2 目录配额:限制某个目录下总大小上线 ext4和XFS均可设置
3 子卷配额:限制卷最大占比 Btrfs可设该配额
5.2 文件系统不一致
背景 mv文件流程:
mv /home/usera /home/common_user/usera:流程是先新增新链接,再删除旧链接,完成mv
问题:忽然断电了咋整
解决:日志(ext4和XFS使用)和写时复制(Btrfs使用)
日志:操作前先在日志写操作步骤,如果日志没写完断电,则开电不操作;如果日志写完了,正在mv断电,则开电后按日志重来一次即可
写时复制:ext4等文件更新时在文件原地址更新,Btrfs将更新的文件写在和源文件地址不同的位置。还是上面的例子,写时复制流程如下
1 创建新文件夹
2 删除文件夹
如果在1和2间断电,则上电后删除旧链接即可。注意,和ext不同的是,Btrfs创建新文件夹,ext创建的是链接,链接重了断电上电不知道哪个新旧,文件夹创了断电上电可以区分
问题 如果日志和写时复制还防不住咋整
方法 1 fsck(fsck.ext4/xfs_repair/btrfs check)
功能:检查文件系统文件不一致的地方,恢复不一致。若文件过多时间会很长。处理方法是不一致的元数据统一删除。这会导致删不一致元数据时可能删多若不想方法1可以定期备份
5.3 文件种类
分普通文件和设备文件,可用ls -l /dev查看。设备有主和次设备号。文件开头c表示字符设备,b表示块设备。两个设备号分别为主,次设备号
5.3.1 字符设备
可进行读写,但无法确定设备字符位置
例子:键盘、鼠标
实验:ps -aux|grep bash可看到用户当前终端设备文件,为字符设备;对其输入字符,会在终端输出。一般应该不会直接这么操作
5.3.2 块设备
和字符设备不同的是,可对字符随机访问,如HDD和SSD。
访问方式:一般先挂载,然后通过文件系统访问,也有情况需直接访问块设备:更新分区表、块设备数据备份与还原、创文件系统、挂载、fsck等
实验:先分个空盘,然后挂载,然后在挂载目录创个文件,strings -t x /dev/xxx查看块设备,可看到挂载目录下创的文件和内容信息,然后直接给块设备操作,写入几个字,再strings xxx可看到块设备新增内容
5.4 基于内存的文件系统
5.4.1 tmpfs
挂载在内存的文件系统,不许访问外存,可提高访问性能。常被挂载到/tmp, /var/run这种一次性地方
free的shared表示tmpfs挂载的内存量
5.4.2 NFS
5.5 虚拟文件系统
5.5.1 procfs
常挂载在/proc下,可访问/proc/pid获取进程信息
maps: 进程内存映射
cmdline:进程命令行参数
stat:进程状态,如cpu时间、优先级、内存使用量
/proc下也有部分有用信息:
cpuinfo:cpu相关信息
diskstat:外存相关信息
meminfo:内存相关信息
sys:系统各种调优参数,和sysctl和/etc/sysctl.conf中调优参数一一对应
ps、sar、top、free等命令的信息都是从procfs采的
5.5.2 sysfs
因为procfs有点乱,有了sysfs。常被挂载在/sys下。主要目录:/sys/devies, /sys/fs
5.5.3 cgroupfs
进程资源量分配相关,常挂载于/sys/fs/cgroup。常被用于docker等paas场景
5.6 Btrfs
5.6.1 多物理卷
ext4和XFS需为每个分区初始化,创文件系统(也叫格式化),Btrfs不需要。
还可对存储池直接增删外存,虽然可能导致系统容量变化,但无需调整文件系统大小,且可以在已挂载状态下进行,也无需卸载文件系统
5.6.2 快照
Btrfs创快照可以子卷(分区)为单位创。和ext4不同的是,不需要复制所有数据,只需复制元数据,回写快照内脏页即可,所以时间快,占空间小
5.6.3 RAID
Btrfs支持RAID 0、1、5、10、6
配RAID时,不以子卷为单位,而以整个Btrfs为单位
5.6.4 数据损坏检测与恢复
Btrfs可检测损坏数据,如果配了RAID,还可能修复,而ext4数据损坏了可能修不了
Btrfs可检测基于校验和,检测数据和元数据间完整性,如果读数据时校验出错会丢此此部分数据并发请求通知上层出问题了
6 外存
通用块层是将驱动程序中公共部分代码提取组成的一个东西
6.1 IO调度器
属于通用块层,将磁盘读写请求合并和排序,合理规划磁盘磁头运动,减少读写时间
6.2 预读
读完一个区将紧邻的后面块读出,如果下次访问的就是预读的这些块,则无需读取,减少因读取的耗时