C语言运行时数据结构

段(Segment):

对象文件/可执行文件:

SVr4 UNIX上被称为ELF(起初"Extensible Linker Format", 现在"Executable and Linking Format")文件。BSD UNIX上被称为a.out。这些格式都具有段的概念

section是存放特定类型二进制文件区域,section是ELF文件的最小组织单元,段通常由多个section组成

段主要有:

  1. BSS段:Block Started by Symbol,放置全局的但是没有初始化的变量。由于BSS段的变量没有任何值,所以不会真的在a.out文件中存储,所以BSS在a.out文件中不占空间(除了需要指明大小需要部分外),而是在运行时申请
  2. text段:代码段,放置指令
  3. data段:数据段,放置全局的,而且已经初始化的变量。局部变量不会放到a.out中,而是在运行时创建

示例:

只有main函数:
   text    data     bss     dec     hex filename
  14292    1532     112   15936    3e40 Hello.exe
定义全局未初始化int:
   text    data     bss     dec     hex filename
  14292    1532     116   15940    3e44 Hello.exe
定义全局初始化int:
   text    data     bss     dec     hex filename
  14292    1536     112   15940    3e44 Hello.exe
定义局部未初始化int:
   text    data     bss     dec     hex filename
  14292    1532     112   15936    3e40 Hello.exe
定义局部初始化int:
   text    data     bss     dec     hex filename
  14308    1532     112   15952    3e50 Hello.exe

结论:

  1. 全局未初始化的变量放到了BSS段
  2. 全局初始化的放到了data段
  3. 局部未初始化变量只意味着在运行时为其分配空间,而不会在生成可执行文件的时候分配空间或者生成相关的语句,所以不涉及BSS\DATA\TEXT段
  4. 局部初始化变量不涉及BSS和DATA段,而是生成语句执行初始化

操作系统如何处理可执行文件:

段会生成运行时链接器可以直接加载的对象,加载器直接把每个段对应到内存中的一部分

这些段就成为执行中程序的一块实际的内存区域

highest memory address
+------------------------+
 |    stack segment  |
 |               .             |
 |               .             |
 |               .             |
+------------------------+
 |      BSS segment  |--未初始化的全局变量
+------------------------+
 |      data segment  |--初始化的全局变量
+------------------------+
 |      text segment   |
+------------------------+
 |                              |
+------------------------+
lowest memory address

text段:

放置程序指令,是加载器直接从文件中复制过来的。一般情况下,text段不会改变,某些操作系统和链接器可以给段的不同section设置不同的权限。比如:text是只读和只执行的,某些data是只读的。某些data是读写但是不可执行的

data段:

包含了初始化的全局变量和静态变量,并进行了初始化赋值

BSS段:

在data段生成之后,加载器就从可执行文件中获取BSS段的大小并获取相应的空间放到data段后面。通常BSS段和data段会合并在一起,由于段在OS内存管理中只是一段连续的虚拟地址空间,所以相连的会被合并。所以数据段通常是最大的段

局部变量、临时存储单元、函数调用的参数传递等就需要用到分配的栈空间

对于动态分配的空间还需要堆空间,堆空间是在需要的时候创建的,在第一次malloc()函数调用的时候

需要注意的是虚拟地址空间的最低的一部分没有映射到物理地址空间,所以任何对这部分地址的引用都是非法的。这部分一般是从0开始的几个字节空间,null指针或含有很小整数值的指针将指向这里

如果考虑到共享库文件,那么实际的映射将是这样的:

highest memory address
+--------------------------+
 |     stack segment    |
 |                .               |
 |                .               |
 |                .               |
+--------------------------+
 |            linker            |
+---------------------------+
 | unmapped segment|
 |                .                |
+---------------------------+
 |       data segment    |
+---------------------------+库文件
 |       text segment      |
+----------------------------+
 |                                  |
+----------------------------+
 |       data segment      |
+----------------------------+库文件
 |       text segment       |
+----------------------------+
 |                                  |
+----------------------------+
 |       data segment      |
+-----------------------------+执行代码
 |       text segment       |
+-----------------------------+
 |                                   |
+-----------------------------+
lowest memory address

C运行时如何处理可执行文件:

C在运行时会维护很多数据结构,比如栈、活动记录、数据、堆等等

栈段:

栈段只包含一个数据结构——栈

经典的栈定义是先进后出的队列,只有push和pop操作。但是这里的栈不仅可以push和pop还可以改变栈中某位置的值

运行时维护一个指针,通常位于寄存器中被称为sp,指向栈顶

