如何确定流程大小?

时间:2012-09-19 08:23:39

标签: c operating-system

我对这些概念很陌生,但我想问你们一个我认为非常基本的问题,但我感到困惑,所以我在问它。 问题是... 如何通过操作系统确定进程的大小? 首先让我清楚一下,假设我已经编写了一个C程序,我想知道它将占用多少内存,我该如何确定它?其次,我知道有许多部分,如代码部分,数据部分,流程的BSS。现在它们的大小是预先确定的吗?其次,如何确定堆栈和堆的大小。堆积和堆的大小是否也很重要,而计算过程的总大小。

我们再说一遍,当我们加载程序时,给进程一个地址空间(由base和limit寄存器完成,我猜是由MMU控制),当进程试图访问一个内存位置时不在其地址空间中我们得到分段错误。进程如何访问不在其地址空间中的内存。根据我的理解,当发生一些缓冲区溢出时,地址就会被破坏。现在当进程想要访问损坏的位置时,我们就会得到分段错误。是否还有其他地址违规方式。

第三,为什么堆栈向下增长并向上堆积。这个过程与所有操作系统相同。它如何影响性能。为什么我们不能以其他方式使用它?

如果我在任何陈述中出错,请纠正我。

由于 苏赫拉布

4 个答案:

答案 0 :(得分:3)

当进程启动时,它会获得自己的虚拟地址空间。虚拟地址空间的大小取决于您的操作系统。通常,32位进程获得4 GiB(4千兆二进制)地址,64位进程获得18个EiB(18个exa二进制)地址。

您无法以任何方式访问未映射到虚拟地址空间的任何内容,因为根据定义,未映射的任何内容都没有适合您的地址。您可能尝试访问当前未映射到任何内容的虚拟地址空间区域,在这种情况下会出现段错误。

在任何给定时间,并非所有地址空间都映射到某个地址。也不是所有的都可以映射(它可以映射多少取决于处理器和操作系统)。在当前一代的英特尔处理器上,可以映射多达256 TiB的地址空间。请注意,操作系统可以进一步限制。例如,对于32位进程(具有最多4个GiB地址),Windows默认情况下为系统保留2 GiB,为应用程序保留2 GiB(但是有一种方法可以使系统为1 GiB,为应用程序提供3 GiB)。

在应用程序运行时,正在使用多少地址空间以及映射的更改量。特定于操作系统的工具将允许您监视当前为正在运行的应用程序分配的内存和虚拟地址空间。

代码部分,数据部分,BSS等是指链接器创建的可执行文件的不同区域的术语。通常,代码与静态不可变数据分开,静态不可变数据与静态分配但可变数据分开。堆栈和堆与上述所有内容分开。它们的大小由编译器和链接器计算。请注意,每个二进制文件都有自己的部分,因此任何动态链接的库都将分别映射到地址空间中,每个部分都映射到某个地方。但是,堆和堆栈不是二进制映像的一部分,每个进程和一个堆通常只有一个堆栈。

堆栈的大小(至少是初始堆栈)通常是固定的。编译器和/或链接器通常有一些标志可用于设置运行时所需的堆栈大小。堆栈通常“向后增长”,因为这是处理器堆栈指令的工作方式。堆栈在一个方向上增长而其余的在另一个方向增长,这使得在您希望两者都无限制但不知道每个可以增长多少的情况下更容易组织内存。

堆通常指的是在流程开始时未预先分配的任何内容。在最低级别,有几个与堆管理相关的逻辑操作(并非所有操作都按照我在所有操作系统中描述的那样实现)。

虽然地址空间是固定的,但某些操作系统会跟踪进程当前回收的部分内容。即使不是这种情况,过程本身也需要跟踪它。因此,最低级别的操作是实际决定将使用地址空间的某个区域。

第二个低级别操作是指示操作系统将该区域映射到某个区域。这通常可以是

  • 一些不可交换的内存

  • 可交换并映射到系统交换文件的内存

  • 可以交换并映射到其他文件的内存

  • 可以交换并以只读模式映射到其他文件的内存

  • 与另一个虚拟地址区域映射到

  • 的映射相同
  • 与另一个虚拟地址区域映射到的相同映射,但处于只读模式

  • 与另一个虚拟地址区域映射到的映射相同,但在写入模式下复制,复制的数据映射到默认交换文件

