Linux内核设计与实现 读书笔记:
http://www.cnblogs.com/wang_yb/tag/linux-kernel/
http://blog.csdn.net/yrj/article/category/718110
第一篇 内存的测量
2.1. 系统当前可用内存
- # cat /proc/meminfo
MemTotal: 8063544 kB
MemFree: 900952 kB
Buffers: 1183596 kB
Cached: 1596808 kB
MemTotal:总共可用物理内存
Buffers:主要是用来给 Linux 系统中块设备做缓冲区
Cached:用来缓冲我们所打开的文件(Linux 的思想是,如果内存充足,不用白不用,它会使用内存来 cache 一些文件,从而加快进程的运行速度;当内存不足时,这些内存又会被回收,供程序使用。)
所以真正可用的内存 = MemFree + Buffers + Cached
被用掉的物理内存 = MemTotal - 真正可用的内存
- # ls /proc
- 1 1139 1536 1832 2042 2089 2151 2257 2647 313 4 4407 63 901 bus iomem modules sysrq-trigger
- 10 12 1541 1833 2043 2090 2155 22806 26820 32 40 47 6996 902 cgroups ioports mounts sysvipc
- 1003 1263 1661 1861 2045 2098 2172 22961 27 323 41 48 7 932 cmdline irq mtrr timer_list
- # ls /proc/1139
- attr cmdline environ io maps mountstats oom_score sched stack syscall
auxv coredump_filter exe latency mem net pagemap schedstat stat task
cgroup cpuset fd limits mountinfo numa_maps personality sessionid statm wchan
clear_refs cwd fdinfo loginuid mounts oom_adj root smaps status - # cat status
数字代表着一个个目录;同时这些数字也与当前系统中运行进程的 PID 一一对应。在这些目录下面的文件,记录着这些进程在 Linux 内核中相应的数据。其中status记录了一些比较有用的信息,而oom_adj是OOM机制的重要参考。
2.2. 虚拟内存与物理内存
malloc只是分配了虚拟内存,kernel 不会分配物理页面给进程。
strcpy一类操作时,进程需要使用这块内存了,kernel 会产生一个页故障,从而为系统分配一个物理页面。
kernel 分配物理内存的最小单位为一个物理页面,一个物理页面为(4K Byte)
- # cat statm
- 345 87 74 1 0 58 0
这里有 7 个数,它们以页(4K)为单位。
Size (total pages,345) 任务虚拟地址空间的大小
Resident(pages,87) 应用程序正在使用的物理内存的大小
Shared(pages,74) 共享页数
Trs(pages,1) 程序所拥有的可执行虚拟内存的大小
Lrs(pages,0) 被映像到任务的虚拟内存空间的库的大小
Drs(pages,58) 程序数据段和用户态的栈的大小
dt(pages,0) 脏页数量(已经修改的物理页面),这个数一些系统可能修改成直接返回0了
其中Size Trs Lrs Drs对应于进程的虚拟内存,Resident shared dr对应于物理内存
- #cat maps
- 00008000-00009000 r-xp 00000000 1f:12 288 /mnt/msc_int0/hello // 该段内存地址对应于进程的代码段,4k
- 00010000-00011000 rw-p 00000000 1f:12 288 /mnt/msc_int0/hello // 该段内存地址对应与进程的数据段,4k
- 00011000-00032000 rwxp 00011000 00:00 0 // heap,132k
- 40000000-40002000 rw-p 40000000 00:00 0 //
- 41000000-41017000 r-xp 00000000 1f:0d 817360 /lib/ld-2.3.3.so
- 4101e000-41020000 rw-p 00016000 1f:0d 817360 /lib/ld-2.3.3.so
- 41028000-41120000 r-xp 00000000 1f:0d 817593 /lib/libc-2.3.3.so
- 41120000-41128000 ---p 000f8000 1f:0d 817593 /lib/libc-2.3.3.so
- 41128000-41129000 r--p 000f8000 1f:0d 817593 /lib/libc-2.3.3.so
- 41129000-4112c000 rw-p 000f9000 1f:0d 817593 /lib/libc-2.3.3.so
- 4112c000-4112e000 rw-p 4112c000 00:00 0
- befeb000-bf000000 rwxp befeb000 00:00 0 // stack, 84K
00008000-00009000 r-xp 00000000 1f:12 288 /mnt/msc_int0/hello分别对应:
内存段虚拟地址起始,权限,偏移量,映射文件的主设备号和次设备号,映射文件节点号,映像文件的路径。
我们可以通过 cat /proc/devices 来查看指定映射文件的主设备号的设备信息。
32位操作系统每个进程4G虚拟内存,由上图可知:0x00000000-0xbfffffff,用户空间3G;0xc0000000-0xffffffff,内核空间1G
实际物理内存如下:
- #cat memmap // 这个命令不一定每个linux版本都有,没有的话可能要自己写驱动
- 2 // 对应cat maps第一行,也就是进程的代码段
1 // 对应cat maps第二行,也就是进程的数据段
100000000000000000000000000000000 // 33个字符,对应132k内存,仅第一页实际使用了
11
4949494949494949491649494949494949494949494949
11
4917171717111617171717171717174949 0 049 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 049 0 0 0
0 0 0 0 0 0 0 044 042424242 0 0 0 041 0 0 0 0 0 0 0 0 0 0 0 0 0 04847 0 0 0 0 0 0 0
04747484949494949494949 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 049 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 04849 0 0 0 04849 0 0 047 049 0 0 0 0 0 0 0 0 0 0 04949 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 046 0 0 0 0 0 0 0 0 0 0 0 0 0 044 0
000000000
00000000
49
111
11
000000000000000000011
每个数字对应内存的一个页面(4k),如果为0,表示该页面空闲。其中代码段系统共享,数据段,堆栈是每个进程私有的。
2.3 关于交换分区和内存回收
之所以移动设备不采用交换分区:
1)交换分区会导致整体性能会下降很多
2)嵌入式设备一般使用 Flash 做为存储介质, Flash 写的次数是有限的。而如果在 Flash 上面建立交换分区的话,必然导致对 Flash 的频繁写,进而影响 Flash 的寿命
内存回收机制:
在 Linux 物理内存,每个页面有一个 dirty 的标志,如果该页面被改写了,我们称之为 dirty page。非dirty page都可以回收。也就是说,一个进程的代码段全部可以回收,数据段视情况而定,堆栈实际分配的物理内存均不能回收。
第二章 内存的优化
2.4 进程
一个进程运行时,所占的内存除了堆栈外,还包括:
全局变量、静态变量:初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。(数据段)
文字常量:常量字符串就是放在这里的,程序结束后由系统释放。(代码段)
程序代码:存放函数体的二进制代码。(代码段)
堆段:
malloc、free、new、delete 是我们从程序的角度去看待堆内存;而在 Linux 内核中会专门为进程分配一段内存地址,用来存放堆的内容;随着进程申请内存的增加,进程会通过系统调用brk,来让Linux内核扩展这段内存空间,从而分配给进程更多的内存;当进程释放内存时,进程又会通过系统调用brk,来告诉内核缩减这段内存空间,Linux 内核便会将其一部分物理内存进行回收。
由于用户态进程申请内存是以字节为单位,而在内核中内存的管理是以页面(4K)为单位;如何减少brk次数成为提升系统性能的一个关键。
堆内分配内存最少为16个字节,以8位对齐,也就是说16位,24位,32位,以此类推。
char* p;
p = malloc(20);
那么,实际分配的大小会写在:*(p-4),具体内存分布为:
*(p-5): prev_size
*(p-4): size
*(p-3): 标志位
*(p-2): M值。M=1表示该内存块通过mmap来分配,只有在分配大块内存时,才采用mmap的方式,那么在释放时会的munmap_chunk()去释放;否则,释放时由chunk_free()完成。实际上在glibc的内存管理中,采用brk的方式,只能管理1G 地址空间以下的内存,如果大于了1G,glibc 将采用mmap的方式,为堆申请一块内存。brk用于更改堆顶地址,而mmap则为进程分配一块虚拟地址空间。
*(p-1): P值。P=0,表示上一块空闲,这时prev_size通常为上一块的大小;P=1,表示上一块正在被使用,这时prev_size通常为0。
为了提高速度,glibc的malloc实现了fastbins,可以通过mallopt(M_MXFAST, value)来设置其伐值。对于所有小于M_MXFAST阀值的小块内存释放时,不会去尝试合并,还会被复用;大于M_MXFAST的会尝试合并和复用。M_MXFAST阀值设为0相当于禁用fastbins功能,也就是说会一直尝试合并和复用内存。总体来说,M_MXFAST对于内存使用的影响没有一个定论,但它肯定会加快进行的运行速度。
大块内存分配:
大块内存分配以后,通过查看 maps 文件,你可以发现增加一段线性区(也就是多了一行)。其权限为 rw-p。与进程缺省堆内存的权限rwxp是不同的。
一些阀值的设置方式也是调用mallopt。取值分别为M_MMAP_THRESHOLD、M_MMAP_MAX。
M_MMAP_THRESHOLD
libc中大块内存阀值,大于该阀值的内存申请,内存管理器将使用mmap系统调用申请内存;如果小于该阀值的内存申请,内存管理其使用brk系统调用来扩展堆顶指针。该阀值缺省值为128kB。
M_MMAP_MAX
该进程中最多使用mmap分配地址段的数量。设为0表示该进程禁用mmap功能。默认值为65536。
内存释放:
在libc中,只有当堆顶有连续的128k空闲内存时,libc才会掉用brk,来通知内核释放这段内存,将其返还给系统。这个默认的128k触发条件可以通过mallopt修改M_TRIM_THRESHOLD来改变。
M_TRIM_THRESHOLD
堆顶内存回收阀值,当堆顶连续空闲内存数量大于该阀值时,libc的内存管理其将调用系统调用brk,来调整堆顶地址,释放内存。该值缺省为128k。
M_TOP_PAD
该参数决定了,当libc内存管理器调用brk释放内存时,堆顶还需要保留的空闲内存数量。该值缺省为 0.
内存空洞:
其实是由于linux内存分配释放机制导致的。只要堆顶没释放,下面的释放将不会实质上真正释放。
应对策略:1)调低mmap阀值,用mmap,代价是,会多次调用系统调用(mmap和unmmap),使进程整体性能下降。
2)尽量优先释放堆顶内存。代码习惯问题,比较难以彻底解决。
内存空洞和内存泄漏是有区别的,后者会一直增加,前者增加到一定地步趋于稳定。
2.4 内存的跟踪
使用 mtrace 检测内存泄漏
1、 引入头文件 #include
2、 在需要跟踪的程序中需要包含头文件,而且在main()函数的最开始包含一个函数调用:mtrace()。由于在main 函数的最开头调用了mtrace(),所以该进程后面的一切分配和释放内存的操作都可以由mtrace来跟踪和分析。
3、 在运行进程前定义一个环境变量,用来指示一个文件。该文件用来输出 log 信息。如下的例子:
$ export MALLOC_TRACE=mymemory.log
4、 正常运行程序。此时程序中的关于内存分配和释放的操作都可以记录下来。
Linux内核设计与实现 读书笔记 转的更多相关文章
-
Linux内核设计与实现 读书笔记
第三章 进程管理 1. fork系统调用从内核返回两次: 一次返回到子进程,一次返回到父进程 2. task_struct结构是用slab分配器分配的,2.6以前的是放在内核栈的栈底的:所有进程的ta ...
-
Linux内核设计与实现读书笔记(8)-内核同步方法【转】
转自:http://blog.chinaunix.net/uid-10469829-id-2953001.html 1.原子操作可以保证指令以原子的方式执行——执行过程不被打断.内核提供了两组原子操作 ...
-
Linux内核设计与实现——读书笔记2:进程管理
1.进程: (1)处于执行期的程序,但不止是代码,还包括各种程序运行时所需的资源,实际上进程是正在执行的 程序的实时结果. (2)程序的本身并不是进程,进程是处于执行期的程序及其相关资源的总称. (3 ...
-
Linux内核设计与实现——读书笔记1:内核简介
内核:有的时候被称管理者或者操作系统核心,通常内核负责响应中断的中断服务程序, 负责管理多个进程从而分享处理器时间的调度程序,负责管理进程地址空间德内存管理程序 和网络,进程间通信等系统服务程序共同组 ...
-
初探内核之《Linux内核设计与实现》笔记上
内核简介 本篇简单介绍内核相关的基本概念. 主要内容: 单内核和微内核 内核版本号 1. 单内核和微内核 原理 优势 劣势 单内核 整个内核都在一个大内核地址空间上运行. 1. 简单.2. 高效 ...
-
Linux内核架构与底层--读书笔记
linux中管道符"|"的作用 命令格式:命令A|命令B,即命令1的正确输出作为命令B的操作对象(下图应用别人的图片) 1. 例如: ps aux | grep "tes ...
-
Linux内核设计第十七章笔记
第十七章 设备与模块 关于设备驱动和设备管理,四种内核成分 设备类型:在所有unix系统中为了统一普通设备的操作所采用的分类 模块:Linux内核中用于按需加载和卸载目标代码的机制 内核对象:内核数据 ...
-
Linux内核分析 一二章读书笔记
第一章 Linux内核简介 1.Unix (1)Unix系统很简洁 (2)在Unix中,所以东西都被当作文件对待,通过一套相同的系统调用接口来进行:open(),read(),write(),lsee ...
-
《Linux内核设计与实现》笔记-1-linux内核简单介绍
一.Linux内核相对于传统的UNIX内核的比較: (1):Linux支持动态内核模块. 虽然Linux内核也是总体式结构,但是同意在须要的时候动态哦卸除(rmmod xxx)和载入内核模块(insm ...
随机推荐
-
字符串操作 replace
"hello".charAt(0); // "h" "hello, world".replace("hello", &q ...
-
require.js 入门笔记
网站越来越庞大,JS也是越写越多. 当所有的JS 都集中在 HTML的 head 部分时,网页加载变得很慢,很多的 JS代码也并不是全都适用在当前的页面,造成了代码的冗余度非常高. 而且长长的JS代码 ...
-
java中HashMap详解
HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接口的常用实现类,HashSet 是 Set 接口的常用实 ...
- js中关于arguments
-
POJ 3304 Segments (直线和线段相交判断)
Segments Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 7739 Accepted: 2316 Descript ...
-
5.5 Function类型
说起来ECMAScript中上面最有意思,我想那莫过于函数了,有意思的根源,则在于函数实际上是对象.每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法.由于函数是对象,因此 ...
-
你必知必会的SQL面试题
写在前面的话 本文参考原博<走向面试之数据库基础:一.你必知必会的SQL语句练习-Part 1>和<走向面试之数据库基础:一.你必知必会的SQL语句练习-Part 2>进行练习 ...
-
“浅入浅出”函数防抖(debounce)与节流(throttle)
函数防抖与节流是日常开发中经常用到的技巧,也是前端面试中的常客,但是发现自己工作一年多了,要么直接复用已有的代码或工具,要么抄袭<JS高级程序设计>书中所述"函数节流" ...
-
lumion实例渲染6.2
放置一些树 打开室外材质库,选择一个·别墅 放车 “景观”“描绘系统”笔刷调大刷出地面, 人群安置一些墙 改变方向,增大数量,墙就连在一起, 放一些树篱在墙上. 拍照模式添加特效,太阳 添加特效,天气 ...
-
【转】Java学习---集合框架那些事
[原文]https://www.toutiao.com/i6593220692525711885/ Arraylist 与 LinkedList 异同 1. 是否保证线程安全: ArrayList 和 ...