简体   繁体   English

C/C++ 中的全局调用栈基础

[英]Base of Global Call Stack in C/C++

I have read that each function invocation leads to pushing of a stack frame in the global call stack and once the function call is completed the call stack is popped off and the control passes to the address that we get from the popped of stack frame.我已经读到每个 function 调用都会导致在全局调用堆栈中推送一个堆栈帧,一旦 function 调用完成,调用堆栈就会被弹出,并且控制权会传递到我们从弹出的堆栈帧中获得的地址。 If a called function calls on to yet another function, it will push another return address onto the top of the same call stack, and so on, with the information stacking up and unstacking as the program dictates.如果一个被调用的 function 调用另一个 function,它会将另一个返回地址压入同一个调用堆栈的顶部,依此类推,信息会按照程序的要求堆叠和拆栈。

I was wondering what's at the base of global call stack in a C or C++ program?我想知道 C 或 C++ 程序中全局调用堆栈的基础是什么?

I did some searching on the internet but none of the sources explicitly mention about it.我在互联网上进行了一些搜索,但没有一个消息来源明确提及它。 Is the call stack empty when our program starts and only once a function is called, the call stack usage starts?当我们的程序启动时调用堆栈是否为空,并且只有一次 function 被调用,调用堆栈使用开始? OR Is the address where main() function has to return, gets implicitly pushed as the base of our call stack and is a stack frame in our call stack?或者main() function 必须返回的地址,被隐式推送为我们调用堆栈的基础并且是我们调用堆栈中的堆栈帧? I expect the main() would also have a stack frame in our call stack since we are always returning something at end of our main() function and there needs to be some address to return to.我希望main()在我们的调用堆栈中也有一个堆栈帧,因为我们总是在 main() function 的末尾返回一些东西,并且需要返回一些地址。 OR is this dependent on compiler/OS and differs according to implementation ?或者这取决于编译器/操作系统并且根据实现而有所不同?

It would be helpful if someone has some informative links about this or could provide details on the process that goes into it.如果有人对此有一些信息链接或可以提供有关该过程的详细信息,那将很有帮助。

I'm not sure if there is a universal answer, as stack is something that may be implemented differently per architecture.我不确定是否有一个通用的答案,因为堆栈的实现可能因架构而异。 For example a stack may grow up (ie stack position pointer value increases when pushing onto the stack) or grow downwards.例如,堆栈可能会向上增长(即堆栈 position 指针值在压入堆栈时会增加)或向下增长。

Exiting main() is usually done by calling an operating function to indicate the program wishes to to terminate (with the specified return code), so I don't expect a return address for main() to be present on the stack, but this may differ per operating system and even compiler.退出main()通常是通过调用操作 function 来指示程序希望终止(使用指定的返回码)来完成的,所以我不希望main()的返回地址出现在堆栈上,但是这个可能因操作系统甚至编译器而异。

I'm not sure why you need to know this, as this is typically something you leave up to the system.我不确定您为什么需要知道这一点,因为这通常是您留给系统的事情。

main() is invoked by the libc code that handles setting up the environment for the executable etc. So by the time main() is called, the stack already has at least one frame created by the caller. main() 由处理为可执行文件等设置环境的 libc 代码调用。因此,在调用 main() 时,堆栈已经至少有一个由调用者创建的帧。

First of all, there is no such thing as a "global call stack".首先,不存在“全局调用堆栈”之类的东西。 Each thread has a stack, and the stack for the main thread is often looking quite different from the thread of any thread spawned later on.每个线程都有一个堆栈,主线程的堆栈通常看起来与以后产生的任何线程的线程完全不同。 And mostly, each of these "stacks" is just an arbitrary memory segment currently declared to be used as such, sub-allocated from any arbitrary suitable memory pool.大多数情况下,这些“堆栈”中的每一个只是一个任意的 memory 段,当前声明为这样使用,从任意合适的 memory 池子分配。

And due to compiler optimizations, many function calls will not even end up on the stack, usually.而且由于编译器优化,许多 function 调用通常甚至不会在堆栈上结束。 Meaning there isn't necessarily a distinguishable stack frame.这意味着不一定有可区分的堆栈框架。 You are only guaranteed that you can reference variables you put on the stack, but not that the compiler must preserve anything you didn't explicitly reference.您只能保证可以引用放入堆栈的变量,但不能保证编译器必须保留您未明确引用的任何内容。