我可能忘记了其他组合,但这些组合是主要组合。

当然,使用的总空间实际上取决于您如何定义它。当前使用的RAM与当前映射的地址空间不同。但正如我在上面所写,依赖于操作系统的工具应该可以让你找出目前正在发生的事情。

答案 1 :(得分:2)

这些部分由可执行文件预先确定。

除此之外,还可能存在任何动态链接库。虽然DLL的代码和常量数据应该在使用它的多个进程之间共享,而不是被计算多次,但是应该在每个进程中考虑其特定于进程的非常量数据。

此外,在此过程中可以动态分配内存。

此外,如果进程中有多个线程,则每个线程都有自己的堆栈。

更重要的是,流程本身和代表内核的每个线程,每个进程和每个库的数据结构(线程本地存储,命令行参数,各种资源的句柄,这些资源的结构等等。)

在不知道如何实现所有内容的情况下,很难准确计算完整的流程大小。不过,你可能会得到一个合理的估计。

W.r.t。 According to my understanding when some buffer overflows happens then the address gets corrupted.这不一定是真的。首先,地址是什么?这取决于缓冲区附近的内存中发生了什么。如果有地址,则在缓冲区溢出期间可能会被覆盖。但是如果附近有另一个包含你的照片的缓冲区,那么图片的像素就会被覆盖。

当您尝试访问您没有必要权限的内存时(例如,映射或以其他方式存在于进程地址空间中的内核部分),您可能会出现分段或页面错误。或者它可以是只读位置。或者该位置无法映射到物理内存。

如果不知道我们所谈论的内容的性能,很难说堆栈和堆的位置和布局是如何影响性能的。你可以推测,但推测结果可能是错误的。

顺便说一句,你应该考虑在SO上就不同的问题提出单独的问题。

答案 2 :(得分:1)

“进程如何访问不在其地址空间中的内存?”

考虑到记忆保护,这是不可能的。但它可能是尝试。考虑随机指针或超出缓冲区的访问。如果你将任何指针增加到足够长的时间,它几乎肯定会徘徊在未映射的地址范围内。简单的例子:

 char *p = "some string";

 while (*p++ != 256)  /* Always true. Keeps incrementing p until segfault. */
     ;

这样的简单错误并不是闻所未闻,但要轻描淡写。

答案 3 :(得分:0)

我可以回答问题#2和#3。

回答#2

在C语言中使用指针时,您实际上使用的数值被解释为内存地址(现代操作系统上的逻辑地址,请参阅脚注)。您可以随意修改此地址。如果该值指向的地址不在您的地址空间中,则会出现分段错误。

例如考虑这种情况:您的操作系统为您的进程提供从0x01000到0x09000的地址范围。然后

int * ptr = 0x01000;
printf("%d", ptr[0]); // * prints 4 bytes (sizeof(int) bytes) of your address space
int * ptr = 0x09100;
printf("%d", ptr[0]); // * You are accessing out of your space: segfault

正如您所指出的,大多数段错误的原因是使用指向NULL的指针(主要是0x00地址,但依赖于实现)或使用损坏的地址。

请注意,在Linux i386上,您可能没有使用基址和限制寄存器。它们不是每个进程的限制,但它们指向两种段:用户空间或内核空间。

回答#3

堆栈增长取决于硬件,而不依赖于操作系统。在i386上,如push和pop这样的汇编指令使堆栈相对于堆栈相关寄存器向下增长。例如,当您执行推送时,堆栈指针会自动减少,而当您执行弹出时会增加堆栈指针。操作系统无法处理它。

<强>脚注

在现代操作系统中,进程使用所谓的逻辑地址。该地址由OS映射为物理地址。要记下这个简单编程的程序:

#include <stdio.h>

int main()
{
    int a = 10;
    printf("%p\n", &a);
    return 0;
}

如果您多次(甚至同时)运行此程序,即使对于不同的实例,您也会看到打印出的相同地址。当然这不是真正的内存地址,但它是一个逻辑地址,将在需要时映射到物理地址。