Chinaunix首页 | 论坛 | 博客
  • 博客访问: 616028
  • 博文数量: 201
  • 博客积分: 3076
  • 博客等级: 中校
  • 技术积分: 2333
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-02 19:44
文章分类

全部博文(201)

文章存档

2010年(118)

2009年(83)

我的朋友

分类: LINUX

2009-09-21 16:07:49

Vmlinuz:就不说了,这是编译出来的压缩了的内核文件。

Initrd简介:

initrdlinux在系统引导过程中使用的一个临时的根文件系统,用来支持两阶段的引导过程。

直白一点,initrd就是一个带有根文件系统的虚拟RAM盘,里面包含了根目录‘/’,以及其他的目录,比如:bindevprocsbinsyslinux启动时必须的目录,以及在bin目录下加入了一下必须的可执行命令。

PC或者服务器linux内核使用这个initrd来挂载真正的根文件系统,然后将此initrd从内存中卸掉,这种情况下initrd其实就是一个过渡使用的东西。 在现在的许多简单嵌入式linux中一般是不卸载这个initrd的,而是直接将其作为根文件系统使用,在这之前就需要把所需要的程序,命令还有其它文件都安装到这个文件系统中。其实现在的大多数嵌入式系统也是有自己的磁盘的,所以,initrd在现在大多数的嵌入式系统中也和一般的linux中的作用一样只是起过渡使用。

Initrd的引导过程:第二阶段引导程序,常用的是grub将内核解压缩并拷贝到内存中,然后内核接管了CPU开始执行,然后内核调用init()函数,注意,此init函数并不是后来的init进程!!!然后内核调用函数initrd_load()来在内存中加载initrd根文件系统。Initrd_load()函数又调用了一些其他的函数来为RAM磁盘分配空间,并计算CRC等操作。然后对RAM磁盘进行解压,并将其加载到内存中。现在,内存中就有了initrd的映象。

然后内核会调用mount_root()函数来创建真正的根分区文件系统,然后调用sys_mount()函数来加载真正的根文件系统,然后chdir到这个真正的根文件系统中。

最后,init函数调用run_init_process函数,利用execve来启动init进程,从而进入init的运行过程。

system.map简介:

内核符号映射表,顾名思义就是将内核中的符号(也就是内核中的函数)和它的地址能联系起来的一个列表。是所有符号及其对应地址的一个列表。之所以这样就使为了用户编程方便,直接使用函数符号就可以了,而不用去记要使用函数的地址。

当你编译一个新内核时,原来的System.map中的符号信息就不正确了。随着每次内核的编译,就会产生一个新的 System.map文件,并且需要用该文件取代原来的文件。

--------------------------------------

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文件?

有两个文件是用作符号表的:

  1. /proc/ksyms
  2. 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文件有什么关系呢?

我想你也会认为EIPCall 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应该位于使用它的软件能够寻找到的地方,也就是说,klogd会在什么地方寻找它。在系统启动时,如果没有以一个参数的形式为klogd给出System.map的位置,则klogd将会在三个地方搜寻System.map。依次为:

  1. /boot/System.map
  2. /System.map
  3. /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) = 23
ps,
   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内容格式为:线性地址 类型 符号

具体内容如下:
00100000 A phys_startup_32
c0100000 T startup_32
c0100000 A _text                             注:表示内核代码第一个字节的地址
c01000c6 t checkCPUtype
c0100147 t is486
c010014e t is386
c0100199 t L6
c010019b t check_x87
c01001c2 t setup_idt
c01001df t rp_sidt
c01001ec t ignore_int
c0100220 t rest_init
c0100220 T stext
c0100220 T _stext
c0100252 t do_pre_smp_initcalls
c0100257 t run_init_process
c0100283 t init
c01003a8 t try_name
c0100529 T name_to_dev_t
c0100790 t calibrate_delay_direct

...

c02f95d7 T register_kretprobe
c02f96cd T unregister_kretprobe
c02f9760 t .text.lock.kprobes
c02f97b0 T __kprobes_text_end
c02f9abe t iret_exc
c02fa1af A _etext                              注:内核代码结束的位置,之后为内核初始化的数据
c02fa1b0 A __start___ex_table
c02facc0 A __stop___ex_table
c02fb000 r __func__.12
c02fb000 A __start_rodata
c02fb00c r __func__.13
c02fb020 r __func__.2
c02fb02c r __func__.3
c02fb040 R linux_banner
c02fb0bb r __func__.0
c02fb0c7 r __func__.1
c02fb0e0 r __func__.0
c02fb0ec r __func__.1
c02fb100 r border
c02fb160 r cplens

...

c03e1b08 D ip_statistics
c03e1b10 D tcp_statistics
c03e1b18 D udp_statistics
c03e1b20 D icmp_statistics
c03e1b28 D net_statistics
c03e1b30 d fn_hash_kmem
c03e1b34 d fn_alias_kmem
c03e1b38 d xfrm_dst_cache
c03e1b3c d secpath_cachep
c03e1b40 A _edata                       注:内核初始化数据结束,之后为未初始化数据
c03e2000 D init_thread_union
c03e4000 A __init_begin
c03e4000 t no_halt
c03e4000 T _sinittext
c03e400d t mca_pentium
c03e401d t no_387
c03e4033 t check_fpu
...

c043a058 b pfkey_table
c043a05c b pfkey_table_lock
c043a05c b pfkey_table_users
c043a060 b pfkey_socks_nr
c043a064 b acqseq.4
c043a068 b acqseq_lock.5
c043a068 A __bss_stop
c043a068 A _end                         注:内核未初始化数据结束
c043b000 A pg0
ffffe400 A __kernel_vsyscall
ffffe410 A SYSENTER_RETURN
ffffe420 A __kernel_sigreturn
ffffe440 A __kernel_rt_sigreturn

符号类型.

小写字母表示局部; 大写字母表示全局(外部).
A
The symbol's value is absolute, and will not be changed by further linking.

B
The symbol is in the uninitialized data section (known as BSS).

C
The symbol is common. Common symbols are uninitialized data. When linking, multiple common symbols may appear with the same name. If the symbol is defined anywhere, the common symbols are treated as undefined references. For more details on common symbols, see the discussion of -warn-common in Linker options.

D
The symbol is in the initialized data section.

G
The symbol is in an initialized data section for small objects. Some object file formats permit more efficient access to small data objects, such as a global int variable as opposed to a large global array.

I
The symbol is an indirect reference to another symbol. This is a GNU extension to the a.out object file format which is rarely used.

N
The symbol is a debugging symbol.

R
The symbol is in a read only data section.

S
The symbol is in an uninitialized data section for small objects.

T
The symbol is in the text (code) section.

U
The symbol is undefined.

V
The symbol is a weak object. When a weak defined symbol is linked with a normal defined symbol, the normal defined symbol is used with no error. When a weak undefined symbol is linked and the symbol is not defined, the value of the weak symbol becomes zero with no error.

W
The symbol is a weak symbol that has not been specifically tagged as a weak object symbol. When a weak defined symbol is linked with a normal defined symbol, the normal defined symbol is used with no error. When a weak undefined symbol is linked and the symbol is not defined, the value of the weak symbol becomes zero with no error.

-
The symbol is a stabs symbol in an a.out object file. In this case, the next values printed are the stabs other field, the stabs desc field, and the stab type. Stabs symbols are used to hold debugging information. For more information, see Stabs.

?
The symbol type is unknown, or object file format specific.


 
阅读(657) | 评论(0) | 转发(0) |
0

上一篇:stdarg (zz)

下一篇:joseph

给主人留下些什么吧!~~