这里记录 跟从 “奔跑吧Linux内核”视频学习的简要笔记,
2019/2/11
奔跑2.0.2 内存管理总览二
32位cpu的虚拟内存空间是4G,一般0~3G是用户空间,3~4G是内核空间。当然也可以把用户空间和内核空间设为2G:2G(为什么默认会设为3G:1G呢?)。
物理内存分为高端映射和线性映射。线性映射,把地址直接映射到3G开始虚拟内存空间,物理内存地址+偏移量=虚拟内存地址;高端映射(High Memory), 动态映射到1G空间。
Page的structure十分重要;Mem_map[]数组是用来存放每一个page struct数据,通过这个数据结构,可以方便地通过page找到页帧号(page frame number);除了page以外,还有一个主要的数据结构Zone用来管理内存,zone内存区域,对应到物理内存的高端映射是Zone High,对应到线性映射是Zone Normal。
用户空间的1G~3G是mmap段,0~1G是堆空间和数据段/代码段;
OS一般用PCB(Process Control Block)来描述进程相关的信息,在Linux中,使用task_struct来描述进程。mm_struct是其重要的成员变量,他又包含2个重要的成员变量mmap和pgd。
mmap指向vma的进程链表,进程地址空间采用vma来管理,内核采用链表和红黑树来管理进程的内存。
pgd是进程进程所在的页表,进程的一级页表在fork的时候创建,进程的二级页表在使用的时候才会动态创建;
从进程管理的角度讲述内存管理的概貌图。
2019/2/10
奔跑2.0.1 内存管理总览一
内存空间分为 用户空间 和 内核空间。
用户空间调用的内存管理函数,到了内核空间都是一一对应的,如malloc对应sys_brk,mmap对应sys_mmap等。
这些内存管理函数申请的地址空间都是在虚拟地址空间的,这一部分是由vma管理。在linux内核中,这样的区域被称之为虚拟内存区域(virtual memory areas),简称vma。一个vma就是一块连续的线性地址空间的抽象,它拥有自身的权限(可读,可写,可执行等等) ,每一个虚拟内存区域都由一个相关的struct vm_area_struct结构来描述。
vma返回的地址还是虚拟地址,用户进程是无法写入的,这个时候就需要“缺页中断”来和物理内存建立关系。
缺页中断会产生2中类型的内存,一是匿名页面(没有关联文件,如malloc),另外一种是page cache(有关联具体文件的内存,如视频播放器去观看一个视频文件,这时系统读取视频文件会产生缓存,这些缓存就是page cache类型的)。
然后通过“页面分配器”和伙伴系统来分配页面。如果物理内存十分充足,则页面分配器十分容易分配页面;但是如果内存十分紧张,则需要涉及到OOM/内存规整等其他技术。分配好页面后,下面来分配页表。
内存相关的硬件则对应MMU/TLB/Cache/物理内存。
页面分配是以页为单位,但是如果一些进程只需要几个byte的内存,就会造成浪费,linux引入slab来解决这个问题。slab是Linux操作系统的一种内存分配机制。其工作是针对一些经常分配并释放的对象,如进程描述符等,这些对象的大小一般比较小,如果直接采用伙伴系统来进行分配和释放,不仅会造成大量的内存碎片,而且处理速度也太慢。而slab分配器是基于对象进行管理的,相同类型的对象归为一类(如进程描述符就是一类),每当要申请这样一个对象,slab分配器就从一个slab列表中分配一个这样大小的单元出去,而当要释放时,将其重新保存在该列表中,而不是直接返回给伙伴系统,从而避免这些内碎片。slab分配器并不丢弃已分配的对象,而是释放并把它们保存在内存中。当以后又要请求新的对象时,就可以从内存直接获取而不用重复初始化。
如果内存不够,则需要启动页面回收机制。对于匿名页面,需要写入swap分区(swap out),需要的时候再次载入(swap in);对于page cache,脏数据则需要写入文件(write back)。
反向映射,是指找到哪些Virtual Address使用了这个页面,以方便页面回收的时候进行相关的操作。在Linux内存管理器中,页表保持对进程使用的内存物理页的追踪,它们将虚拟页映射到物理页。有些页可能不是长时间使用,它们应该被交换出去。但是在交换出去之前,我们首先要做的就是要更新使用该交换页的每1个进程的页表项。为了更新进程页表项,以往为了确定某个要回收的物理页面都被哪些页表项引用,必须要遍历所有进程,这是1项非常耗资源和时间的工程。所以引入反向映射(RMAP)机制,使得更加有效地回收1个共享页面,反向映射技术的实现主要是基于页表项链表。
KSM是合并内容相同的匿名页面。2.6.32引入了KSM(Kernel Samepage Merging)允许这个系统管理程序通过合并内存页面来增加并发虚拟机的数量。VMware 的 ESX 服务器系统管理程序将这个特性命名为 Transparent Page Sharing (TPS),而 XEN 将其称为 MemoryCoW。不管采用哪种名称和实现,这个特性都提供了更好的内存利用率,从而允许操作系统(KVM 的系统管理程序)过量使用内存,支持更多的应用程序或 VM。
Huge Page主要用于服务器,手机系统则很少使用。huge page分配2M或以上的大小页面(一般的page大小是4k)。好处是减少TLB miss的次数。
页迁移,可以迁移一个进程的内存到指定的节点。在很多的内核模块都有使用,如内存规整和内存热插拔,都有用到页迁移。
内存规整是为了解决内存碎片化。
OOM killer,内存不足的时候,必要的时候会起到OOM Killer,杀掉某些进程。
2019/2/9
奔跑2.0.0 内存管理硬件知识
早期内存使用直接是访问物理地址,这会造成一下问题,
1. 一个程序的内存空间,容易被其他程序篡改;甚至内核的内存空间也可能被恶意程序篡改;
2. 内存的使用效率不高。
因此,后面也就有了“内存分段”的概念,来解决问题1;“内存分页”则用来引入“虚拟内存”的概念,来解决问题2.
“虚拟内存”和“物理内存”是相互映射的,通过硬件MMU来管理。
下面举例来说明,
比如,CPU要访问某一个数据,会先通过MMU来看看是否可以在“一级缓存”中存在,如果“一级缓存”没有,则看看“二级缓存”;
如果缓存中没有要访问的数据(TLB没有命中),则访问实际的“物理内存”;
如果此时数据因为一些原因被放到“交换分区swap”,则需要访问磁盘。
2019/2/8
序言2.3 搭建Eclipse图形化调试内核
这部分是在“序言2.2 搭建Qemu加gdb单步调试内核”基础之上,加入了Eclipse可视化,这样调试内核代码就像调试应用程序一样方便,可以查看栈的信息,以及各种数据结构。
大致方法如下,
1. 安装 "sudo apt-get install eclipse-cdt", 这个是eclipse c/c++的环境;
2. 新建一个项目,并且配置,这个可以参考 笨叔叔 的视频,要点如下,
2.1 点选 run->Debug Configurations.., 选择C/C++ Attach to Application, 选中“New_configuration”
2.1.1 "Name"可以输入自己喜欢的,如“linux”,
2.1.2 "Main"选项中,“Project”输入自己喜欢的,如“linux-4.0”, 然后通过“Browse”选择之前编译好的"vmlinux"文件。
2.1.3 "Debugger"页面中,“Debugger”选中“gdbserver”; "Main"页面下, 在“gdb debugger”中,输入“arm-none-eabi-gdb”;“Connection”页面下,“port number”更改为“1234”.
然后选择“Apply”-》“Debug”, 至此配置完成。
3. 新开一个terminal,输入“qemu-system-arm -M vexpress-a9 -smp 4 -m 1024M -kernel arch/arm/boot/zImage -append "rdinit=/linuxrc console=ttyAMA0 loglevel=8" -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic -S -s”
4. 然后在Eclipse中,在debug中选择刚刚配置的linux选项
4.1 然后在“console”中,输入“file vmlinux”
4.2 设置断点“b do_fork”
4.3 输入“c”继续调试
然后就可以看到局部变量等信息。
2019/2/7
序言2.2 搭建Qemu加gdb单步调试内核
1. Install the package
sudo apt-get install qemu libncurses5-dev gcc-arm-linux-gnueabi build-essential gdb-arm-none-eabi gcc-aarch64-linux-gnu eclipse-cdt git
2.
https://github.com/figozhang/runninglinuxkernel_4.0.git3. Follow below step, to build kernel image and running it in the qemu,
本实验是在Ubuntu 16.04上完成的,使用其他版本的Linux发行版遇到编译问题请自行解决了。
$ sudo apt-get install qemu libncurses5-dev gcc-arm-linux-gnueabi build-essential gdb-arm-none-eabi gcc-aarch64-linux-gnu eclipse-cdt libdw-dev systemtap systemtap-runtime
编译ARM32内核:
$ export ARCH=arm
$ export CROSS_COMPILE=arm-linux-gnueabi-
$ cd _install_arm32/dev/
$ sudo mknod console c 5 1 (注意,不要遗漏该步骤)
$ cd runninglinuxkernel_4.0
$ make vexpress_defconfig (在runninglinuxkernel_4.0目录下输入make命令)
$ make bzImage –j4
$ make dtbs
运行ARM32内核:
$ qemu-system-arm -M vexpress-a9 -smp 4 -m 1024M -kernel arch/arm/boot/zImage -append "rdinit=/linuxrc console=ttyAMA0 loglevel=8" -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic
4. gdb debu
4.1 running below command in one terminal,
$ qemu-system-arm -M vexpress-a9 -smp 4 -m 1024M -kernel arch/arm/boot/zImage -append "rdinit=/linuxrc console=ttyAMA0 loglevel=8" -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic -S -s
4.2 running below command in another terminal, input 'c' to continue, input
$arm-none-eabi-gdb --tui vmlinux
4.3 Connect with qemu with gdb, input command 'target remote localhost:1234'
4.4 Set break points, for example 'b do_fork'
4.5 type 'c' to coninue
4.6 type 's' to debug step by step
2019/2/5
git入门与实战8:实战rebase到最新的代码
git入门与实战10:如何制作和管理多个patch
2019/2/2
git入门与实战5:提交更改
1. 修改最近一次提交,首先修改文件,然后使用git add,之后使用git commit --amend
2. 修改最近一个提交的作者,git commit -amend -author "xx@xx.com"
3. git reset,
3.1 --soft, 将HEAD引用指向给定的提交,索引和工作目录的内容保持不变;
3.2 --mixed,将HEAD引用指向给定的提交,索引跟着变,工作目录保持不变;
3.3 --hard, 将HEAD引用指向给定的提交,同时索引和工作目录都会改变
4. git revert, 撤销某个提交,如git revert master~3
5. git cherry-pick, 从某个git tree或者分支上摘取一个commit到当前分支, 如git cherry-pick dev~3
git入门与实战6:远程仓库
1. git remote命令
1.1 git remote命令可以添加一个远程仓库本地中
# git remote add ben 远程仓库地址
1.2 git remote show xxx,可以查看这个xx远程版本库的所有信息
1.3 git remote update 抓取远程版本库中所有可用更新到本地库里
1.4 git fetch xx, 抓取远程xx版本库到本地
2. 添加远程分支
# git push <远程主机名> <本地分支名>:<远程分支名>
3. 删除远程分支名
# git push <远程主机名> :<远程分支名>
# git push <远程主机名> -d <远程分支名>
git入门与实战7:实战git和内核开发与管理
用到的命令
git --bare init
git clone
git commit
git remote add [注意:一个本地库里面,可以包含多个远程库]
git remote -v
git fetch
git reset
git merge
git push
git入门与实战9:如何给Linux内核社区发补丁
三部曲,
1. 发现缺陷,
2. 制作补丁,$ git format-patch -1
3. 发布补丁
3.1 安装git-email, $ sudo apt-get install git-email
3.2 配置send-email, 修改~/.gitconfig文件
3.3 获取维护者的名字
$ ./scrpts/git_maintainer.pl your_fix.patch
3.4 发送补丁
$ git send-email --to "xx@xx.com" 0001-your-fix-patch.patch
2019/2/1
git入门三:分支管理
1. 创建分支
$ git branch 分支名
$ git branch ben/dev (斜杠创建一个分层的命名)
2. 查看分支
$ git branch -a
$ git show-branch -a
3. 切换分支
$ git checkout 分支名
$ git checkout -b 分支名 (创建新分支并切换到新分支上)
4. 删除分支
$ git branch -D 分支名(删除新分支)
5. 合并分支
$ git merge xxx: 将xxx分支合并到当前分支,所有提交按照顺序
$ git rebase xxx: 新的提交基于在xxx分支之上
git入门实战4:冲突解决
1. 使用git merge命令进行合并分支,把dev分支合并到master上
1.1 git merge dev
1.2 查看冲突,vim test.c,手工修改冲突
1.3 添加修改文件,git add test.c
1.4 使用git merge --continue继续下一个冲突的地方
2. 使用git rebase命令进行合并分支,把dev分支合并到master
2.1 git rebase dev
2.2 查看冲突,vim test.c,然后手工修改冲突
2.3 添加修改文件, git add test.c
2.4 使用git rebase --continue继续下一个冲突的地方
3. 从dev分支最新commit生成一个patch发给master分支,并在master分支上打上这个patch
3.1 git am xxx.patch发生冲突的时候
3.2 $ git am --show-current-patch查看当前的patch
$ git apply PATCH --reject
git add fixed_files
git am --resolved
2019/1/31
实战运维8:Linux内存管理参数调优(三)
这一节视频主要介绍了/sys/kernel/mm目录下面的参数调优
1. /sys/kernel/mm/hugepages
以前一个page大小是4kb,对于现在的系统而言太小了,会造成频繁的缺页中断,所以才有hugepages
2. 修改hugepages大小,需要编辑/etc/default/grub文件
3. /sys/kernel/mm/transparent_hugepage, 透明巨页(起到什么作用?)
4. ksm内存调优, pages_to_scan, sleep_milisecs, merge_across_nodes
5. OOM Killer
5.1 /proc/<pid>/oom_score_adj: -1000~100, -1000表示不会被oom killer选中。
5.2 /proc/<pid>/oom_adj: -17~15, 值越小越不会被oom killer选中。
git入门与实战2:快速入门
1. 查看git log
1.1 使用哈希ID值,绝对提交名
1.2 符号引用,HEAD指向当前分支最近的提交
1.3 相对提交,比如master表示master分支的head,master^表示master分支倒数第二个commit;master~12..master~10表示master分支倒数第11和第10个提交。
2. git diff
2.1 git diff, 显示尚未提交的改动
2.2 git diff --cached, 查看已经缓存的改动
2.3 git diff HEAD, 查看所有的改动
2.4 git diff --stat, 查看摘要
3. git status
分为3种,tracked, un-tracked, ignored
4. git add:添加文件或者文件夹
5. git rm:删除文件或者文件夹
6. git mv: 移动或者重新命名文件
7. git commit
git commit --amend: 提交修改
(如果是要修改某个文件,则需要使用git add xxx.c,再使用git commit --amend)
2019/1/28
git入门一:基本用法
git的主要特点是分布式,开发成员可以选择把代码保存到本地或者server,也可以和team member之间相互sync。
常用命令,
1. git log
git log --oneline #比git log显示更加简洁,一行显示一个commit
git log --author=Linus --oneline # 只显示author为Linus的commit
git log --patch-with-stat # 显示更详细的提交内容,如详细的更改内容。
2. 提交一个commit
a. 修改源代码
b. git diff查看修改文件的差异
c. git status查看哪些文件被修改了
d. git add添加修改文件
e. git commit提交修改到本地仓库
3. 本地建一个git repo
3.1 git服务器端
a. git --bare init命令创建一个空的远程仓库
3.2 client端
a. git init创建本地的仓库
b. git add添加文件
c. git commit生成一个新的提交
d. git remote add命令来添加刚才远程仓库的地址
e. git push origin master推送到远程仓库
2019/1/22
实战运维7:Linux内存管理参数调优(二)
下面的参数位于/proc/sys/vm
1. “cat min_free_kbytes”, min水位的值,大小和zone有关系,越大的话,会造成系统越早触及low水位;
2. “cat lowmem_reserve_ratio”, 为每个zone预留的内存,cat /proc/zoneinfo可以看到protection;先从DMA32先分配内存,然后从DMA;
3. “cat admin_reserve_kbyte”:为root用户预留的内存;
4. “cat user_reserve_kbytes”:为普通用户预留的内存;
5. “cat panic_on_oom”: 为1的时候,发生Out Of Memory时,系统会panic;一般设置为0;
6. “cat oom_dump_tasks”: dump被oom杀掉的task信息;
7. “cat oom_kill_allocating_task”: 设为0则表示oom会选取耗内存高的task杀掉;
8. “cat overcommit_memory”:是否允许内存过量分配;
9. “cat overcommit_kbytes”:overcommit_memory设为2才有用,和overcommit_ratio二选一;
10. “cat overcommit_ratio”
11. “cat block_dump”
12. "echo 1 > compact_memory": 让零散的内存整片化;
13. “cat laptop_mode”:
14. "cat legacy_va_layout"
15. "cat max_map_count"
16. "cat mmap_min_addr":最小的虚拟地址
17. “cat page-cluster”:
18. "cat percpu_pagelist_fraction"
19. "cat stat_interval"
2019/1/21
实战运维4 - 读懂proc meminfo
1. “cat /proc/meminfo”, 这个节点十分普通,普通的不起眼,但是仔细看下去,里面很多细项都有很多知识点。以后要好好详细研究。特别提一下,内存分配的函数malloc(), kmalloc(), vmalloc(), 这3个函数有哪些差异?
2. “cat /proc/zoneinfo”,内存分配有node,node下面有zone,zone下面有page。
3. “cat /proc/pagetypeinfo”,里面含有page相关的内存信息。page block, 2的9次方大小,每个block有512pages。
4. "cat /proc/slabinfo",读取slab相关的信息,但是没有slabtop方便看。
5. "cat /proc/iomem", 查看以memory map为映射的方式,如ARM,会占用内存空间。
6. "cat /proc/ioport",查看以io map为映射的方式,如x86架构。
实战运维5 - Linux运维能力进阶图
Linux运维可以划分不同能力等级,
1. 入门级别,可以安装系统,部署服务,会使用简单的工具;
2. 初级的,可以读懂kernel log,使用top/vmstat等工具,读懂meminfo等常用的内核信息,会调节内核参数;
3. 中级,可以熟练使用debug工具,rkdump,systemtap,内存泄漏查找;读懂内核出错信息,了解内存管理/进程管理等。
4. 高级,就像Linuis本人类似的能力,读懂大部分内核代码,知道内核有哪些不足并能够提供方案。
实战运维6 - Linux内存管理参数调优(一)
1. "/proc/sys/vm"
page cache是什么?磁盘和CPU速度严重不匹配,怎么办呢?方法有,比如在磁盘端增加cache,还有在内存中增加page, cache,cpu端L1/L2/L3 cache。文件会先存放在page cache中,之后再sync到磁盘;何时sync则会影响到系统性能。
常用参数,
dirty_background
dirty_radio:脏数据超过这个比率时,就会flush。
dirty_backgournd_ratio: 和dirty_radio相比,类似,但是write不会被阻塞(异步)。
dirty_bytes: 超过dirty_bytes size后,则flush;和diryt_radio二选一;
dirty_expire_centisecs: 超过多次时间则被回写。
dirty_writeback_centisecs:多次时间被唤醒查看是否需要flush(单位10ms)
drop_cache: 更改减小page cache大小。不会drop脏的page,如果脏数据比较多,可以先使用sync命令。
vfs_cache_pressure: 回收inode和文件系统的cache。
swappiness:值越高,越倾向写入swap磁盘;如果设为0,则是尽量使用page cache。
2. "/sys/kernel/mm"
2019/1/20
实战运维2 - 查看内存信息工具(二),
1. 工具"vmstat". proc,有r(运行状态)和b(阻塞状态)的进程数目;memory,有swapd(交换分区),free,buff,cache;还有swap,io,system,cpu。可以查看哪个模块出了问题。
2. “cat /proc/slabinfo”工具,不过需要了解比较专业slab的知识;
3. “slabtop”工具, 比较容易了解slab的相关信息。
4. “pmap”工具,查看具体一个process的内存状态,anon匿名页面,查看分配了多少内存。
实战运维3 - 读懂内核log中的内存管理信息,
1. "dmesg", 打印kernel log。参数如-T,-d,等。
2. proc和sys文件系统。/proc/meminfo, /proc/cpuinfo可以查看内存和cpu相关的信息;sys系统则是为设备模型所引入。
2019/1/18
实战运维1 - 查看内存信息工具(一),
1. 从可视化工具“System Monitor”来查看内存状态。分为Memory和Swap(交换分区),可以查看已经使用和总的内存空间。
2. 使用"free" 命令。可以查看Memory和Swap的如下项目,如total, used, free, shared, buff/cache, avaiable. 别看他简单,特别有用哦。
3. "top"命令。可以查看cpu和内存使用状况,需要好好研究。