0

对不起,如果问题很愚蠢,但它们真的让我感到困惑!根据elf标准,二进制分为文本段(包含代码和RO数据)和数据段(包含RW和BSS),在程序执行和进程创建时加载到内存中,这些段提供信息用于流程执行的环境准备。问题是,当我在进程创建期间不提供堆栈大小时,如何决定分配多少堆栈给进程?此外,使用数据段,我们可以确定进程需要多少内存(用于全局变量),但是一旦分配了该内存,如何使用该分配内存中的地址空间完成变量映射?最后,这与分散加载有什么关系吗?我认为情况并非如此,因为当将图像加载到内存中并且一旦将控制权传递给操作系统时会完成分散加载,分配给可执行文件或应用程序的内存将由操作系统本身处理!我知道这些问题太多了,但我们将不胜感激任何帮助。如果您可以提供任何参考书或链接,我可以在其中详细研究此内容,那也将不胜感激。谢谢一吨!:) 谢谢一吨!:) 谢谢一吨!:)

4

1 回答 1

0

The question is, how it is decided that how much stack to allocate to process, when i am not providing stack size during process creation?

创建新进程时,execve()系统调用用于将新程序作为进程映像从当前运行的进程映像加载到内存中。这意味着加载新程序时的execve.text会替换旧的.data.segmentheap并重置stack. 现在 ELF 可执行文件被映射到内存地址空间,使堆栈空间被环境数组和参数数组初始化为main().

do_execve_common () 子程序下调用过程bprm_mm_init () 处理诸如,

  1. mm_struct使用调用mm_alloc ()来管理进程地址空间的新实例。

  2. 用init_new_context ()初始化这个实例。

  3. bprm_mm_init () 初始化堆栈。

  4. search_binary_handler () 例程搜索合适的二进制格式,即load_binaryload_shlib以分别加载程序或动态库。然后将内存映射到虚拟地址空间,并在调度程序识别进程时使进程准备好运行。

    因此,堆栈内存最终如下所示,它将main()在执行开始时显示为例程。函数调用子集的每个环境,包括参数和局部变量,都会在调用发生时动态存储或推送到堆栈内存区域中。

    -----------------
    |                 | <--- Top of the Stack
    | environmental   | 
    | variables and   |
    | the other       |
    | parameters to   |
    | main()          |
     _________________  <--- Stack Pointer
    |                 |                
    |  Stack Space    |
    |                 |   
    

Also, using the data segment we can determine how much memory the process requires (for global variables) but once this memory is allocated how mapping of variables is done with the address space inside this allocated memory?

让我们尝试通过调试一个简单的程序来弄清楚变量如何映射到内存段的不同部分,C如下所示,

/* File Name: elf.c : Demonstrating Global variables  */
#include <stdio.h>
int add_numbers(void);           
int value1 = 10;        // Global Initialized: .data section 
int value2;             // Global Initialized: .bss section

int add_numbers(void)
{
    int result;         // Local Uninitialized: Stack section
    result = value1 + value2;
    return result;
}

int main(void)
{
    int final_result;   // Local Uninitialized: Stack section
    value2 = 20;
    final_result = add_numbers();
    printf("The sum of %d + %d is %d\n",
        value1, value2, final_result);
}

用于readelf显示.data部分标题如下,

    $readelf -a elf
    ...
    Section Headers:
    [26] .data             PROGBITS         00000000006c2060  000c2060
       00000000000016b0  0000000000000000  WA       0     0     32
    [27] .bss              NOBITS           00000000006c3720  000c3710
       0000000000002bc8  0000000000000000  WA       0     0     32
    ...
    $readelf -x 26 elf
    Hex dump of section '.data':
    0x006c2060 00000000 00000000 00000000 00000000 ................
    0x006c2070 0a000000 00000000 00000000 00000000 ................
    ...

让我们使用 GDB 来看看这些部分包含什么,

   (gdb) disassemble 0x006c2060
   Dump of assembler code for function `data_start`:
   0x00000000006c2060 <+0>: add    %al,(%rax)
   0x00000000006c2062 <+2>: add    %al,(%rax)
   0x00000000006c2064 <+4>: add    %al,(%rax)
   0x00000000006c2066 <+6>: add    %al,(%rax)
   End of assembler dump.

上面段的首地址.data指的是data_start子程序。

   (gdb) disassemble 0x006c2070
   Dump of assembler code for function `value1`:
   0x00000000006c2070 <+0>: or     (%rax),%al
   0x00000000006c2072 <+2>: add    %al,(%rax) 
   End of assembler dump.
   ....

上面的反汇编转储了value1初始化为 10 的全局变量的地址。但是我们value2在下一个地址中看不到全局未初始化的变量。

让我们看看打印的地址value2

   (gdb) p &value2 
   $1 = (int *) 0x6c5eb0
   (gdb) info symbol 0x6c5eb0
   value2 in section **.bss**
   (gdb) disassemble 0x6c5eb0 
   Dump of assembler code for function `value2`:
   0x00000000006c5eb0 <+0>: add    %al,(%rax)
   0x00000000006c5eb2 <+2>: add    %al,(%rax)
   End of assembler dump.

多田!value2反汇编变量存储在.bss节中的引用指针。这解释了未初始化的全局变量如何映射到进程内存空间。

Lastly, is there any relation of this with scatter loading?

不。

于 2013-09-30T13:25:02.483 回答