设为首页 加入收藏

TOP

C语言运行时数据 (一)
2014-11-23 22:04:12 来源: 作者: 【 】 浏览:0
Tags:语言 行时 数据

1 可执行文件的格式

在UNIX传统的操作系统中,所有编译生成的输出文件都缺省地使用同一个名字a.out,在现代操作系统中,a.out格式的可执行文件是链接器的输出,而不是汇编程序的输出(在计算机的远古时代,a.out是汇编器的输出,那个时候还没有链接器)。

目标文件和可执行文件有几种不同的格式,大多数都采用了一种ELF的格式,更多的格式可以使用下面的命令来查看(有的系统中可能找不到手册):

$ man a.out

你可以在linux系统中,对编译链接生成的可执行文件使用:

$ file 可执行文件名

可以看到他的输出,说明这是一个ELF格式的可执行文件:


yuanlu@bear-labpc:~/workspace/wifi/wifi_hello$ file hello.ko

hello.ko: ELF 32-bit LSB relocatable, ARM, version 1 (SYSV), not stripped

2 UNIX中段的概念

在NUIX系统中,有很多的不同格式,但他们都有一个共同的概念—段。

所谓的段是目标文件的概念,一个目标文件有多个段,他们是二进制文件中简单的的区域,里面保存了和某种特定类型相关的所有信息,如:符号表条目。这里不要把UNIX和Intel X86中的段概念混淆,后者中的段表示一种内存模型的设计结果,在这种设计中,地址空寂并非一个整体,而是分成一些固定大小的区域,称之为段。

对于一个目标文件,运行size命令可以告诉你这个文件的三个段的大小。这三个段分别是:代码段(文本段),数据段和bss段:

yuanlu@bear-labpc:~/workspace/wifi/wifi_hello$ size hello.ko

text data bss dec hex filename

224 300 0 524 20c hello.ko

这里对三个段中存放的内容做一个说明:

(1) 文本段:即代码段,存放要执行的指令代码

(2) 数据段:存放全局或静态的已经初始化的数据变量

(3) bss段:存放全局或静态的尚未初始化的数据变量。由于BSS段只保存没有值得变量,所以事实上他并不需要保存这些变量的映像,而只是将BSS段在运行时需要的大小记录在目标文件中,因此BSS段并不占据目标文件的任何空间。

需要注意的是一个a.out可执行文件的组成是下面这个样子的:


a. a.out神奇数字

b. a.out的其他内容

c. BSS数据段所需大小

d. 数据段(初始化后的全局和静态变量)

e. 文本段(可执行文件的指令)

其中文件的头部有一个a.out的神奇数字,它是一种能够确认一组随机的二进制位集合的什么数字,我们暂且不用理会;对于局部变量而言,它不存在a.out中而是在运行时创建。下面我们借用号称最简单的hello world驱动来证明上面的内容的正确性。

1 #include

2 #include

3

4 MODULE_LICENSE("GPL");

5

6 /* the init function*/

7 static __init int hello_init(void)

8

9 {

10

11 printk(KERN_WARNING "Hello world !/n");

12

13 return 0;

14 }

15

16 /* the distory function*/

17 static __exit void hello_exit(void)

18 {

19 printk(KERN_WARNING "Goodbye!/n");

20 }

21

22 module_init(hello_init);

23 module_exit(hello_exit);

编译生成hello.ko可执行文件后,使用size hello.ko命令查看每个段的使用情况:

yuanlu@bear-labpc:~/workspace/wifi/wifi_hello$ size hello.ko

text data bss dec hex filename

224 300 0 524 20c hello.ko


现在的bss段的内容为空,这里的0表示bss段的大小,data段的大小为300(单位都是字节)。我们增加分别两个全局和静态已经初始化的变量和未初始化的变量,在函数中也增加一个大数组的声明:

1 #include

2 #include

3

4 MODULE_LICENSE("GPL");

5

6 int i;

7 int m = 2;

8

9 static int j;

10 static int n = 1;

11

12 /* the init function*/

13 static __init int hello_init(void)

14

15 {

16 printk(KERN_WARNING "Hello world !/n");

17

18 return 0;

19 }

20

21 /* the distory function*/

22 static __exit void hello_exit(void)

23 {

24 printk(KERN_WARNING "Goodbye!/n");

25 }

26

27 module_init(hello_init);

28 module_exit(hello_exit);

编译后使用size工具:

yuanlu@bear-labpc:~/workspace/wifi/wifi_hello$ size hello.ko

text data bss dec hex filename

224 300 8 532 214 hello.ko

这里发现bss段的大小变成了8,data段却没有增加,通过后面的继续求证发现这样一个事实:

(1)对于函数内部的局部变量没有存放在可执行文件里

(2)对于全局变量而言,未初始化的变量存放在bss段,初始化的变量分两种:第一,如果被初始化为0,仍然被放在bss段,否则放在数据段(这一点有点不同哦)

(3)对于静态全局变量而言,不管有没有初始化,都不存放在可执行文件中

如果说前面两点很好理解的话,俺么对于第三点的实际表现与理论上的比较大的出入,有待后面继续求证,这里只能猜测是编译器的差异(我的环境是交叉编译环境)。

3 a.out的内存布局

段可以被方便地映射到连接器在运行时可以直接载入的对象中,载入器只是去可执行文件的每一个段的一个映像,本质上段就是正在执行的程序中的一块内存区域。连接器把每个段从文件拷贝到内存中,一般使用mmap()系统调用。

下面是可执行文件中的段在内存中的布局图

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇C运算符优先级的错误 下一篇狼追兔子问题C模拟解

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: