In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-10 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/01 Report--
This article introduces the relevant knowledge of "what is the process of MMU in Linux memory management". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
Access memory flow in ARMv8
I like to illustrate things in a graphical way, simple and straightforward:
The blue part is cpu, the gray part is memory, and the white part is the process of cpu accessing memory and address translation. Before explaining the nature of address translation, let's understand the following concepts:
The process of TLB:MMU work is the process of querying the page table. If it is too expensive to query the page table in memory, in order to improve the lookup efficiency, a small area with faster access is used to store address translation entries. (when the contents of the page table change, you need to clear the TLB to prevent address mapping errors. )
The caching mechanism between Caches:cpu and memory is used to improve the access rate. In the case of armv8 architecture, the caches in the figure above is actually L2 Cache, which is not explained further here.
"so how does CPU access memory through MMU and Cache? "
You can see that the key to the translation of virtual and physical addresses is the process Table Walk Unit.
The nature of the translation of virtual addresses into physical addresses
We know that the size of the addressing space in the kernel is controlled by CONFIG_ARM64_VA_BITS. Here, take 48 bits as an example. In ARMv8, the Kernel Space page table base address is stored in the TTBR1_EL1 register, and the User Space page table base address is stored in the TTBR0_EL0 register, where the high order of the kernel address space is all 1, (0xFFFF0000_00000000 ~ 0xFFFFFFFF_FFFFFFFF), and the high bit of the user address space is all 0, (0x00000000_00000000 ~ 0x0000FFFF_FFFFFFFF).
With the macro concept, let's take the kernel addressing process as an example to see how to convert a virtual address into a physical address.
We know that linux uses a paging mechanism, usually using four-level page table, page global catalog (PGD), page parent catalog (PUD), page middle directory (PMD), page table (PTE). As follows:
Read the base address of the physical page where the page directory is located from the CR3 register (that is, the so-called page directory base address), obtain the index of the page directory entry from the first part of the linear address, and add them to get the physical address of the page directory entry. Read the memory for the first time to get the directory entry of the pgd_t structure, from which the physical page base address is taken out, that is, the physical base address of the page parent page directory. The index of the page parent directory entry is extracted from the second part of the linear address, and the physical address of the page parent directory entry is obtained by adding the base address of the page parent directory. The second time the memory is read, the directory entry of the pud_t structure is obtained, from which the physical base address of the middle directory of the page is fetched. The index of the directory item in the middle of the page is extracted from the third part of the linear address, and the physical address of the directory item in the middle of the page is obtained by adding the base address of the directory in the middle of the page. The third time the memory is read to get the directory entry of the pmd_t structure, from which the physical base address of the page table is extracted. The index of the page table item is taken from the fourth part of the linear address and added to the page table base address to get the physical address of the page table item. The fourth time reading memory gets the directory entry of the pte_t structure, from which the base address of the physical page is fetched. The offset of the physical page is taken from the fifth part of the linear address and added to the physical page base address to get the final physical address. Read the memory for the fifth time to get the final data to be accessed.
The whole process is relatively mechanical, each translation first obtains the physical page base address, then obtains the index from the linear address, and then accesses the memory after synthesizing the physical address. Both the page table and the data to be accessed are stored in main memory on a page-by-page basis, so each time memory is accessed, the base address is obtained first, and then the data is accessed within the page through an index (or offset). So you can think of a linear address as a collection of several indexes.
Implementation of address translation in linux / * describe page table items in page tables at all levels * /
Typedef struct {pteval_t pte;} pte_t
Typedef struct {pmdval_t pmd;} pmd_t
Typedef struct {pudval_t pud;} pud_t
Typedef struct {pgdval_t pgd;} pgd_t
/ * convert the page table item type to an unsigned type * /
# define pte_val (x) ((x) .pte)
# define pmd_val (x) ((x) .pmd)
# define pud_val (x) ((x) .pud)
# define pgd_val (x) ((x) .PG d)
/ * convert an unsigned type to a page table item type * /
# define _ _ pte (x) ((pte_t) {(x)})
# define _ _ pmd (x) ((pmd_t) {(x)})
# define _ _ pud (x) ((pud_t) {(x)})
# define _ _ pgd (x) ((pgd_t) {(x)})
/ * get the index value of the page table item * /
# define pgd_index (addr) (addr) > > PGDIR_SHIFT) & (PTRS_PER_PGD-1))
# define pud_index (addr) (addr) > > PUD_SHIFT) & (PTRS_PER_PUD-1))
# define pmd_index (addr) (addr) > > PMD_SHIFT) & (PTRS_PER_PMD-1))
# define pte_index (addr) (addr) > > PAGE_SHIFT) & (PTRS_PER_PTE-1))
/ * get the offset value of entry in the page table * /
# define pgd_offset (mm, addr) (pgd_offset_raw ((mm)-> pgd, (addr)
# define pgd_offset_k (addr) pgd_offset (& init_mm, addr)
# define pud_offset_phys (dir, addr) (pgd_page_paddr (* (dir)) + pud_index (addr) * sizeof (pud_t))
# define pud_offset (dir, addr) ((pud_t *) _ _ va (pud_offset_phys ((dir), (addr)
# define pmd_offset_phys (dir, addr) (pud_page_paddr (* (dir)) + pmd_index (addr) * sizeof (pmd_t))
# define pmd_offset (dir, addr) ((pmd_t *) _ _ va (pmd_offset_phys ((dir), (addr)
# define pte_offset_phys (dir,addr) (pmd_page_paddr (READ_ONCE (* (dir) + pte_index (addr) * sizeof (pte_t))
# define pte_offset_kernel (dir,addr) ((pte_t *) _ va (pte_offset_phys ((dir), (addr)
When a process switches, it finds the PGD field in mm_struct according to task_struct, obtains the page global catalog of the new process, and then fills it into the CR3 register to complete the page switch.
Let's get started and get a deep understanding of how virtual addresses are converted into physical addresses through the code.
# include
# include
# include
# include
# include
# include
# include
# include
MODULE_DESCRIPTION ("vitual address to physics address")
Static int pid
Static unsigned long va
Module_param (pid,int,0644); / / pass parameters (variables, types, permissions) from the command line
Module_param (va,ulong,0644); / / va represents a virtual address
Static int find_pgd_init (void)
{
Unsigned long pa = 0; / / physical address represented by pa
Struct task_struct * pcb_tmp = NULL
Pgd_t * pgd_tmp = NULL
Pud_t * pud_tmp = NULL
Pmd_t * pmd_tmp = NULL
Pte_t * pte_tmp = NULL
Printk (KERN_INFO "PAGE_OFFSET = 0x%lx\ n", PAGE_OFFSET); / / how many items are there in the page table
/ * how many bits are occupied by pud, pmd and so on in linear addresses * /
Printk (KERN_INFO "PGDIR_SHIFT =% d\ n", PGDIR_SHIFT)
/ / Note: PGD and PUD are the same in a 32-bit system
Printk (KERN_INFO "PUD_SHIFT =% d\ n", PUD_SHIFT)
Printk (KERN_INFO "PMD_SHIFT =% d\ n", PMD_SHIFT)
Printk (KERN_INFO "PAGE_SHIFT =% d\ n", PAGE_SHIFT)
Printk (KERN_INFO "PTRS_PER_PGD =% d\ n", PTRS_PER_PGD); / / how many ptrs are there in each PGD
Printk (KERN_INFO "PTRS_PER_PUD =% d\ n", PTRS_PER_PUD)
Printk (KERN_INFO "PTRS_PER_PMD =% d\ n", PTRS_PER_PMD); / / how many items are there in PMD
Printk (KERN_INFO "PTRS_PER_PTE =% d\ n", PTRS_PER_PTE)
Printk (KERN_INFO "PAGE_MASK = 0x%lx\ n", PAGE_MASK); / / the mask of the page
Struct pid * p = NULL
P = find_vpid (pid); / / find the structure of struct pid through the pid number of the process
Pcb_tmp = pid_task (pforce pid pid); / / find the task struct of the process through the structure of the PIDT
Printk (KERN_INFO "pgd = 0x%p\ n", pcb_tmp- > mm- > pgd)
/ / determine whether the given address va is legal (vamm,va)) {
Printk (KERN_INFO "virt_addr 0x%lx not available.\ n", va)
Return 0
}
Pgd_tmp = pgd_offset (pcb_tmp- > mm,va); / / returns the linear address va, which corresponds to the linear address of the table item in the page global catalog
Printk (KERN_INFO "pgd_tmp = 0x%p\ n", pgd_tmp)
/ / pgd_val gets the page global catalog entry referred to by pgd_tmp
/ / pgd_val is to print out the values in pgd_tmp
Printk (KERN_INFO "pgd_val (* pgd_tmp) = 0x%lx\ n", pgd_val (* pgd_tmp))
If (pgd_none (* pgd_tmp)) {/ / determine whether pgd is mapped or not
Printk (KERN_INFO "Not mapped in pgd.\ n")
Return 0
}
Pud_tmp = pud_offset (pgd_tmp,va); / / returns the linear address of the parent directory entry of the page corresponding to va
Printk (KERN_INFO "pud_tmp = 0x%p\ n", pud_tmp)
Printk (KERN_INFO "pud_val (* pud_tmp) = 0x%lx\ n", pud_val (* pud_tmp))
If (pud_none (* pud_tmp)) {
Printk (KERN_INFO "Not mapped in pud.\ n")
Return 0
}
Pmd_tmp = pmd_offset (pud_tmp,va); / / returns the linear address of the va corresponding to the table item in the middle of the page directory
Printk (KERN_INFO "pmd_tmp = 0x%p\ n", pmd_tmp)
Printk (KERN_INFO "pmd_val (* pmd_tmp) = 0x%lx\ n", pmd_val (* pmd_tmp))
If (pmd_none (* pmd_tmp)) {
Printk (KERN_INFO "Not mapped in pmd.\ n")
Return 0
}
/ / here, change the original pte_offset_map () to pte_offset_kernel
Pte_tmp = pte_offset_kernel (pmd_tmp,va); / / pte means to find the table
Printk (KERN_INFO "pte_tmp = 0x%p\ n", pte_tmp)
Printk (KERN_INFO "pte_val (* pte_tmp) = 0x%lx\ n", pte_val (* pte_tmp))
If (pte_none (* pte_tmp)) {/ / determine whether there is a mapping
Printk (KERN_INFO "Not mapped in pte.\ n")
Return 0
}
If (! pte_present (* pte_tmp)) {
Printk (KERN_INFO "pte not in RAM.\ n")
Return 0
}
Pa = (pte_val (* pte_tmp) & PAGE_MASK); / / calculation method of physical address
Printk (KERN_INFO "virt_addr 0x%lx in RAM Page is 0x%lx.\ n", va,pa)
/ / printk (KERN_INFO "contect in 0x%lx is 0x%lx\ n", pa,* (unsigned long *) ((char *) pa + PAGE_OFFSET))
Return 0
}
Static void _ exit find_pgd_exit (void)
{
Printk (KERN_INFO "Goodbye!\ n")
}
Module_init (find_pgd_init)
Module_exit (find_pgd_exit)
MODULE_LICENSE ("GPL")
The running result is as follows: you can see that the physical address corresponding to the virtual address ffff99b488d48000 is 80000000c8d48000. This process is also the process of mmu.
This is the end of the content of "what is the process of MMU in Linux memory management". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 270
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.