栈段的作用主要有三个,两个关于函数,一个关于表达式计算:

  1. 栈为函数中的局部变量提供存储空间,这些变量被成为"自动变量"
  2. 栈保存函数调用时需要的维护信息,被称为"程序活动记录"。包含调用结束时的返回地址、不能放到寄存器中的参数和保存调用前的寄存器状态
  3. 栈也可以作为高速暂存寄存器,当程序需要临时存储的时候使用。如长表达式的计算,中间结果会被放到栈中并在使用的时候取出

alloca()函数分配的空间也在栈中,但是这部分空间会被下一次函数调用重写

如果不是有函数递归调用,栈是不被需要的。如果没有递归调用,局部变量、参数需要的空间和返回地址都可以在编译器知道并且在BSS段分配

程序活动记录

活动记录的目的是追踪调用链,每次调用函数都会在栈中生成一个活动记录,活动记录支持函数的调用以及记录调用结束后需要恢复的状态。具体活动记录的设计和实现相关,活动记录内部各区域的顺序可能各不相同,也可能有一个区域存储函数调用之前的寄存器值

大多数现代程序语言都支持函数内部定义函数(和数据一起)。但是C不允许函数嵌套声明,所有的函数都必须在词法顶层。这种限制能够一定程度的简化C编译器实现

在允许嵌套函数的语言中,活动记录会包含一个指向其外部函数的指针,这个指针被称为静态链接(static link)(和编译文件时的静态链接区分一下),这个指针允许内部函数获取外部函数的栈帧

虽然可能一个外部函数同时被多次调用,但是静态链接总能指向正确的栈帧,访问到正确的局部数据

对外部函数数据的获取被称为上层引用(uplevel reference)

之所以被称为静态链接,是因为对其外部函数的指向是在编译期确定的,而动态链接是运行时被调用时指向其调用者的栈帧

典型的活动记录如下:
+-------------------------+
 |         local vars       | -- 存储如调用结束时需要恢复得寄存器值等
+-------------------------+
 |       arguments       | -- 参数
+-------------------------+
 |       prev frame       | -- 调用者的栈帧
+-------------------------+
 |       return addr      | -- 返回地址
+-------------------------+

每次调用函数都会生成一个这样的活动记录。但是编译器作者会尽量的减少存储的信息以提高程序的性能,比如:

  1. 用寄存器存储某些信息而不是在栈中
  2. 对于叶函数(不会调用其他函数的函数)不生成完整的栈帧,调用者不再存储寄存器的值而是让被调用者存储
  3. 使用指向调用者栈帧的指针可以简化函数返回时弹栈到先前记录的工作

auto和static

auto的变量是函数调用时在栈中分配空间存储的,函数调用结束的时候对应的栈空间就会被释放并且可以被重写。所以如果函数返回一个指针指向局部变量就会返回一个"悬空指针",指向的值不是有效的。

如果想要返回一个函数中定义的变量,可以将其定义为static。static变量不是存储在栈中,而是在数据段分配空间存储。这样变量就会在程序的生命周期中存在,即便函数调用结束数据也依然存在,下一次函数调用还可以访问

栈帧不一定在栈中

如果把活动记录放到寄存器中会有更好的性能。SPARC架构以"寄存器窗口"来提高栈帧的性能。芯片中有一组专门用来存放活动记录中的参数的寄存器。空的栈帧还会被压入栈中,如果调用链过长导致寄存器窗口被用光,那么就会通过把寄存器中的值填充到对应的空栈帧中来释放寄存器 

线程控制:

每个线程都会有自己的栈并且用red zone和其他线程的栈结构区分

setjmp和longjmp

它们通过操纵活动记录实现,这个特性弥补了C在跳转能力上的不足

工作方式:

  1. setjmp(jmp_buf j)首先被调用,用变量j记录当前语句所在的位置,调用后返回0
  2. longjmp(jmp_buf j,int i)在setjmp之后调用,返回j记录的地址,使之看起来像是从函数setjmp()返回,而且返回值是整数i,用以区分这是从longjmp()返回的
  3. j的内容在使用longjmp()之后被销毁

setjmp()保存了当前程序计数器和当前栈顶指针的内容。然后longjmp()恢复这些内容,高效的将控制流转移到原来的位置恢复保存的状态。并且会回退所有保存的栈顶之前的栈空间

和goto的不同:

  1. goto语句无法跳出当前函数,但是longjmp甚至可以调到另一个文件的函数
  2. longjmp只能回到之前控制流到过的某个地方,即设置了setjmp()并且被执行过的地方

