This article is transferred from the network
I. prerequisites-program memory allocation
The memory occupied by a C/C ++ compiled program is divided into the following parts:
1. STACK: the stack zone is automatically allocated and released by the compiler, and stores function parameter values and local variable values. The operation method is similar to the stack in the data structure.
2. Heap-generally assigned and released by the programmer. If the programmer does not release the heap, it may be recycled by the OS at the end of the program. Note that it is different from the heap in the data structure. The allocation method is similar to the linked list.
3. Global (static)-the storage of global variables and static variables is put together, and the initialized global variables and static variables are in one area, uninitialized global variables and uninitialized static variables are in another adjacent area. -The system is released after the program ends.
4. Text Constant Area-constant strings are placed here. The program is released by the System
5. program code area-stores the binary code of the function body.
Ii. Example Program
This is written by a senior. It is very detailed.
// Main. cpp
Int A = 0; global initialization Zone
Char * P1; uninitialized globally
Main ()
{
Int B; stack
Char s [] = "ABC"; stack
Char * P2; stack
Char * P3 = "123456"; 123456 \ 0 is in the constant zone, and P3 is in the stack.
Static int C = 0; Global (static) initialization Zone
P1 = (char *) malloc (10 );
P2 = (char *) malloc (20 );
The allocated 10-byte and 20-byte areas are in the heap area.
Strcpy (P1, "123456"); 123456 \ 0 is placed in the constant area, and the compiler may optimize it into a place with the "123456" that P3 points.
}
Ii. Theoretical knowledge of heap and stack
2.1 Application Method
STACK:
Automatically assigned by the system. For example, declare a local variable int B in the function; the system automatically opens up space for B in the stack.
Heap:
The programmer needs to apply and specify the size. In C, the malloc Function
For example, P1 = (char *) malloc (10 );
Use the new operator in C ++
For example, P2 = (char *) malloc (10 );
But note that P1 and P2 are in the stack.
2.2
System Response after application
STACK: as long as the remaining space of the stack exceeds the applied space, the system will provide the program with memory. Otherwise, an exception will be reported, prompting stack overflow.
Heap: First, you should know that the operating system has a linked list that records idle memory addresses. When the system receives a program application,
The linked list is traversed to find the heap node with the first space greater than the requested space. Then, the node is deleted from the idle node linked list and allocated to the program, for most systems, the size of the allocation will be recorded at the first address in the memory space, so that the delete statement in the code can correctly release the memory space. In addition, because the size of the heap node is not necessarily equal to the applied size, the system automatically places the excess part in the idle linked list.
2.3 Application size limit
STACK: in windows, a stack is a data structure extended to a low address and a continuous memory area. This statement indicates that the stack top address and the maximum stack capacity are pre-defined by the system. In Windows, the stack size is 2 MB (OR 1 MB, in short, it is a constant determined during compilation. If the requested space exceeds the remaining space of the stack, overflow will be prompted. Therefore, the space available from the stack is small.
Heap: the heap is a data structure extended to the high address and a non-sequential memory area. This is because the system uses the linked list to store the idle memory address, which is naturally discontinuous, And the traversal direction of the linked list is from the low address to the high address. The heap size is limited by the valid virtual memory in the computer system. It can be seen that the space obtained by the heap is flexible and large.
2.4 comparison of application efficiency:
The stack is automatically allocated by the system, which is faster. But programmers cannot control it.
The heap is the memory allocated by new, which is generally slow and prone to memory fragments. However, it is most convenient to use.
In addition, in windows, the best way is to use virtualalloc to allocate memory. Instead of heap or stack, it is to reserve a fast memory in the address space of the process, although it is the most inconvenient to use. However, it is fast and flexible.
Storage content in 2.5 heap and stack
STACK: when calling a function, the first entry to the stack is the address of the next instruction in the main function (the next executable statement in the function call statement), and then the parameters of the function, in most C compilers, parameters are written from right to left into the stack, followed by local variables in the function. Note that static variables are not included in the stack.
When the function call ends, the local variable first goes out of the stack, then the parameter, and the top pointer of the stack points to the address of the initial storage, that is, the next instruction in the main function, where the program continues to run.
Heap: Generally, the heap size is stored in one byte in the heap header. The specific content in the heap is arranged by the programmer.
2.6 comparison of access efficiency
Char S1 [] = "aaaaaaaaaaaaa ";
Char * S2 = "bbbbbbbbbbbbbbbbb ";
Aaaaaaaaaaa is assigned a value at the runtime;
Bbbbbbbbbbbbb is determined during compilation;
However, in future access, the array on the stack is faster than the string pointed to by the pointer (such as the heap.
For example:
# I nclude
Void main ()
{
Char A = 1;
Char C [] = "1234567890 ";
Char * P = "1234567890 ";
A = C [1];
A = P [1];
Return;
}
Corresponding assembly code
10: A = C [1];
00401067 8A 4D F1 mov Cl, byte PTR [ebp-0Fh]
0040106a 88 4D FC mov byte PTR [ebp-4], Cl
11: A = P [1];
0040106d 8B 55 EC mov edX, dword ptr [ebp-14h]
00401070 8A 42 01 mov Al, byte PTR [edX + 1]
00401073 88 45 FC mov byte PTR [ebp-4], Al
The first type reads the elements in the string directly into the CL register, while the second type reads the pointer value into EDX. Reading the characters based on edX is obviously slow.
2.7 summary:
The difference between stack and stack can be seen in the following metaphor:
Using Stacks is like eating at a restaurant, just ordering food (sending an application), paying for it, and eating (using it). If you are full, you can leave, without having to worry about the preparation work, such as cutting and washing dishes, as well as the cleaning and tail work such as washing dishes and flushing pots, his advantage is fast, but his degree of freedom is small.
Using heap is like making your favorite dishes. It is troublesome, but it suits your taste and has a high degree of freedom.
**************************************** ************************************
First, let's take an example:
Void F () {int * P = new int [5];}
This short sentence contains the heap and stack. When we see new, we should first think that we allocated a heap memory. What about the pointer P? It allocates a stack memory, so this sentence means that the stack memory stores a pointer P pointing to a heap memory. The program will first determine the size of memory allocated in the heap, then call operator new to allocate the memory, then return the first address of the memory, and put it into the stack, the assembly code in vc6 is as follows:
00401028 push 14 h
0040102a call operator new (00401060)
0040102f add ESP, 4
00401032 mov dword ptr [ebp-8], eax
00401035 mov eax, dword ptr [ebp-8]
00401038 mov dword ptr [ebp-4], eax
Here, we have not released the memory for simplicity, So how should we release it? Is it delete p? Australia, the error should be "Delete [] P" to tell the compiler: I deleted an array and vc6 will release the memory based on the cookie information.
Well, let's go back to our topic: What is the difference between stack and stack?
The main differences are as follows:
1. Different management methods;
2. Different space sizes;
3. Whether fragments can be generated is different;
4. Different Growth directions;
5. Different allocation methods;
6. Different Allocation Efficiency;
Management Method: For stacks, it is automatically managed by the compiler without manual control. For heaps, the release work is controlled by programmers and memory leak is easily generated.
Space size: Generally, in a 32-bit system, the heap memory can reach 4 GB. From this perspective, there is almost no limit on the heap memory. But for the stack, there is usually a certain amount of space. For example, under vc6, the default stack space is 1 MB (as if so, I cannot remember ). Of course, we can modify:
Open the project and choose Project> setting> link, select output from category, and set the maximum value and commit of the stack in reserve.
Note: The minimum reserve value is 4 byte. Commit is retained in the page file of the virtual memory. Compared with the general setting, commit makes the stack open up a large value, memory overhead and startup time may be increased.
Fragmentation problem: for the heap, frequent New/delete operations will inevitably lead to memory space disconnections, resulting in a large number of fragments, reducing program efficiency. For the stack, this problem will not exist, because the stack is an advanced and outgoing queue. They are so one-to-one correspondence that it is impossible to have a memory block popped up from the middle of the stack, before the pop-up, the post-stack content has been popped up. For details, refer to the data structure. We will not discuss it one by one here.
Growth direction: For the stack, the growth direction is upward, that is, the direction to the memory address increase; For the stack, the growth direction is downward, is to increase towards memory address reduction.
Allocation Method: The heap is dynamically allocated without static allocation. There are two stack allocation methods: static allocation and dynamic allocation. Static allocation is completed by the compiler, such as local variable allocation. Dynamic Allocation is implemented by the alloca function, but the stack dynamic allocation is different from the heap dynamic allocation. Its Dynamic Allocation is released by the compiler without manual implementation.
Allocation Efficiency: the stack is the data structure provided by the machine system, and the computer will provide support for the stack at the underlying layer: allocate a dedicated register to store the stack address, the output stack of the Pressure Stack has dedicated Command Execution, which determines the high efficiency of the stack. The heap is provided by the C/C ++ function library, and its mechanism is very complicated. For example, to allocate a piece of memory, library functions search for available space in heap memory based on certain algorithms (for specific algorithms, refer to data structures/operating systems, if there is not enough space (probably because there are too many memory fragments), it is possible to call the system function to increase the memory space of the program data segment, so that there is a chance to allocate enough memory, then return. Obviously, the heap efficiency is much lower than the stack efficiency.
From this point, we can see that compared with the stack, the use of a large number of new/delete operations may easily cause a large amount of memory fragments; because of the absence of dedicated system support, the efficiency is very low; because it may lead to switching between the user State and the core state, the memory application will become more expensive. Therefore, stacks are the most widely used in applications. Even function calls are completed using stacks. The parameters and return addresses in the function call process are as follows, both EBP and local variables are stored in stacks. Therefore, we recommend that you use stacks instead of stacks.
Although the stack has so many advantages, but because it is not so flexible as the heap, sometimes it is better to allocate a large amount of memory space.
Whether it is a heap or a stack, it is necessary to prevent cross-border phenomena (unless you intentionally cross-border it), because the cross-border result is either a program crash, either it is to destroy the heap and stack structure of the program and generate unexpected results. Even if the above problem does not occur during your program running, you should be careful, maybe it will collapse at some time. At that time, debugging was quite difficult :)
By the way, there is another thing. If someone puts the stack together, it means stack, not heap.
**************************************** ***********************************
The differences between stack and stack are as follows:
The heap and stack of the operating system, as mentioned above, will not be mentioned much.
There is also the heap and stack in the data structure. These are different concepts. Here, the heap actually refers to a Data Structure (meeting the heap nature) of the priority queue. The 1st elements have the highest priority; stack is actually a mathematical or data structure that meets the needs of the advanced and later stages.
Although the stack is called a connection, they still have a lot of difference. The connection is only due to historical reasons.
**************************************** ***********************************
Multi-thread parsing through stack call
First, the heap is the global data memory storage area of the process, and the stack is the local data memory storage area of the function. Most books refer to stacks in terms of stack or stack. Therefore, the title of a question is also expressed in this way. I hope readers will not confuse it.
Some people may find it strange that multithreading has something to do with stacks? It is difficult to identify many concepts due to multithreading. To fully understand multithreading, you must have a clear understanding of the stack. I personally think that in Windows programming, the concept of stack is like a pointer in C/C ++, which is very important but difficult to fully understand. Books on the market introduce stacks, float them on the surface, or be too theoretical, not specific enough and hard to understand. Here, I will share my learning experience with you in the form of examples. For clarity, This article consists of two parts: the first part introduces stack calling, which is the core of this article. The second part resolves the concept of multithreading.
I. Stack call
As we all know, in the process of function calling, parameter passing is completed through the stack. What is the machine code like? Different call conventions (Pascal conventions or stdcall conventions) will lead to different parameter pressure stack sequence. These details will be omitted. Interested readers can refer to relevant bibliography. In order to clearly express the concept of stack, some simple assembly language knowledge will be involved here, just a little bit. A simple C ++ console program is used as an example for further details. Listing the code is simple enough.
# Include <iostream. h>
Int FN (int n)
{
N + = 1;
Return N;
}
Void main ()
{
Int I = 1, j = 10;
I = FN (I); //
J = FN (j); // B
Cout <I <"" <j <Endl;
}
Now we need a little compilation knowledge to better understand what Stack is. Generally speaking, stack is a memory storage area, but the operation in this memory storage area is a bit special. Stack is a memory array directly managed by the CPU, which is managed by the CPU using registers. The address where the ESP register stores the data at the bottom of the stack (the stack increases downward. When the push command pushes data into the stack, the value of the ESP register decreases. On the contrary, the pop command pops up the data from the bottom of the stack, and the value of the ESP register increases accordingly. In this way, the ESP register will always store the address of the bottom stack data. On the other hand, when the CPU executes code, it relies entirely on the internal registers of the CPU. Find the code to be executed through the EIP register, locate the address of the function parameter, the local variable of the function through EBP and ESP, and so on.
Next let's take a look at how the stack changes when a function is called.
(L), function parameters are pushed into the stack.
(2) The return address (the address of the statement executed after the called function is executed) is pushed into the stack and the function is called. At this time, the CPU is ready to execute the code in the function body.
(3) When the function code is executed, the EBP is pushed into the stack.
(4) Make the EBP value equal to ESP. Now, the EBP records the current stack base address (ESP ), in the future, the function can use EBP to address the function parameters of the stack (function parameters, return addresses, and original EBP values of the stack that have been pressed ).
(5) Remove a certain number from ESP, leaving space for local variables for the function. After that, ESP will be used for addressing local variables.
The ingenuity of compiler designers is admirable. A simple function call written in advanced languages is compiled into such complicated machine code.
Well, let's take the previous C ++ program as an example. We will use the VC disassembly breakpoint debugging method to see how the above process is implemented.
At a, the main thread of the process calls the function FN and Passes parameters. The Assembly Code is as follows:
004010b6 mov eax, dword ptr [ebp-4] // At this time EBP = 0x0012ff80, & I = 0x12ff7c, ebp-4 is the address of I
004010b9 push eax
004010ba call @ ILT + 20 (FN) (00401019)
Obviously, the first line of assembly code puts the value of variable I into the register eax. The second line of assembly code pushes the value of variable I into the stack, corresponding to the Front (1 ). The third line assembles the code to execute the call command. This command automatically pushes the return address to the stack, jumps to the inside of the function body, and prepares to execute the code inside the function, corresponding to the Front (2 ).
Now let's take a look at the code inside the function. Only part of the relevant code is intercepted here.
2: int FN (int n)
3 :{
00401050 push EBP
00401051 mov EBP, ESP
... ... ... ...
4: N + = 1;
00401068 mov eax, dword ptr [EBP + 8] // at this time, EBP = 0x12ff20 & n = 0x12ff28 eax = 1
0040106b add eax, 1
004020.e mov dword ptr [EBP + 8], eax
The first line of Assembly Code corresponds to the first line (3 ). The second line of Assembly Code corresponds to the first (4 ). The third line of assembly code puts the value of Variable N into the register eax. The fourth line of Assembly Code adds one. The fifth line of assembly code puts the result back to Variable N. Here we can clearly see that the EBP register is used for parameter addressing.
Now let's take a look at the function calls in section B.
004010c5 mov ECx, dword ptr [ebp-8] // At this time EBP = 0x12ff80, & J = 0x12ff78, ebp-8 is J address
004010c8 push ECx
004010c9 call @ ILT + 20 (FN) (00401019)
We can see that there is no difference except for the change in the value of the pressure stack. Pressure on stack I at a and pressure on Stack J at B.
Next we will use the VC single-step debugging. Let's look at the function body. The assembly code is no different, but the difference is stack. Of course, the code of the function body is compiled by the compiler at one time. Even if the function is called multiple times to complete different tasks, the difference is that parameters (call stacks), the function uses indirect addressing internally, but the same machine code operates in different memory buckets.
2: int FN (int n)
3 :{
00401050 push EBP
00401051 mov EBP, ESP
... ... ... ...
4: N + = 1;
00401068 mov eax, dword ptr [EBP + 8] // at this time, EBP = 0x12ff20 & n = 0x12ff28 eax = 10
0040106b add eax, 1
004020.e mov dword ptr [EBP + 8], eax
Now, the concept of stack is finished.
Ii. multi-thread Parsing
The first thing to note is that each thread has a stack independently.
We know that Windows is a multi-task operating system, and multiple threads can be executed simultaneously. As mentioned above, the CPU execution program code relies entirely on various registers. When a thread is suspended, the current Register values are stored in the thread stack. When the CPU re-executes this thread, the value of the Register will be taken from the stack and then run, as if this thread has never been interrupted. It is precisely because each thread has an independent stack that enables the thread to "build a car by closed doors. As long as the parameter is passed to the stack of the thread, the CPU will take on the management of this memory storage area and execute the thread function code to operate on it in a timely manner, all of this is no different from what we described above. When the system switches between multiple threads, the CPU executes the same code to operate on different stacks.
The following is an example to deepen your understanding.
With the popularization of object-oriented programming methods, we are happy to package any operation into a class. Thread functions are no exception. It is a common method for C ++ programming to place thread functions in the form of static functions in the class. Normally, objects include attributes (class variables) and methods (class functions ). Attribute indicates the nature of the object. The method is used to manipulate the object and change its attributes. Pay attention to a small problem. Static functions of a class can only contain static variables of the category. Static variables do not belong to a single object and are stored in the global data storage area of the process. In general, we want each object to be "independent". That is to say, multiple objects can work independently and do not disturb each other. If you store the attributes of an object using a class (static) variable in the usual method, a problem may occur because the class (static) variable does not belong to a single object. What should we do now? How to maintain the "independence" of each object ". The solution is to use the stack to pass the parameter to the local variable (stack storage area) of the thread function and manage each thread with a single object. The problem is solved. Of course, there are a variety of solutions. Here we only want to further explain the relationship between multithreading and objects.
Because the internal implementation of Windows is too complicated, the stack is explained at the application layer. If you go deep into windows, the stack positioning first needs to obtain the corresponding linear address (Virtual Address) base address based on the register SS through the (global or local) segment descriptor table. This base address is added to the EIP, the physical memory is then addressable through the paging mechanism. Interested readers can refer to the bibliography after the article. I want to share my learning experience with you. If anything is wrong, please correct me.
Bibliography:
Author: Kip R. Irvine, translated and published by the Electronic Industry Publishing House.
32-bit assembly language programming in windows, Author: Luo yunbin, published by the Electronics Industry Publishing House.