热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

channelread0会被调用两次_面试官:Linux是如何进行函数调用的?

先抛出几个问题:进程虚拟地址空间是如何分布的?函数调用的栈帧结构是什么样子?函数调用涉及到的寄存器都起了什么作用?函数参数是

 先抛出几个问题:

  • 进程虚拟地址空间是如何分布的?

  • 函数调用的栈帧结构是什么样子?

  • 函数调用涉及到的寄存器都起了什么作用?

  • 函数参数是如何传递的?传递顺序如何?

  • 函数的返回值是如何传递的?

如果您对上述问题有些困惑,请继续往下看吧!

进程的内存布局

如图:

bcf88315b6aece42877268080630979c.png

高地址的一部分空间会分配给内核,称为内核空间,剩下的内存空间给用户使用,称为用户空间。

用户空间中有几个主要的内存区域:

  • 栈:用于维护函数调用的上下文,离开了栈,函数调用就没法实现,栈通常在用户空间的最高地址处分配,通常有数兆字节的大小。

  • 堆:堆用来容纳程序动态分配的内存区域,程序中malloc或new分配的内存就来自堆里。堆通常存在于栈的下方(低地址方向),在某些时候,堆也可能没有固定统一的存储区域,堆一般比栈大很多,可以有百兆甚至几G的大小。

  • 动态链接库映射区:这个区域用于映射装载的动态链接库,Linux下如果可执行文件依赖其它共享库,那系统就会在这个区域分配相应空间,并将共享库装入该空间。

  • 可执行文件映像:存储着可执行文件在内存里的映像,由装载器在装载时将可执行文件的内存读取或映射到这里。

  • 保留区:保留区并不是一个单一的内存区域,而是堆内存中受到保护而禁止访问的内存区域的总称,例如在大多数操作系统里,极小的地址通常都是不允许访问的,如NULL,通常C语言将无效地址赋值为0也是出于这个考虑,因为0地址正常情况下不可能有有效的可访问数据。

函数调用的栈帧结构

我们都知道函数调用都是以栈帧为单位,机器通常用栈来传递函数参数、保存返回地址、保存寄存器(即函数调用的上下文)及存储本地局部变量等。

一个单独的栈帧结构如图所示:

ea99f12cc5140e048ae8616ecf3e1a60.png

为单个函数调用分配的那部分栈称为栈帧,栈帧的边界由两个指针界定:寄存器%ebp为帧指针,指向当前栈帧的起始处,通常较为固定;寄存器%esp为栈指针,指向当前栈帧的栈顶位置,当程序执行时,栈指针可以移动,因此大多数数据的访问都是相对于帧指针的。

一次函数调用的栈帧图如下:

b41699fb883db18e68a7d2091d6ec9e9.png

寄存器使用约定

直接看图:

a9a57a3a746cec4845c1cc1f845bc5c9.png

图片来源于网络,侵权删

上图表达的应该已经很清楚啦,简单示例解释一下,函数调用需要传递参数时,第一个参数存到%edi里,第二个参数会存到%esi里,如果有返回值会存到%eax里,这里如果是64位的返回值,会使用%rax。

函数的调用约定

这里主要涉及三种约定:

  • 函数参数的传递顺序和方式:这里可以有很多参数传递方式,栈传递和寄存器传递,函数的调用方将参数压入栈中,函数自己再从栈中将参数取出,需要规定压栈的顺序,是从左到右,还是从右到左,有的也使用寄存器传递,这都需要约定好。

  • 栈的维护方式:在函数将参数压栈后,函数体会被调用,此后需要将被压入栈中的参数全部弹出,以使得栈在函数调用前后保持一致,这个弹出的工作可以是由函数的调用方完成还是函数本身来完成需要约定好。

  • 名字修饰策略:为了链接的时候对调用约定进行区分,需要对函数本身的名字进行修饰,不同的调用约定有不同的名字修饰策略。一般都是前面加个下划线。

C语言默认的调用约定是cdecl方式,可以通过__attribute__((cdecl))标明使用cdecl约定,其实还有其它一些调用约定,如图:

6ff6f01df0f5c20f132ba1766b973db5.png

函数的返回值传递

这里有几种情况:

4字节:当函数返回值是4个字节会通过%eax寄存器作为通道,函数将返回值存储在%eax中,返回后函数的调用方再读取%eax。

5-8个字节:通过rax寄存器作为通道。

大于8个字节:以如下代码举例:

struct A {// ...大于8字节 };A func() {A b;return b;}A x = func();

返回值传递方式如图:

3fb456c71fd0650286116dc1de441432.png

