1《unix 操作系统教程》课程实验报告实验名称 Linux 内存管理实验实验序号 5 姓 名系院专业班 级学 号 实验日期 2012.11.28 指导教师成 绩一、实验目的1. 通过在Linux 环境下对内存管理的基本操作,感性认识Linux 如何对内存进行管理。
2. 利用readelf 和objdump 观测 linux 下的内存地址映射过程以及进程的虚拟地址空间。
二、实验内容与要求(1)按照实验内容完成实验操作步骤,学习内存管理中的一些常用命令 (2)理解linux 中逻辑地址、线性地址的概念。
(3)提交实验报告。
三、实验设备地点: 实验实训中心A4-2 设备:计算机一台 linux 操作系统2四、实验步骤与测试实验一free 命令显示显示内存的使用情况(使用的和空闲的),包括物理内存、交换区内存、内核缓冲区内存。
不包括共享内存。
free 命令默认选项为-k 语法:free [-bkmotV] [-s <间隔秒数>]选项介绍: -b: 以Byte 为单位显示内存使用情况; -k: 以KB 为单位显示内存使用情况; -m: 以MB 为单位显示内存使用情况; -o: 不显示缓冲区调节列;-s<间隔秒数>: 每间隔指定时间执行一次free 命令; -t: 显示内存总和列;-V: 显示版本信息;(1)free -k: 以KB 为单位显示内存使用情况;解释:total: 内存总量: 3355508(k) used: 已经使用的内存量: 490664(k) free: 空闲的内存量: 2864844(k)shared: 当前已经废弃不用,总量是0(k) buffers: 25164(k)Buffer Cache 内存量: 263480(k)cached: Page Cache 内存量: 21436(k)(2)free –m -s 5:以M 为单位,5秒显示以下内存信息解释:以上为每隔5秒显示内存信息,由以上图可知:两次内存使用情况没有变化。
(3)free -o: 不显示缓冲区调节列;解释:由以上可知Buffer Cache 这一列没有显示出来。
(4)free -t: 显示内存总和列;解释:如上所示,内存总和是3355508(k),内存已使用为491408(k),内存空闲为2864100(k)。
(5)free -V: 显示版本信息解释:版本号为3.2.72.swapon –s:显示交换区的使用状况解释:交换区名为:/dev/sda3,类型为partition,大小为425712,已使用为0,优先级为-1。
3. vmstat是Virtual Meomory Statistics(虚拟内存统计)的缩写,可对操作系统的虚拟内存、进程、CPU活动进行监视。
它是对系统的整体情况进行统计,不足之处是无法对某个进程进行深入分析。
解释:vmstat [-V] [-n] [delay [count]]其中,-V表示打印出版本信息;-n表示在周期性循环输出时,输出的头部信息仅显示一次;delay是两次输出之间的延迟时间;count是指按照这个时间间隔统计的次数。
对于vmstat输出各字段的含义,可运行man vmstat查看。
下面给出了各个参数的不同含义:procs:r-->在运行队列中等待的进程数b-->在等待io的进程数w-->可以进入运行队列但被替换的进程memoyswap-->现时可用的交换内存(k表示)free-->空闲的内存(k表示)buff-->被用来做为缓存的内存数,单位:KBcache-->被用来做为文件读写缓存的内存数,单位:KBswapsi-->从磁盘交换到内存的交换页数量,单位:KB/秒so-->从内存交换到磁盘的交换页数量,单位:KB/秒IObi-->磁盘块入34bo-->磁盘块出 Systemin-->每秒的中断数,包括时钟中断 cs-->每秒的环境(上下文)切换次数 CPU按 CPU 的总使用百分比来显示 us-->用户进程使用的时间 sy-->系统进程使用的时间 id-->cpu 空闲的时间 pagesre-->回收的页面mf-->非严重错误的页面 pi-->进入页面数(k 表示) po-->出页面数(k 表示) fr-->空余的页面数(k 表示)de-->提前读入的页面中的未命中数 sr-->通过时钟算法扫描的页面 disk 显示每秒的磁盘操作。
s 表示scsi 盘,0表示盘号 fault 显示每秒的中断数 in-->设备中断 sy-->系统中断 cy-->cpu 交换实验二1.搭建linux 实验平台,并在搭建好的平台中用vi 编辑器编写一个简单C 程序:1. /*test.c */2.3. #include <stdio.h>4.5. int global_data = 4;6.7. int global_data_2; 8.9. int main(int argc, char **argv) 10. 11. { 12.13. int local_data = 3; 14.515. printf("HelloWorldn"); 16.17. printf("global_data= %dn", global_data); 18.19. printf("global_data_2= %dn", global_data_2); 20.21. printf("local_data= %dn", local_data); 22.23. return (0); 24. 25. }2. 编译: $gcc -o test test.cA.查看ELF 的头。
刚生成的二进制就是我们要查看的目标。
从ELF 的头开始吧: $readelf -h test 实验结果:可执行文件是可以在Intel x86 32 bit 的体系的机器上运行的(从“machine”和“class”字段)。
当执行时,程序将从虚地址0x080482c0(看“Entry point address”)开始运行。
这个地址不是指向我们常见的main()函数地址的,但是它指向是一个名为__start 的函数。
__start 函数是被linker 创建的,它的目标是初始你的程序。
6这个程序还有28个节区(section)和7个段(segment)什么是节区(section)? Section 是在目标文件中的一个区,它包括一些信息(这些信息对连接过程有用):程序的代码、程序的数据(变量、数组、字符串),可重定向的信息和其它。
所以,在每一个区,几种信息组合在一起,这里有一个明显地含义:代码区只有代码,数据区只是初始化的或是没有初始化的数据,等等。
节区头部分列表(Section Header Table,SHT )精确地告诉我们:ELF 目标文件中有什么section 。
至少从“Number of section headers”字段中知道“test”目标文件有28个section.如果section 是一个二进制表示的,linux 内核不能用一种方式读懂它,linux 内核准备几个VMA (Virtual Memory Area ),它们包括虚拟地址连续的页面帧。
在VMA 的内部,一个或多个section 被映射其中。
在这个例子中每一个VMA 都代表一个ELF 的段(segment)。
那内核是如何知道哪个section 去往哪个segment 呢?这是Program Header Table(PHT)的工作。
B.查看Section Header Table(SHT)让我们看一个Section 在程序中的存在形式: $ readelf -S test 实验结果:7编译器把可执行代码保存到.text 节区中。
那.text 节区被标记为可执行('X'在flag 字段)。
在这个节区,你可以看到我们main()函数的机器代码。
$ objdump -d -j.text test 实验结果:-d 选项告诉objdump 分解机器代码。
-j 告诉objdump 只关心那个特定的节区(在本例中,是.text )。
以下是执行命令后的部分内容。
.data 节区保存所有的初始化的变量,这些变量不在栈中。
“Initialized”是指这些变量被赋于初始值,如”global_data”。
那”local_data”呢?“local_data”的值不在此节区中,它们生活在进程的栈里。
以下是用objdump 查看.data 节区:8$ objdump -d -j.data test 实验结果:C.查看 program Header Table(PHT)段(segment)是一个OS“看懂”我们程序的方法。
让我们看看我们程序是如何变成段的吧: $readelf -l test 实验结果:实验结果解释:映射很直观。
例如段号2,这里有15个节区被映射到其中。
.text 节区就映射到此段。
它的标志是R,E 其含义分别是可读,可执行。
W 就是可读的含义。
可以使用/proc/<pid>/maps 文件也可以得看到它。
<pid>是一个我们想要查看的进程的ID 。
, test 进程运行的太快了,在进入/proc 这前,它就结束了。
可以使用gdb 来解决此问题。
也可以在return 之前调用 sleep()来搞定这个问题。
9在另一个控制台中: $ gdb test (gdb) b mainBreakpoint 1 at 0x8048376 (gdb) rBreakpoint 1, 0x08048376 in main ()在此保持(hold)住,打开另一个控制台,找到test 的PID 。
如果你想图省事的话,就这样: $ cat /proc/`pgrep test`/maps 实验结果:回到gdb ,输入: (gdb) q10于是,最后,我们看到了12个段(实际上是VMA )。
重点关注第一个字段和最后一字段。
第一字段显示了VMA 的地址范围,最后一个字段显示了背后的文件。
你在看到VMA 的第8行与之前PHT 的第2行的类似点了吗?不同之处是SHT 说它自己于0x080484fc 结束,但在8号段中我们看到它的结束地址是0x08049000。
在VMA9号与段3号之间也有同样的现象。
SHT 显示3号段开始于0x080494fc 。