Linux内核设计与实现 读书笔记 转
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 ...
随机推荐
- 大数据时代的数据存储,非关系型数据库MongoDB
在过去的很长一段时间中,关系型数据库(Relational Database Management System)一直是最主流的数据库解决方案,他运用真实世界中事物与关系来解释数据库中抽象的数据架构. ...
- ps闪闪发光的字 教程+自我练习
本教程的文字效果非常经典.不仅是效果出色,创作思路及制作手法都堪称完美.作者并没有直接使用纹理素材,纹理部分都是用滤镜来完成.这需要很强的综合能力,非常值得学习和借鉴.最终效果 我的: 1.创建一个新 ...
- JS简单入门教程
JS简单教程 使用方法:放到任意html页面的head标签下 Test1方法弹出当前时间对话框 Test2方法for循环输出 Test3方法for(…in…)输出数组内容 <script typ ...
- MVC用户登录方法(lamda表达式)
public bool ValidateUser(account model) { using (assertEntities db = new assertEntities()) { acc ...
- dom 学习的开始~简单留言1
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- cordova,phonegap 重力感应
3.0版本后,cordova通过插件模式实现设备API,使用CLI的plugin命令可以添加或者移除插件: $ cordova plugin add org.apache.cordova.device ...
- 实体框架 (EF) 入门 => 四、CodeFirst 枚举支持
当使用 Code First 开发时,通常是从编写用来定义概念(域)模型的 .NET Framework 类开始. 插入记录没有为 Budget 赋值. 数值类型默认值为0,数据库中都为not nul ...
- Mac下安装eclipse+python+pydev+numpy+matplotlib
*本人亲测是成功的安装过程 1.更新Mac系统默认低版本的python2.7.请参见这篇文章:http://jingyan.baidu.com/article/14bd256e39b63dbb6d26 ...
- jquery call方法和apply方法接触
call方法: 语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]]) 定义:调用一个对象的一个方法,以另一个对象替换当前对象. 说明: call 方法可以用来 ...
- Configure the Struts Tag Libraries
In Struts framework, you always need to configure the Struts tag libraries in order to access it in ...