About linux memory management Principles of knowledge

  
 

Linux memory management is mainly divided into two parts: physical address to virtual address mapping, kernel memory allocation management (mainly based on slab). 1, the concept physical address (physical address) for the memory chip level of the unit addressing, corresponding to the processor and the CPU connected to the address bus. —— This concept should be the best understanding of these concepts, but it is worth mentioning that although you can directly understand the physical address as the memory itself inserted in the machine, treat the memory as a slave. A large array of bytes up to the largest empty byte by byte number, and then this array is called a physical address, but in fact, this is just a hardware abstraction provided to the software, the way memory is addressed is not the case. Therefore, it is more appropriate to say that it corresponds to the address bus, but it is acceptable to put the physical address into the physical memory one by one. Perhaps the wrong understanding is more conducive to metaphysical abstraction. Virtual memory This is an abstract description of the entire memory (not to the top of the machine). It is relative to physical memory, it can be directly understood as "not straightforward", "false" memory, for example, an 0× 08000000 memory address, it is not the big array on the physical address Medium 0 time; 08000000 – 1 that address element; the reason is that modern operating system
provides a memory management image, that is, virtual memory (virtual memory). The process uses the address in virtual memory, and the operating system
assists the relevant hardware to convert it into a real physical address. This "conversion" is the key to all issues discussed. With such an abstraction, a program can use a much larger address space than the real physical address. (Removing the East Wall, making up the Western Wall, the bank does the same), and even multiple processes can use the same address. Not surprising, because the translated physical addresses are not the same. You can decompile the connected program and find that the connector has assigned an address to the program. For example, to call a function A, the code is not call A, but call 0× 0811111111, that is, function A The address has been fixed. There is no such a "transition", there is no concept of virtual address, this is simply not feasible. Stopped, this problem will not be able to stop. Logical address Intel has retained the segmental memory management method of ancient times for compatibility. A logical address is an address used in a machine language instruction to specify an operand or an instruction. In the above example, we say that the connector is assigned to 0 by A; the address 0811111 is the logical address. —— But I am sorry, saying this, it seems to violate the Intel mid-range management, the logical address requirement, "a logical address, is a segment identifier plus an offset of the relative address within the specified segment. , expressed as [segment identifier: offset within the segment], that is, the 0th time in the above example, 0811111111, should be expressed as [A code segment identifier: 0x08111111], so that it is complete " linear address (linear address) or virtual address (virtual address) is similar to a logical address, it is also an unreal address, if the logical address is the corresponding hardware platform segment management pre-conversion address, then the linear address corresponds to the hardware page The pre-conversion address of the memory. The CPU converts the address in a virtual memory space into a physical address. Two steps are required: first, a logical address (actually an intra-segment offset, this must be understood!!!), the CPU must use its segmentation The memory management unit first converts a logical address into a thread address, and then uses its page memory management unit to convert to the final physical address. It is really cumbersome and unnecessary to do this two conversions, because the linear address can be directly drawn to the process. The reason for this redundancy is that Intel is completely compatible. 2, CPU segment memory management, how the logical address is converted to a linear address A logical address consists of two parts, the segment identifier: the offset within the segment. A segment identifier consists of a 16-bit field called a segment selector. The first 13 bits are an index number. The last 3 bits contain some hardware details, as shown in the figure: The last two involve permission checks, which are not included in this post. The index number, or directly understood as an array subscript —— then it always corresponds to an array, what is it Dongdong index? This stuff is "segment descriptor" & rdquo;, huh, the specific address of the segment descriptor describes a segment (for the understanding of the word & rdquo; segment & rdquo;, I imagine it, take a hand Knife, cut virtual memory, cut into several pieces & mdash; — segment). Thus, a large number of segment descriptors, an array is called, "segment descriptor table", so that a specific segment descriptor can be found directly in the segment descriptor table by the first 13 bits of the segment identifier. This descriptor describes a segment. I just used the abstraction of the segment to be less accurate, because I have a look at what is in the descriptor and what is the description of it. What is the stuff, each segment descriptor consists of 8 bytes, as shown below: These stuff are very complicated, although you can use a data structure to define it, but I only care about the same here, is the Base field, which describes The linear address of the beginning of a segment. Intel's original intention is that some global segment descriptors are placed in the "global segment descriptor table (GDT)", some local, such as each process's own, is placed in the so-called "local segment" Descriptor Table (LDT) & rdquo; When should I use GDT and when should I use LDT? This is indicated by the T1 field in the segment selector, =0, indicating that GDT is used, and =1 indicates that LDT is used. The address and size of the GDT in memory are stored in the gdtr control register of the CPU, while the LDT is in the ldtr register. A lot of concepts, like a tongue twister. This picture seems to be more intuitive: First, given a complete logical address [segment selector: offset within the segment], 1, see the segment selector T1 = 0 or 1, know that the current conversion is GDT The segment, or the segment in the LDT, gets its address and size according to the corresponding register. We have an array. 2, take out the first 13 bits of the segment selector, you can find the corresponding segment descriptor in this array, so that it has Base, that is, the base address is known. 3, Base + offset, is the linear address to be converted. It's still quite simple. For software, in principle, you need to prepare the information needed for hardware conversion, and you can let the hardware complete the conversion. OK, let's see how Linux does it. 3, Linux segment management Intel requires two conversions, so although it is compatible, but it is very redundant, huh, no way, the hardware requires this, the software can only do it, how can it be formalism . On the other hand, some other hardware platforms do not have the concept of secondary conversion. Linux also needs to provide a high-level abstraction to provide a unified interface. Therefore, Linux's segment management, in fact, is just "spoof" & rdquo; a little hardware. According to Intel's intention, GDT is used globally, and each process uses LDT—— however, Linux uses the same segment for all processes to address instructions and data. That is, the user data segment, the user code segment, and corresponding, the kernel data segment and the kernel code segment. There is nothing strange about doing this. It was originally a form, just like we wrote the year-end summary. include /asm-i386 /segment.h #define GDT_ENTRY_DEFAULT_USER_CS 14 #define __USER_CS (GDT_ENTRY_DEFAULT_USER_CS * 8 + 3) #define GDT_ENTRY_DEFAULT_USER_DS 15 #define __USER_DS (GDT_ENTRY_DEFAULT_USER_DS * 8 + 3) #define GDT_ENTRY_KERNEL_BASE 12 #define GDT_ENTRY_KERNEL_CS (GDT_ENTRY_KERNEL_BASE + 0) # define __KERNEL_CS (GDT_ENTRY_KERNEL_CS * 8) #define GDT_ENTRY_KERNEL_DS (GDT_ENTRY_KERNEL_BASE + 1) #define __KERNEL_DS (GDT_ENTRY_KERNEL_DS * 8) wherein the macro substitution to numerical values, was: #define __USER_CS 115 [00000000 1110 0 11] #define __USER_DS 123 [00000000 1111 0 11] #define __KERNEL_CS 96 [00000000 1100 0 00] #define __KERNEL_DS 104 [00000000 1101 0 00] rear brackets are four 16-bit segment selector system represented by two, their index field values ​​T1 and also Can be calculated __USER_CS index= 14 T1=0 __USER_DS index= 15 T1=0 __KERNEL_CS index= 12 T1=0 __KERNEL_DS index = 13 T1 = 0 T1 are 0, then use the GDT, and then look at the contents of the GDT is initialized corresponding items 12-15 (arch /i386 /head.S): .quad 0x00cf9a000000ffff /* 0 & times; 60 kernel 4GB code at 0 & times; 00000000 * /.quad 0x00cf92000000ffff /* 0 & times; 68 kernel 4GB data at 0 & times; 00000000 * /.quad 0x00cffa000000ffff /* 0 & times; 73 user 4GB code at 0 & times; 00000000 * /.quad 0x00cff2000000ffff /* 0x7b user 4GB data at 0 & times; 00000000 * /as previously described in the descriptor table, they can be expanded and found to bits 16-31 are all 0, i.e., four segment base address are all 0. Thus, given an intra-segment offset address, according to the previous conversion formula, 0 + intra-segment offset, converted to a linear address, can draw important conclusions, "Under Linux, the logical address is always consistent with the linear address ( It is the same, not the same as some people say), that is, the value of the offset field of the logical address is always the same as the value of the linear address. ! ! ! ” Ignore too many details, such as permission checks for segments. Ha ha. In Linux, most of the processes do not use LDT, unless you use Wine to emulate the Windows
program.

Copyright © Windows knowledge All Rights Reserved