System.map文件的作用
有关System.map文件的信息好象很缺乏。其实它一点也不神秘,并且在整个事情当中它并不象看上去那么得重要。但是由于缺乏必要的文档说明,使其显得比较神秘。它就象耳垂,我们每个人都有,但却不知道是干什么用的。本网页就是用来说明这个问题的。
注意,我并不会是百分之一百正确的。例如,一个系统很可能没有/proc文件系统支持,但是大多数系统肯定有。这里我假定你是“随大流的”,并有一个典型配置的系统。
某些有关内核出错(oops)的阐述来自于Alessandro Rubini的“Linux设备驱动程序” 一书,我是从其中学到大部分内核编程知识的。
什么是符号(Symbols)?
在编程中,一个符号(symbol)是一个程序的创建块:它是一个变量名或一个函数名。 正如你自己编制的程序一样,内核具有各种符号也是不应该感到惊奇的。当然,区别在 于内核是一非常复杂的代码块,并且含有许多、许多的全局符号。
内核符号表(Kernel Symbol Table)是什么东西?
内核并不使用符号名。它是通过变量或函数的地址(指针)来使用变量或函数的,而 不是使用size_t BytesRead,内核更喜欢使用(例如)c0343f20来引用 这个变量。
而另一方面,人们并不喜欢象c0343f20这样的名字。我们跟喜欢使用象 size_t BytesRead这样的表示。通常,这并不会带来什么问题。内核主要 是用C语言写成的,所以在我们编程时编译器/连接程序允许我们使用符号名,并且使 内核在运行时使用地址表示。这样大家都满意了。
然而,存在一种情况,此时我们需要知道一个符号的地址(或者一个地址对应的 符号)。这是通过符号表来做到的,与gdb能够从一个地址给出函数名(或者给出一个 函数名的地址)的情况很相似。符号表是所有符号及其对应地址的一个列表。这里是 一个符号表例子:
c03441a0 B dmi_broken
c03441a4 B is_sony_vaio_laptop
c03441c0 b dmi_ident
c0344200 b pci_bios_present
c0344204 b pirq_table
c0344208 b pirq_router
c034420c b pirq_router_dev
c0344220 b ascii_buffer
c0344224 b ascii_buf_bytes你可以看出名称为dmi_broken的变量位于内核地址c03441a0处。
什么是System.map文件?
有两个文件是用作符号表的:
- /proc/ksyms
- System.map
这里,你现在可以知道System.map文件是干什么用的了。
每当你编译一个新内核时,各种符号名的地址定会变化。/proc/ksyms 是一个 "proc文件" 并且是在内核启动时创建的。实际上 它不是一个真实的文件;它只是内核数据的简单表示形式,呈现出象一个磁盘文件似 的。如果你不相信我,那么就试试找出/proc/ksyms的文件大小来。因此, 对于当前运行的内核来说,它总是正确的..
然而,System.map却是文件系统上的一个真实文件。当你编译一个新内核时,你原 来的System.map中的符号信息就不正确了。随着每次内核的编译,就会产生一个新的 System.map文件,并且需要用该文件取代原来的文件。
什么是一个Oops?
在自己编制的程序中最常见的出错情况是什么?是段出错(segfault),信号11。
Linux内核中最常见的bug是什么?也是段出错。除此,正如你想象的那样,段出 错的问题是非常复杂的,而且也是非常严重的。当内核引用了一个无效指针时,并不 称其为段出错 -- 而被称为"oops"。一个oops表明内核存在一个bug,应该总是提出 报告并修正该bug。
请注意,一个oops与一个段出错并不是一回事。你的程序并不能从段出错中恢复 过来,当出现一个oops时,并不意味着内核肯定处于不稳定的状态。Linux内核是非常 健壮的;一个oops可能仅杀死了当前进程,并使余下的内核处于一个良好的、稳定的 状态。
一个oops并非是内核死循环(panic)。在内核调用了panic()函数后,内核就不能 继续运行了;此时系统就处于停顿状态并且必须重启。如果系统中关键部分遭到破坏 那么一个oops也可能会导致内核进入死循环(panic)。例如,设备驱动程序中 出现的oops就几乎不会导致系统进行死循环。
当出现一个oops时,系统就会显示出用于调试问题的相关信息,比如所有CPU寄存器 中的内容以及页描述符表的位置等,尤其会象下面那样打印出EIP(指令指针)的内容:
EIP: 0010:[<00000000>]
Call Trace: []
一个Oops与System.map文件有什么关系呢?
我想你也会认为EIP和Call Trace所给出的信息并不多,但是重要 的是,对于内核开发人员来说这些信息也是不够的。由于一个符号并没有固定的地址, c010b860可以指向任何地方。
为了帮助我们使用oops含糊的输出,Linux使用了一个称为klogd(内核日志后台程序)的 后台程序,klogd会截取内核oops并且使用syslogd将其记录下来,并将某些象c010b860 的信息转换成我们可以识别和使用的信息。换句话说,klogd是一个内核消息记录器(logger), 它可以进行名字-地址之间的解析。一旦klogd开始转换内核消息,它就使用手头的记录器, 将整个系统的消息记录下来,通常是使用syslogd记录器。
为了进行名字-地址解析,klogd就要用到System.map文件。我想你现在 知道一个oops与System.map的关系了。
深入说明: 其实klogd会执行两类地址解析活动。
- 静态转换,将使用System.map文件。
- 动态转换,该方式用于可加载模块,不使用System.map,因此与本讨论没有关系,但我仍然对其加以简单说明。
Klogd动态转换
假设你加载了一个产生oops的内核模块。于是就会产生一个oops消息,klogd就会截获它,并发现该oops发生在d00cf810处。由于该地址属于动态加载模块,因此在System.map文件中没有对应条目。klogd将会在其中寻找并会毫无所获,于是断定是一个可加载模块产生了oops。此时klogd就会向内核查询该可加载模块输出的符号。即使该模块的编制者没有输出其符号,klogd也起码会知道是哪个模块产生了oops,这总比对一个oops一无所知要好。还有其它的软件会使用System.map,我将在后面作一说明。
System.map应该位于什么地方?
System.map应该位于使用它的软件能够寻找到的地方,也就是说,klogd会在什么地方寻找它。在系统启动时,如果没有以一个参数的形式为klogd给出System.map的位置,则klogd将会在三个地方搜寻System.map。依次为:
- /boot/System.map
- /System.map
- /usr/src/linux/System.map
System.map 同样也含有版本信息,并且klogd能够智能化地搜索正确的map文件。例如,假设你正在运行内核2.4.18并且相应的map文件位于/boot/System.map。现在你在目录/usr/src/linux中编译一个新内核2.5.1。在编译期间,文件 /usr/src/linux/System.map就会被创建。当你启动该新内核时,klogd将首先查询 /boot/System.map,确认它不是启动内核正确的map文件,就会查询 /usr/src/linux/System.map, 确定该文件是启动内核正确的map文件并开始读取其中的符号信息。
几个注意点:
- 在2.5.x系列内核的某个版本,Linux内核会开始untar成linux-version,而非只是linux (请举手表决 -- 有多少人一直等待着这样做?)。我不知道klogd是否已经修改为在/usr/src/linux-version/System.map中搜索。TODO:查看klogd源代码。
在线手册上对此也没有完整描述,请看:
strace -f /sbin/klogd | grep 'System.map'
31208 open("/boot/System.map-2.4.18", O_RDONLY|O_LARGEFILE) = 2
显然,不仅klogd在三个搜索目录中寻找正确版本的map文件,klogd也同样知道寻找名字为 "System.map" 后加"-内核版本",象 System.map-2.4.18. 这是klogd未公开的特性。有一些驱动程序将使用System.map来解析符号(因为它们与内核头连接而非glibc库等),如果没有System.map文件,它们将不能正确地工作。这与一个模块由于内核版本不匹配而没有得到加载是两码事。模块加载是与内核版本有关,而与即使是同一版本内核其符号表也会变化的编译后内核无关。
还有谁使用了System.map?
不要认为System.map文件仅对内核oops有用。尽管内核本身实际上不使用System.map,其它程序,象klogd,lsof,
satan# strace lsof 2>&1 1> /dev/null | grep System
readlink("/proc/22711/fd/4", "/boot/System.map-2.4.18", 4095) = 23ps,
satan# strace ps 2>&1 1> /dev/null | grep System
open("/boot/System.map-2.4.18", O_RDONLY|O_NONBLOCK|O_NOCTTY) = 6以及其它许多软件,象dosemu,需要有一个正确的System.map文件。
如果我没有一个好的System.map,会发生什么问题?
假设你在同一台机器上有多个内核。则每个内核都需要一个独立的 System.map文件!如果所启动的内核没有对应的System.map文件,那么你将定期地看到这样一条信息:
System.map does not match actual kernel (System.map与实际内核不匹配)
不是一个致命错误,但是每当你执行ps ax时都会恼人地出现。有些软件,比如dosemu,可能不会正常工作。最后,当出现一个内核oops时,klogd或ksymoops的输出可能会不可靠。
我如何对上述情况进行补救?
方法是将你所有的System.map文件放在目录/boot下,并使用内核版本号重新对它们进行命名。假设你有以下多个内核:
- /boot/vmlinuz-2.2.14
- /boot/vmlinuz-2.2.13
那么,只需对应各内核版本对map文件进行改名,并放在/boot下,如:
/boot/System.map-2.2.14
/boot/System.map-2.2.13如果你有同一个内核的两个拷贝怎么办?例如:
- /boot/vmlinuz-2.2.14
- /boot/vmlinuz-2.2.14.nosound
最佳解决方案将是所有软件能够查找下列文件:
/boot/System.map-2.2.14
/boot/System.map-2.2.14.nosound但是说实在的,我并不知道这是否是最佳情况。我曾经见到搜寻"System.map-kernelversion",但是对于搜索"System.map-kernelversion.othertext"的情况呢? 我不太清楚。此时我所能做的就是利用这样一个事实:/usr/src/linux是标准map文件的搜索路径,所以你的map文件将放在:
- /boot/System.map-2.2.14
- /usr/src/linux/System.map (对于nosound版本)
你也可以使用符号连接:
System.map-2.2.14
System.map-2.2.14.sound
System.map -> System.map-2.2.14.sound
System.map文件的作用的更多相关文章
- System.map文件的作用解析
有关System.map文件的信息好象很缺乏.其实它一点也不神秘,并且在整个事情当中它并不象看上去那么得重要.但是由于缺乏必要的文档说明,使其显得比较神秘.它就象耳垂,我们每个人都有,但却不知道是干什 ...
- System.map文件【转】
转自:http://blog.csdn.net/david104/article/details/7194185 当运行GNU链接器gld(ld)时若使用了"-M"选项,或者使用n ...
- Linux System.map文件【转】
转自:http://blog.csdn.net/ysbj123/article/details/51233618 当运行GNU链接器gld(ld)时若使用了"-M"选项,或者使用n ...
- .map文件的作用以及在chorme下会报错找不到jquery-1.10.2.min.map文件,404 的原因
source map文件是js文件压缩后,文件的变量名替换对应.变量所在位置等元信息数据文件,一般这种文件和min.js主文件放在同一个目录下. 比如压缩后原变量是map,压缩后通过变量替换规则可能会 ...
- auto make System.map to C header file
#!/bin/bash # auto make System.map to C header file # 说明: # 该脚本主要是将Linux内核生成的System.map文件中的符号.地址存入结构 ...
- Vue打包后出现一些map文件
Vue打包后出现一些map文件的解决办法: 问题: 可能很多人在做vue项目打包,打包之后js中,会自动生成一些map文件,那我们怎么把它去掉不要呢? 1,运行 cnpm run build 开始 ...
- 内核编译之vmlinuz vmlinux system.map initrd
一.vmlinuz vmlinuz是可引导的.压缩的内核.“vm”代表“Virtual Memory”.Linux 支持虚拟内存,不像老的操作系统比如DOS有640KB内存的限制.Linux能够使用 ...
- vue .map 文件
参数: productionSourceMap:false 这个改为false.去掉打包产生的map文件 map文件的作用:定位线上错误代码位置;
- vue打包后出现的.map文件
run dev build 打包项目后出现的list中的".map"文件最大. “.map”文件的作用:项目打包后,代码都是经过压缩加密的,如果运行时报错,输出的错误信息无法准确得 ...
随机推荐
- MySQL数据库导入或者同步大量数据时数据丢失解决方案
相信大家都经常遇到这样的情况,我们在编码的过程中经常需要在调试代码的时候切换到本地的数据库上做修改调试,如果当测试数据库的数据在几十万或者上百万数据的时候,我们无论是通过恢复备份/导入SQL的方式来把 ...
- Python数据挖掘指南
Data Mining in Python: A Guide 转载原文:https://www.springboard.com/blog/data-mining-python-tutorial/(全英 ...
- 准备PPT过程中的一些文档记录
http://jm.taobao.org/2016/12/23/20161223/ https://www.csdn.net/article/2015-02-10/2823900 https://da ...
- .Net WebApi 初探
实现服务层与api层共用,也就表明Service层就是api层. 关键类和接口 System.Web.Http.Dispatcher.DefaultHttpControllerSelector web ...
- golang子进程的启动和停止,mac与linux的区别
今天接到一个任务是将原来运行在mac的应用移植到linux,原因当然是因为客户那边当前是linux环境,也不想再采购mac电脑. 通常来说,这个工作并不难,因为我选用的服务器端技术是c或者golang ...
- c# 中的封装、继承、多态详解
面向对象有封装.继承.多态这三个特性,面向对象编程按照现实世界的特点来管理复杂的事物,把它们抽象为对象,具有自己的状态和行为,通过对消息的反应来完成任务.这种编程方法提供了非常强大的多样性,大大增加了 ...
- 行为驱动:Cucumber + Selenium + Java(四) - 实现测试用例的参数化
在上一篇中,我们介绍了Selenium + Cucumber + Java框架下的使用Tags对测试用例分组的实现方法,这一篇我们用数据表格来实现测试用例参数化. 4.1 什么是用例参数化 实际测试中 ...
- 【Java基础】【22IO(其他流)&Properties】
22.01_IO流(序列流)(了解) 1.什么是序列流 序列流可以把多个字节输入流整合成一个, 从序列流中读取数据时, 将从被整合的第一个流开始读, 读完一个之后继续读第二个, 以此类推. 2.使用方 ...
- Hbase给初学者的“下马威”
自从成为架构师()之后,李大胖的学习动力似乎少了一些,尤其是今年(当然也有一些客观因素). 临近岁末,内心着实有些惭愧,决定学习一把大数据.跟随一下业界前沿(其实已经不是前沿了),梦想着有一天能够拥有 ...
- 以语音评测的PC端demo代码为例,讲解口语评测如何实现
本文由云+社区发表 作者:腾讯智慧教育 概述 腾讯云智聆口语评测(英文版)(Smart Oral Evaluation-English,SOE-E)是腾讯云推出的语音评测产品,是基于英语口语类教育培训 ...