setjmp/longjmp最有用的地方是错误恢复。只要你没有从函数返回,如果发现了一个不可恢复的错误,你就可以移动控制流到之前的某个节点,然后从那里重新开始。可以用来从多重函数调用中立即返回,也可以用来预防危险的代码
如:

switch(setjmp(jbuf)) { case 0:  apple = *suspicious;  break;  case 1:  printf("suspicious is indeed a bad pointer\n");  break;  default:  die("unexpected value returned by setjmp");  
}

如果在某个地方检测到了这个指针危险,就可以返回到这里

就像goto语句,setjmp/longjmp也会导致程序难以理解难以调试,所以要尽量避免使用

UNIX下的栈段:
栈随着程序需要增长,程序员可以认为栈是无限大的

UNIX使用某种虚拟内存模式,当尝试获取超过分配的空间的空间的时候就会产生一个页错误,处理方式依赖于引用是否有效。内核处理非法引用的方式通常是向产生错误引用的程序发送一个信号。在栈顶之后有一个red zone,对这里的引用不会产生错误,操作系统相应的会增加栈空间的大小,虚拟地址空间会相应的增加

MS-DOS的栈段:
DOS中栈的大小是由可执行文件指定的,而且不能再运行时修改,对超过空间的访问会导致程序失效。如果打开了检查,就会产生栈溢出错误。如果超出了段的限制,也会在编译器产生这个错误。Turbo C如果数据段或代码段过大,会产生Segment overflowed   maximum  size  <lsegname>,80x86架构限制为64Kbytes

转载于:https://www.cnblogs.com/biaoJM/p/10186679.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/395866.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java挂起线程

