Linux 进程 – 内存布局、退出和 _exit C 函数

举报
Tiamo_T 发表于 2022/06/09 11:10:53 2022/06/09
【摘要】 在本文中,我们将讨论进程的内存布局和终止 C 函数的进程。

在本文中,我们将讨论进程的内存布局和终止 C 函数的进程。

进程的内存布局

如果我们尝试详细呈现和描述所有内容,Linux 中进程的内存布局可能会非常复杂。所以,在这里,我们将只介绍具有重要意义的东西。

如果我们尝试可视化进程的内存布局,我们会得到这样的结果:

让我们一一解释上述布局的每个组件:

  • 命令行参数和环境变量存储在进程内存布局的顶部较高地址。
  • 然后是堆栈段。这是进程使用的内存区域,用于存储函数的局部变量和每次调用函数时保存的其他信息。该其他信息包括返回地址,即调用函数的地址,调用者环境的一些信息,如其机器寄存器等存储在堆栈中。这里还值得一提的是,每次调用递归函数时都会生成一个新的堆栈帧,这样每组局部变量都不会干扰任何其他组。
  • 堆段是用于动态内存分配的段。该段不限于单个进程,而是在系统中运行的所有进程之间共享。任何进程都可以从该段动态分配内存。由于此段在进程之间共享,因此应谨慎使用此段中的内存,并且应在进程使用该内存完成后立即释放。
  • 如上图所示,堆栈向下增长,而堆向上增长。
  • 所有未在程序中初始化的全局变量都存放在 BSS 段中。在执行时,所有未初始化的全局变量都被初始化为零值。请注意,BSS 代表“由符号开始的块”。
  • 所有初始化的全局变量都存储在数据段中。
  • 最后,文本段是包含 CPU 执行的机器指令的内存区域。通常,此段在正在执行的同一程序的不同实例之间共享。由于没有必要更改 CPU 指令,因此该段具有只读权限。

请注意,上图只是内存布局的逻辑表示。不能保证在给定的系统上,进程的内存布局看起来像这样。除了这些之外,还有其他几个符号表、调试信息等段。

进程终止函数 exit() 和 _exit()

以下函数可能导致进程终止:


  1.  退出(状态)(与返回状态相同)
  2.  _exit(状态)或 _Exit(状态)

exit() 函数和 _exit() 函数之间的区别在于前者确实支持在将控制权交还给内核之前进行一些清理,而其他两个函数则立即返回给内核。

_exit 函数由 POSIX 指定,而 _Exit 函数由 ISO C 指定。除此之外,两者之间没有其他主要区别。

正如上面已经讨论过的,清理是exit() 和_exit() 之间的主要区别。在实际证明这一点之前,让我们了解另一个函数“atexit()”。

以下是原型:

int atexit(void (*function)(void));

顾名思义,这是一个系统调用,它接受一个函数指针并将该特定函数注册为该程序的清理函数。这意味着只要进程正常终止并且进程终止支持清理,就会调用注册的函数。

如果您再次浏览上一段的最后一行,您将看到函数 'atexit' 是清理过程的一部分,它区分了 exit() 和 _exit() 函数。所以,这是一个使用 atexit() 和 exit() 函数的代码..

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

extern char **environ;

void exitfunc(void)
{
  printf("\n Clean-up function called\n");
}

int main(int argc, char *argv[])
{
  int count = 0;

  atexit(exitfunc);

  printf("\n");
  while(environ[count++] != NULL)
  {
    // Dos some stuff
  }

  exit(0);
}

在上面的代码中,函数 'exitfunc()' 通过使用 atexit() 函数注册到内核作为清理函数。

当上面的代码运行时:

$ ./environ

Clean-up function called

我们看到调用了清理函数。

如果我们将上述代码中的 exit() 调用更改为 _exit() :

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

extern char **environ;

void exitfunc(void)
{
  printf("\n Clean-up function called\n");
}

int main(int argc, char *argv[])
{
  int count = 0;

  atexit(exitfunc);

  printf("\n");
  while(environ[count++] != NULL)
  {
    // Dos some stuff
  }

  _exit(0);
}

如果我们运行这个程序,我们会看到:

$ ./environ
$

所以我们看到这次没有调用清理函数'exitfunc()',这显示了exit()和_exit()函数之间的区别。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。