There is not even a guarantee that the memory layout for your call stack must even be organized in distinguishable frames.甚至不能保证调用堆栈的 memory 布局甚至必须组织在可区分的框架中。 Function pointers are never guaranteed to be part of the stack frame, just happens to be an implementation detail in architectures where data and function pointers may co-exist in the address space. Function 指针永远不能保证是堆栈帧的一部分,只是恰好是数据和 function 指针可能在地址空间中共存的架构中的实现细节。 (As there are architectures which require return addresses to be stored in a different address space than the data used in the call stack.) (因为有些架构要求将返回地址存储在与调用堆栈中使用的数据不同的地址空间中。)

That aside, yes, there is code which is executed outside of the main() function.除此之外,是的,有一些代码在main() function 之外执行。 Specifically initializers for global static variables, code to set up the runtime environment (env, call parameters, stdin/stdout) etc.特别是全局static变量的初始化程序,用于设置运行时环境的代码(env、调用参数、stdin/stdout)等。

Eg when having linked to libc , there is __libc_start_main which will call your main function after initialization is done.例如,当链接到libc时,有__libc_start_main它将在初始化完成后调用您的main function。 And clean up when your main function returns.并在您的main function 返回时进行清理。

__libc_start_main is about the point where "stack" starts being used, as far as you can see from within the program. __libc_start_main是关于开始使用“堆栈”的点,从程序中可以看出。 That's not actually true though, there has already been some loader code been executed in kernel space, for reserving memory for your process to operate in initially (including memory for the future stack), initializing registers and memory to well defined values etc. That's not actually true though, there has already been some loader code been executed in kernel space, for reserving memory for your process to operate in initially (including memory for the future stack), initializing registers and memory to well defined values etc.

Right before actually "starting" your process, after dropping out of kernel mode, arbitrary pointers to a future stack, and the first instruction of your program, are loaded into the corresponding processor registers.在实际“开始”您的进程之前,在退出 kernel 模式后,指向未来堆栈的任意指针和程序的第一条指令被加载到相应的处理器寄存器中。 Effectively, that's where __libc_start_main (or any other initialization function, depending on your runtime) starts running, and the stack visible to you starts building up.实际上,这就是__libc_start_main (或任何其他初始化 function,取决于您的运行时)开始运行的地方,并且您可见的堆栈开始构建。

Getting back into the kernel usually involves an interrupt now, which doesn't follow the stack either, but may just directly access processor registers to simply swap the contents of the corresponding processor registers.回到 kernel 现在通常涉及一个中断,它也不跟随堆栈,但可能只是直接访问处理器寄存器以简单地交换相应处理器寄存器的内容。 (Eg if you call a function from the kernel, the memory required by the call stack inside the function call is not allocated from your stack, but from one you don't even have access to.) (Eg if you call a function from the kernel, the memory required by the call stack inside the function call is not allocated from your stack, but from one you don't even have access to.)

Either way, everything that happens before main() is called, and whenever you enter a syscall, is implementation dependent, and you are not guaranteed any specific observable behavior.无论哪种方式,调用main()之前发生的所有事情,无论何时进入系统调用,都取决于实现,并且不能保证任何特定的可观察行为。 And messing around with processor registers, and thereby alternating the program flow, is also far outside defined behavior as far as a pure C / C++ run time is concerned.就纯粹的 C / C++ 运行时间而言,弄乱处理器寄存器,从而改变程序流程,也远远超出了定义的行为。

Every system I have seen, when main() is called a stack is setup.我见过的每个系统,当调用 main() 时都会设置一个堆栈。 It has to be or just declaring a variable inside main would fail.它必须是或者只是在 main 中声明一个变量会失败。 A stack is setup once a thread or process is created.一旦创建了线程或进程,就会设置堆栈。 Thus any thread of execution has a stack.因此,任何执行线程都有一个堆栈。 Further in every assembly language i know, a register or fixed memory location is used to indicate the current value of the stack pointer, so the concept of a stack always exists (the stack pointer might be bad, but stack operations always exist since they are built into the every mainstream assembly language).此外,在我所知道的每一种汇编语言中,寄存器或固定的 memory 位置用于指示堆栈指针的当前值,因此堆栈的概念始终存在(堆栈指针可能不好,但堆栈操作始终存在,因为它们是内置于所有主流汇编语言中)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM