C++-程序源代码编译后一般都分为代码段和数据段,这样的段划分有什么道理,好处在哪?

C++-程序源代码编译后一般都分为代码段和数据段,这样的段划分有什么道理,好处在哪?

晚风撩人 发布于 2017-05-10 字数 119 浏览 1613 回复 6

程序源代码编译后一般都分为代码段和数据段,这样的分段有什么道理,好处在哪?个人觉得混杂的放在一个段里面更简单啊。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

夜无邪 2017-08-26 6 楼

不知道楼主了解哈佛结构的处理器不?也就是同时有数据cache和指令cache的。个人认为这是其中一个原因,可以提高程序的执行速度。

另外,即使不是哈佛结构,数据比代码更可能被多次调入CPU,把所有数据都放在同一块地方,作缓存也会更方便些。

以上只是个人看法,不保证准确。愿与各位朋友讨论。

偏爱自由 2017-08-21 5 楼

在编译期间,对于不同的数据放到不同的段,代码放到一个段等等划分管理,可以提高编译器工作的效率,其实编译器在最后链接的时候会合并一些“属性”相同的段。

可执行文件中包含许多的段,当段的数量增多时,就会产生很大的空间浪费。因为我们知道,ELF文件被映射时,是以系统的页长度(一般是4K)作为单位的,那么每个段在映射时的长度应该是系统页长的整数倍;如果不是,那么多余部分也将占用一个页。一个ELF文件中往往有十几个段,那么内存的浪费是可想而知的。

当我们站在操作系统装载可执行文件的角度来看问题时,可以发现它其实并不关心可执行文件各个段所包含的实际内容,操作系统只关心一些跟装载相关的问题,最主要的是段的权限(可读,可写和可执行)。ELF文件中,段的权限往往只有一下为数不多的组合,基本上是3中:

1.以代码段为代表的权限为可读可执行的段。
2.以数据段和BSS段位代表的权限为可读可写的段。
3.以只读数据为代表的权限为只读的段。

从而也可以说明为什么需要将代码段和数据段分开,因为它们的权限是不一样的

那么我们可以找到一个很简单的方案就是:对于相同权限的段,把它们合并到一起当做一个段进行映射。比如有两个段:".text"和".init",它们包含的分别是程序的可执行代码和初始化代码,并且它们的权限相同,都是可读可执行的。假设.text 为 4097 字节,.init 为 512 字节,这两个段分别映射的话需要占用三个页面,但是,如果将它们合并成一起映射的话只占2个页面。

清晨说ぺ晚安 2017-07-27 4 楼

代码段(Code),由机器指令组成,该部分是不可改的,编译之后就不再改变,放置在文本段(.text)。
数据段(Data),它由以下几部分组:
常量(constant),通常放置在只读read-only的文本段(.text)
静态数据(static data),初始化的放置在数据段(.data);未初始化的放置在(.bss,Block Started by Symbol,BSS段的变量只有名称和大小却没有值)
动态数据(dynamic data),这些数据存储在堆(heap)或栈(stack)

冯氏计算机的关键特点是用数字表示指令,于是在计算机内部看来“指令”和“数据”没有任何区别,会造成混淆。为了避免这个问题,采用指令和数据分开存放的办法,在代码段读到的统一被解释为机器指令,在数据段读到的统一解释为操作数。这样就把代码和数据区分开来。

举个例子,代码段是共享的,各个实例共享一个代码段。然后数据段不是共享的。因此从这方面看分段是必须的。

瑾兮 2017-07-21 3 楼

前面的答案基本上都回答得很好了,这里我从另外一个角度来给出把各个段分开的好处吧。

主要是涉及到内存页面的回收机制。

在Linux系统中,有一个专门的守护进程 kswapd,它会定期地检查系统中空闲内存的数量,一旦发现空闲内存数量小于一个阈值的时候,就会将若干页面换出,存放到交换分区中,以腾出足够的内存空间。

对于交换分区的使用,有着如下的规则:

对于那些没有被改写过的页面,这块内存不需要写到交换分区上,可以直接回收。

对于那些已经改写的页面,我们称之为脏页面(dirty page),则需要写到交换分区。

下面分析下,哪些内存可能是 dirty page 页面,哪些不是?

(1) 代码段,其权限是只读属性,不可能被改写,所以其所占的物理内存,都不是 dirty page

(2) 数据段,其权限是可读、可写,所以其所占的物理内存可能是 dirty page,也可能不是

(3) 堆栈段,其没有对应的映射文件,内容都是通过程序来改写的,所以其所占的物理内存全部是 dirty page

(4) 共享内存,其所占的物理内存,全部是 dirty page

综上所述,也就是说, 可以直接被回收的内存,主要是进程的代码段和未修改的数据段。因此,这样进行分段,可以更好地支持系统的内存回收机制。

想挽留 2017-07-05 2 楼

上图是一个PE格式的示意图。(当然,这生成目标文件的时候就已经对数据进行划分了,这里只是做个例子)
可以看到,里面各个段的划分情况。这么做的原因有很多很多,仅说几个我暂时了解的吧
1、在编译期间,对于不同的数据放到不同的段,代码放到一个段等等划分管理,可以提高编译器工作的效率。例如,把所有外部符号组织好的话,在链接期间查找符号就会很方便。也很必要。
2、在运行期间。代码和数据分开放。用户对于代码段,只有读的权限,就可以保证代码段不受破坏。
3、在装载到内存之后,更有利于内存的组织,例如内存里的哪一块是堆区,哪一块是栈区,哪一块是代码区等等
上面这三点仅是暂时想到的吧,这个想一下子都说清楚很难。第一是有遗漏,第二是理解的深度的问题。LZ有兴趣的话可以找本讲编译和操作系统的书来看下。相信会有更深理解的

加一点:
如果你的程序在操作系统中以多个进程运行。你打开两个firefox,那么代码段(只读的)在内存中只有一份,只要将数据段拷贝一份就可以了。可以大大减少内存,提高第二次程序启动速度

偏爱自由 2017-06-12 1 楼

很简单的,比如很多东西是不开源的,只告诉你调用方法和接口,然后CPP文件打包成库,只让你能看到头文件,这是其中一个原因。额。。。我发现我看错问题啦,在此道歉!