当前位置:编程学习 > C/C++ >>

C语言运行时数据

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 <linux/init.h>

  2 #include <linux/module.h>

  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 <linux/init.h>

  2 #include <linux/module.h>

  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()系统调用。

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

                                                                                                                  堆栈段

                                   &nbs

补充:软件开发 , C语言 ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,