  1. 调用函数首先在栈上额外开辟一片空间,作为临时对象(temp)

  2. temp作为隐藏参数传递给被调用函数

  3. 函数将数据拷贝给temp,同时%eax为指向temp的指针

  4. 返回返回后将%eax指向的temp拷贝回被赋予的对象

返回值类型的尺寸太大导致函数返回时,会开辟一段区域作为中介,返回值对象会被拷贝两次,而C++在有些情况下会做返回值优化,减少拷贝的次数。

长按订阅更多面经分享

8eec0b446b0e5d34ee51f189105b9dd6.png

扫码回复 程序员 有彩蛋

参考资料

https://blog.csdn.net/slvher/article/details/8831885
https://blog.csdn.net/slvher/article/details/8831983
https://www.cnblogs.com/alantu2018/p/8465904.html
https://mp.weixin.qq.com/s/fpf4qRRLN3wVDUrWka3HfQ
https://mp.weixin.qq.com/s/j7SKtrMCmYs6g8yH75OH4A
https://www.sec4.fun/2018/05/29/stack/
https://murphypei.github.io/blog/2019/01/linux-heap
https://cloud.tencent.com/developer/article/1515763
《程序员的自我修养:链接装载与库》



推荐阅读
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • Linux磁盘的分区、格式化的观察和操作步骤
    本文介绍了如何观察Linux磁盘的分区状态,使用lsblk命令列出系统上的所有磁盘列表,并解释了列表中各个字段的含义。同时,还介绍了使用parted命令列出磁盘的分区表类型和分区信息的方法。在进行磁盘分区操作时,根据分区表类型选择使用fdisk或gdisk命令,并提供了具体的分区步骤。通过本文,读者可以了解到Linux磁盘分区和格式化的基本知识和操作步骤。 ... [详细]
  • Centos7搭建ELK(Elasticsearch、Logstash、Kibana)教程及注意事项
    本文介绍了在Centos7上搭建ELK(Elasticsearch、Logstash、Kibana)的详细步骤,包括下载安装包、安装Elasticsearch、创建用户、修改配置文件等。同时提供了使用华为镜像站下载安装包的方法,并强调了保证版本一致的重要性。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 阿里Treebased Deep Match(TDM) 学习笔记及技术发展回顾
    本文介绍了阿里Treebased Deep Match(TDM)的学习笔记,同时回顾了工业界技术发展的几代演进。从基于统计的启发式规则方法到基于内积模型的向量检索方法,再到引入复杂深度学习模型的下一代匹配技术。文章详细解释了基于统计的启发式规则方法和基于内积模型的向量检索方法的原理和应用,并介绍了TDM的背景和优势。最后,文章提到了向量距离和基于向量聚类的索引结构对于加速匹配效率的作用。本文对于理解TDM的学习过程和了解匹配技术的发展具有重要意义。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 本文介绍了在Linux下安装Perl的步骤,并提供了一个简单的Perl程序示例。同时,还展示了运行该程序的结果。 ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • Webmin远程命令执行漏洞复现及防护方法
    本文介绍了Webmin远程命令执行漏洞CVE-2019-15107的漏洞详情和复现方法,同时提供了防护方法。漏洞存在于Webmin的找回密码页面中,攻击者无需权限即可注入命令并执行任意系统命令。文章还提供了相关参考链接和搭建靶场的步骤。此外,还指出了参考链接中的数据包不准确的问题,并解释了漏洞触发的条件。最后,给出了防护方法以避免受到该漏洞的攻击。 ... [详细]
  • Ubuntu 9.04中安装谷歌Chromium浏览器及使用体验[图文]
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • 本文介绍了Linux Shell中括号和整数扩展的使用方法,包括命令组、命令替换、初始化数组以及算术表达式和逻辑判断的相关内容。括号中的命令将会在新开的子shell中顺序执行,括号中的变量不能被脚本余下的部分使用。命令替换可以用于将命令的标准输出作为另一个命令的输入。括号中的运算符和表达式符合C语言运算规则,可以用在整数扩展中进行算术计算和逻辑判断。 ... [详细]
  • Vagrant虚拟化工具的安装和使用教程
    本文介绍了Vagrant虚拟化工具的安装和使用教程。首先介绍了安装virtualBox和Vagrant的步骤。然后详细说明了Vagrant的安装和使用方法,包括如何检查安装是否成功。最后介绍了下载虚拟机镜像的步骤,以及Vagrant镜像网站的相关信息。 ... [详细]
author-avatar
mobiledu2502875993
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有