2019独角兽企业重金招聘Python工程师标准>>> 不优雅的suspend import java.util.concurrent.TimeUnit;public class SuspendTest {static Object lock new Object();SuppressWarnings("deprecation")public static void main(String[] args) {Suspend s1…

Hibernate包及相关工具包下载地址

Hibernate包及相关工具包下载地址&#xff1a; http://prdownloads.sourceforge.net/hibernate/ 这里包含所有hibernate各个版本的包下载&#xff0c;且提供了 Middlegen Hibernate及hibernate-extensions包的下载。这两个包是用于自动生成相就的JAVA和*.hb…

init(coder:)_2018年《 New Coder》调查:31,000人告诉我们他们如何学习编码并在工作中获得工作…

init(coder:)More than 31,000 people responded to our 2018 New Coder Survey, granting researchers an unprecedented glimpse into how adults are learning to code.超过31,000人对我们的2018年《新编码器调查》做出了回应&#xff0c;使研究人员对成年人如何学习编码有了…

Redis源码解析:21sentinel(二)定期发送消息、检测主观下线

六&#xff1a;定时发送消息 哨兵每隔一段时间&#xff0c;会向其所监控的所有实例发送一些命令&#xff0c;用于获取这些实例的状态。这些命令包括&#xff1a;”PING”、”INFO”和”PUBLISH”。 “PING”命令&#xff0c;主要用于哨兵探测实例是否活着。如果对方超过一段时间…

[SDOI2018]原题识别

题解&#xff1a; 。。感觉挺烦得 而且我都没有注意到树随机这件事情。。 就写个30分的莫队。。 #include <bits/stdc.h> using namespace std; #define rint register int #define IL inline #define rep(i,h,t) for (int ih;i<t;i) #define dep(i,t,h) for (int it;…

django app中扩展users表

app models中编写新的User1 # _*_ coding:utf-8 _*_2 from __future__ import unicode_literals34 from django.db import models5 from django.contrib.auth.models import AbstractUser # 继承user67 # Create your models here.8910 class UserProfile(AbstractUser):11 …

[bzoj2301] [HAOI2011]Problem b

Description 对于给出的n个询问&#xff0c;每次求有多少个数对(x,y)&#xff0c;满足a≤x≤b&#xff0c;c≤y≤d&#xff0c;且gcd(x,y) k&#xff0c;gcd(x,y)函数为x和y的最大公约数。 Input 第一行一个整数n&#xff0c;接下来n行每行五个整数&#xff0c;分别表示a、b、…

华为p4用鸿蒙系统吗_华为p40pro是鸿蒙系统吗

华为的鸿蒙OS是一款“面向未来”的操作系统&#xff0c;一款基于微内核的面向全场景的分布式操作系统&#xff0c;此前mate30系列并没有搭载鸿蒙系统。那华为p40pro是鸿蒙系统吗&#xff1f;品牌型号&#xff1a;华为p40pro华为p40pro是鸿蒙系统吗&#xff1f;华为p40pro没有搭…

设置MYSQL允许用IP访问

mysql>use mysql;mysql>update user set host % where user root;mysql>flush privileges;mysql>select host,user from user where userroot;mysql>quit 转载于:https://www.cnblogs.com/vipstone/p/5541619.html

Web优化 --利用css sprites降低图片请求

sprites是鬼怪&#xff0c;小妖精&#xff0c;调皮鬼的意思&#xff0c;初听这个高端洋气的名字我被震慑住了&#xff0c;一步步掀开其面纱后发觉非常easy的东西。作用却非常大 什么是CSS Sprites CSS Sprites是指把网页中非常多小图片&#xff08;非常多图标文件&#xff09;做…

[BZOJ3203][SDOI2013]保护出题人(凸包+三分)

https://www.cnblogs.com/Skyminer/p/6435544.html 先不要急于转化成几何模型&#xff0c;先把式子化到底再对应到几何图形中去。 1 #include<cstdio>2 #include<algorithm>3 #define rep(i,l,r) for (int i(l); i<(r); i)4 typedef long long ll;5 using names…

轻松创建nodejs服务器(1):一个简单nodejs服务器例子

这篇文章主要介绍了一个简单nodejs服务器例子,本文实现了一个简单的hello world例子,并展示如何运行这个服务器,需要的朋友可以参考下我们先来实现一个简单的例子&#xff0c;hello world。 似乎每种语言教程的第一节都会讲这个&#xff0c;我们也不例外。 首先我们先创建一个项…

谁是赢家_人工智能竞赛正在进行中。 这是赢家。

谁是赢家by Terren Peterson由Terren Peterson 人工智能竞赛正在进行中。 这是赢家。 (The race is on for artificial intelligence. Here’s who is winning.) On Saturday, Louisville, Kentucky hosted the 143rd running of the Kentucky Derby. It was a spectacle wher…

mysql取消mvvc机制_MySQL探秘(六):InnoDB一致性非锁定读

一致性非锁定读(consistent nonlocking read)是指InnoDB存储引擎通过多版本控制(MVVC)读取当前数据库中行数据的方式。如果读取的行正在执行DELETE或UPDATE操作&#xff0c;这时读取操作不会因此去等待行上锁的释放。相反地&#xff0c;InnoDB会去读取行的一个快照。上图直观地…

自动化脚本

自动化脚本工具: http://appium.io/slate/cn/master/?python#about-appium 查看app元素工具: uiautomatorviewer http://www.cnblogs.com/ITGirl00/p/4235466.html app 反编译原理 http://blog.csdn.net/jiangwei0910410003/article/details/47188679转载于:https://www.cnblo…

springmvc常用注解之@Controller和@RequestMapping

对于各种注解而言&#xff0c;排第一的当然是“Controller”,表明某类是一个controller。 “RequestMapping”请求路径映射&#xff0c;如果标注在某个controller的类级别上&#xff0c;则表明访问此类路径下的方法都要加上其配置的路径&#xff1b;最常用是标注在方法上&…

最小可行产品是什么_无论如何,“最小可行产品”到底意味着什么?

最小可行产品是什么by Ravi Vadrevu通过拉维瓦德雷武(Ravi Vadrevu) 无论如何&#xff0c;“最小可行产品”实际上是什么意思&#xff1f; (What does “Minimum Viable Product” actually mean, anyway?) 伊隆马斯克(Elon Musk)提出一个令人困惑的想法 (Elon Musk on makin…

站立会议12-2

编写团队博客&#xff0c;进行资料的查看转载于:https://www.cnblogs.com/qijun1120/p/10247725.html

彻底删除mysql server 2005_sql2005卸载工具(sql server 2005卸载工具)

如果您要安装新版的sql就必须先完整的卸载sql2005&#xff0c;如果你按照常规的方法是不能完整的卸载sql2005&#xff0c;从而会引起安装的时候说sql已经挂起的错误&#xff0c;sql2005卸载工具(sql server 2005卸载工具)&#xff0c;是一个帮你完整的清理已经安装的sql的工具。…

谷歌浏览器有时会卡顿_Google不会,不要学:为什么搜索有时会比了解更好

谷歌浏览器有时会卡顿by Jeremy Gunter杰里米甘特(Jeremy Gunter) Google不会&#xff0c;不要学&#xff1a;为什么搜索有时会比了解更好 (Google not, learn not: why searching can sometimes be better than knowing) A few months ago, I was reading through some of th…