1. Page-related data structures and macro definitions
The paging mechanism is the hardware support for paging, which is the hardware basis for virtual memory management. To make this hardware mechanism fully functional, you must have the support of the corresponding software. Let's take a look at some of the main data structures defined in Linux, the page is distributed in the include/asm-i386/directory. h, pgtable. H and pgtable-2level.h in three files.
1. Definition of table items
As described above, table items in PGD, PMD, and PT tables all occupy 4 bytes. Therefore, they are defined as unsigned long integers, pgd_t, pmd_t, and pte_t (Pte is the page table entry. H is defined as follows:
Typedef struct {unsignedlong pte_low;} pte_t;
Typedef struct {unsignedlong PMD;} pmd_t;
Typedef struct {unsigned long PGD;} pgd_t;
Typedefstruct {unsigned long pgprot;} pgprot_t;
It can be seen that Linux does not define long integers for these types but a structure to enable GCC to perform more rigorous type checks during compilation. In addition, several macros are defined to access the components of these structures, which is also an embodiment of Object-oriented thinking:
# Definepte_val (x). pte_low)
# Define pmd_val (x). pMD)
# Define pgd_val (x). PGD)
As shown in Figure 2.13, these table items should be defined as bit segments, but the kernel is not defined as such. Instead, a page protection structure pgprot_t and some macros are defined:
Typedefstruct {unsigned long pgprot;} pgprot_t;
# Definepgprot_val (x). pgprot)
The pgprot value of the field corresponds to the low 12 phase of the table in Figure 2.13, and the nine digits correspond to 0 ~ The corresponding macro is defined in pgtalbe. h:
# DEFINE _ page_present 0 × 001
# DEFINE _ page_rw 0 × 002
# DEFINE _ page_user 0 × 004
# DEFINE _ page_pwt 0 × 008
# DEFINE _ page_pcd 0 × 010
# DEFINE _ page_accessed 0x020
# DEFINE _ page_dirty 0 × 040
# DEFINE _ page_pse 0x080/* 4 MB (or 2 MB) page, Pentium +, if present ..*/
# DEFINE _ page_global 0x100/* Global TLB entry ppro + */
When you read the source code, you can understand that defining a flag as a macro rather than a bit segment is more conducive to encoding.
In addition, the page Directory table and page table are defined in pgtable. h as follows:
Externpgd_t swapper_pg_dir [1024];
Externunsigned long pg0 [2, 1024];
Swapper_pg_dir is a temporary page Directory table, which is statically initialized during kernel compilation. Pg0 is a temporary page table used during initialization.
2. Linear address domain definition
(1) number of digits of page offset
# Define page_shift 12
# Define page_size (1ul
# Include # Include # Include # Include # Include
# Include
Module_license ("GPL ");
Static int PID;
Static unsigned long Va;
Module_param (PID, Int, 0644 );
Module_param (va, ulong, 0644 );
Static int find_pgd_init (void)
{
Unsigned long Pa = 0;
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 );
Printk (kern_info "pgdir_shift = % d/N", pgdir_shift );
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 );
Printk (kern_info "ptrs_per_pud = % d/N", ptrs_per_pud );
Printk (kern_info "ptrs_per_pmd = % d/N", ptrs_per_pmd );
Printk (kern_info "ptrs_per_pte = % d/N", ptrs_per_pte );
Printk (kern_info "page_mask = 0x % LX/N", page_mask );
If (! (Pcb_tmp = find_task_by_pid (PID ))){
Printk (kern_info "can't find the task % d./N", pid );
Return 0;
}
Printk (kern_info "PGD = 0x % P/N", pcb_tmp-> MM-> PGD );
/* Determine whether the given address VA is valid (va <vm_end )*/
If (! Find_vma (pcb_tmp-> MM, VA )){
Printk (kern_info "cmd_addr 0x % lx not available./N", VA );
Return 0;
}
Pgd_tmp = pgd_offset (pcb_tmp-> MM, VA );
Printk (kern_info "pgd_tmp = 0x % P/N", pgd_tmp );
Printk (kern_info "pgd_val (* pgd_tmp) = 0x % LX/N", pgd_val (* pgd_tmp ));
If (pgd_none (* pgd_tmp )){
Printk (kern_info "not mapped in PGD./N ");
Return 0;
}
Pud_tmp = pud_offset (pgd_tmp, 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 );
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 );
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 )){
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) | (va &~ Page_mask );
Printk (kern_info "cmd_addr 0x % lx in Ram 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 find_pgd_exit (void)
{
Printk (kern_info "goodbye! /N ");
}
Module_init (find_pgd_init );
Module_exit (find_pgd_exit );
Test: Open gedit, and then open the task manager. Check that the process ID of gedit is pid = 12749,
Right-click its memory ing and find a valid virtual address Va = 0xb8041000. Then:
Sudo insmod mem. Ko pid = 12749 Va = 0xb8041000
If your kernel is later than 2.6.24, change find_task_by_pid to find_task_by_vpid.
.
Result: pid = 12749 Va = 0xb8041000