【Linux内核分析与应用-陈莉君老师】动手实践-把虚拟内存转换成物理地址

虚拟内存--虚拟内存到物理内存的映射 系统实现地址翻译的大致流程 阅读详情

目录

 

1.地址转换

2.Linux4级页表

3.64位线性地址

4.实际观察寻页的过程 


1.地址转换

进程中不直接对物理地址直接操作,CPU在运行时指定的地址需要经过内存管理单元MMU
转换后才能访问到真正的物理内存,地址转换的过程分为两块:分段和分页.
分段机制简单地来说就是将进程的代码,数据,栈分在不同的虚拟地址段上,从而避免
进程间的互相影响,分段之前的地址我们称之为逻辑地址,由两部分组成:高位的段选择
符和低位的段内偏移,在分段时,我们先用段选择符在相应的段描述符表中找到段描述符--
也就是某个段的基地址,再加上段内偏移量就得到对应的线性地址,线性地址也称之为
虚拟地址,在实际应用中linux为了增加可移植性并没有完整的使用分段机制,它将所有的
段都指向相同的地址范围,段的基地址都为0,这样逻辑地址和线性地址在数值上就相同了.
所以今天分析的重点在分页,也就是由线性地址到物理地址转换的过程.

2.Linux4级页表

linux为了兼容32和64位的CPU,需要一个统一的页面地址模型.目前常用的就是四级页表模型,
里面有PGD-页全局目录,PUD-页上级目录,PMD-页中级目录,PTE-页表这四个部分.
根据不同的需要其中的部分页表可能未被使用,线性地址(图中的linear Address)中
的每部分索引的大小会根据计算机体系结构灵活调整,比如说:
对于没有启用物理地址扩展功能的32位系统来说,两级页表就足够了,linux首先会让在
线性地址中将页上级目录索引和页中级目录索引这两位置为0,从根本上上取消这两个字段,
但是这两个页目录在指针序列中的位置仍然被保留下来,也就是说寻址的过程中不能跳过
页上级目录和页中级目录直接由页全局目录到页表,内核会将这两个页目录的表项都置为1.

3.64位线性地址

由于64位处理器硬件的限制,它的地址线只有48条,所以线性地址实际使用的也只有48位,
在linux 64位中使用了4级页表的结构,线性地址的划分如图,其中
PGD-页全局目录索引
PUD-页上级目录索引
PMD-页中级目录索引
PTE-页表的索引
分别占了9位,页内偏移占了12位,共计48位,剩下的高位都保留,留作以后扩展使用,
这种情况下,每一个页面的大小都是4K,每个页表项的大小是8bits,这个页表可以映射
的空间是256TB,而新的intel芯片的MMU硬件规定可以进行5级的页表管理,所以在4.15
的内核中,linux已经在页全局目录和页上级目录之间添加了一个新的页目录(P4D页目录),
这个页目录同32位中的情况中一样,现在还没有被使用.它的页目录项只有一个,
目前的线性地址中也没有它的索引位置.
此处有一个非常重要的寄存器,CR3,这个寄存器中CPU中一些列CPU控制寄存器之一,这些
控制寄存器主要是用来保存控制系统级别的操作和信息的,这个CR3就是用来保存当前
进程的页全局目录的地址的,寻址就是从这个页全局目录开始的,页全局目录的地址又在哪里呢?
内核在创建一个进程的时候就会为它分配页全局目录,在进程描述符task_struct结构中有一个
指向mm_struct结构的指针mm,而mm_struct这个结构就是用来描述进程的虚拟地址空间的,在
mm_struct中有一个字段pgd就是保存该进程的全局目录的地址的,所以在进程切换的时候,
操作系统通过访问task_struct中的mm_struct结构中的pgd字段取得新进程的页全局目录地址,
填充到CR3寄存器中,就完成了页表的切换.

4.实际观察寻页的过程 

在内核中先申请一个页面然后用内核提供的函数按照寻页的步骤一步一步查询各级页目录,
最终找到所对应的物理地址.这些步骤就相当于我们手动模拟了MMU的寻页过程.

/*
page_lowmem.c
*/

#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/mm_types.h>
#include <linux/sched.h>
#include <linux/export.h>
static unsigned long cr0,cr3;
static unsigned long vaddr = 0;
// 打印页机制中的重要参数
static void get_pgtable_macro(void)
{
        cr0 = read_cr0();
        cr3 = read_cr3();
        printk("cr0 = 0x%lx,cr3 = 0x%lx\n",cr0,cr3);
        printk("PGDIR_SHIFT = %d\n",PGDIR_SHIFT);
//      printk("P4D_SHIFT = %d\n",P4D_SHIFT);
        printk("PUD_SHIFT = %d\n",PUD_SHIFT);
        printk("PMD_SHIFT = %d\n",PMD_SHIFT);// 这些宏用来指示线性地址中相应字段所能够映射区域的大小的对数
        printk("PAGE_SHIFT = %d\n",PAGE_SHIFT);// 大小的以2为底的对数,4K对应的对数大小是12

        printk("PTRS_PER_PGD = %d\n",PTRS_PER_PGD);
//      printk("PTRS_PER_P4D = %d\n",PTRS_PER_P4D);
        printk("PTRS_PER_PUD = %d\n",PTRS_PER_PUD);
        printk("PTRS_PER_PMD = %d\n",PTRS_PER_PMD);
        printk("PTRS_PER_PTE = %d\n",PTRS_PER_PTE);// 这些宏是用来指示相应的页目录表中项的个数,为了后面寻页时进行位运算的
        printk("PAGE_MASK = 0x%lx\n",PAGE_MASK);// 页内偏移掩码,用来屏蔽page_off字段的
}
// 线性地址到物理地址转换的函数
static unsigned long vaddr2paddr(unsigned long vaddr)
{
        pgd_t *pgd;
//      p4d_t *p4d;
        pud_t *pud;
        pmd_t *pmd;
        pte_t *pte;
        unsigned long paddr = 0;
        unsigned long page_addr = 0;
        unsigned long page_offset = 0;
        pgd = pgd_offset(current->mm,vaddr);
        printk("pgd_val = 0x%lx,pgd_index = %lu\n",pgd_val(*pgd),pgd_index(vaddr));
        if(pgd_none(*pgd))
        {
                printk("not mapped in pgd");
                return -1;
        }
        /*这个需要4.15以上版本的内核才支持,我的实验的内核版本是4.10的,所以将此段注释掉
        p4d = pdg_offset(pgd,vaddr);
        printk("p4d_val = 0x%lx,p4d_index = %lu\n",p4d_val(*p4d),p4d_index(vaddr));
        
        if(p4d_none(*p4d))
        {
                printk("not mapped in p4d");
                return -1;
        }
        */
        pud = pud_offset(pgd,vaddr);
        printk("pud_val = 0x%lx,pud_index = %lu\n",pud_val(*pud),pud_index(vaddr));
        if(pud_none(*pud))
        {
                printk("not mapped in pud");
                return -1;
        }

        pmd = pmd_offset(pud,vaddr);
        printk("pmd_val = 0x%lx,pmd_index = %lu\n",pmd_val(*pmd),pmd_index(vaddr));
        if(pmd_none(*pmd))
        {
                printk("not mapped in pmd\n");
                return -1;
        }

        pte = pte_offset_kernel(pmd,vaddr);
        printk("pte_val = 0x%lx,pte_index = %lu\n",pte_val(*pte),pte_index(vaddr));
        if(pte_none(*pte))
        {
                printk("not mapped in pte line 76\n");
                return -1;
        }
        // 从页表项中取出该页表所映射页框的物理地址,将其与PAGE_MASK做位或操作,取出其高48位,就得到了页框的物理地址
        page_addr = pte_val(*pte) & PAGE_MASK;
        // 取出线性地址中的第12位,将 PAGE_MASK按位取反,然后与vaddr做了位或的操作,然后取出页内偏移
        page_offset = vaddr & ~PAGE_MASK;
        // 将上面两个地址拼接起来就可以得到想要的物理地址
        paddr = page_addr | page_offset;
        printk("line 85 --- page_addr = %lx,page_offset = %lx\n",page_addr,page_offset);
        printk("line 86 --- vaddr = %lx,paddr = %lx\n",vaddr,paddr);
        return paddr;
}
static int __init v2p_init(void)
{
        unsigned long vaddr = 0;
        printk("vaddr to paddr is running .\n");
        get_pgtable_macro();
        printk("\n");
        vaddr = __get_free_page(GFP_KERNEL);
        if(vaddr == 0 )
        {
                printk("get free page failed.\n");
                return 0;
        }
        sprintf((char*) vaddr,"hello world from kernel");
        printk("get_page_vaddr=0x%lx",vaddr);
        vaddr2paddr(vaddr);
        return 0;
}
static void __exit v2p_exit(void)
{
        printk("vaddr to paddr module is leaving.\n");
        free_page(vaddr);
}
module_init(v2p_init);
module_exit(v2p_exit);
                                          
//-------------------------------------------------------------------
//	dram.c
//
//	This module implements a Linux character-mode device-driver
//	for the processor's installed physical memory.  It utilizes
//	the kernel's 'kmap()' function, as a uniform way to provide  
//	access to all the memory-zones (including the "high memory"
//	on systems with more than 896MB of installed physical ram). 
//	The access here is 'read-only' because we deem it too risky 
//	to the stable functioning of our system to allow every user
//	the unrestricted ability to arbitrarily modify memory-areas
//	which might contain some "critical" kernel data-structures.
//	We implement an 'llseek()' method so that users can readily 
//	find out how much physical processor-memory is installed. 
//
//	NOTE: Developed and tested with Linux kernel version 2.6.10
//
//	programmer: ALLAN CRUSE
//	written on: 30 JAN 2005
//	revised on: 28 JAN 2008 -- for Linux kernel version 2.6.22.5
//	revised on: 06 FEB 2008 -- for machines having 4GB of memory
//-------------------------------------------------------------------

#include <linux/module.h>	// for module_init() 
#include <linux/highmem.h>	// for kmap(), kunmap()
#include <asm/uaccess.h>	// for copy_to_user() 

char modname[] = "dram";	// for displaying driver's name
int my_major = 85;		// note static major assignment 
loff_t 	dram_size;		// total bytes of system memory

loff_t my_llseek( struct file *file, loff_t offset, int whence );
ssize_t my_read( struct file *file, char *buf, size_t count, loff_t *pos );

struct file_operations 
my_fops =	{
		owner:		THIS_MODULE,
		llseek:		my_llseek,
		read:		my_read,
		};

static int __init dram_init( void )
{
	printk( "<1>\nInstalling \'%s\' module ", modname );
	printk( "(major=%d)\n", my_major );
	
	dram_size = (loff_t)num_physpages << PAGE_SHIFT;
	printk( "<1>  ramtop=%08llX (%llu MB)\n", dram_size, dram_size >> 20 );
	return 	register_chrdev( my_major, modname, &my_fops );
}

static void __exit dram_exit( void )
{
	unregister_chrdev( my_major, modname );
	printk( "<1>Removing \'%s\' module\n", modname );
}

ssize_t my_read( struct file *file, char *buf, size_t count, loff_t *pos )
{
	struct page	*pp;
	void		*from;
	int		page_number, page_indent, more;
	
	// we cannot read beyond the end-of-file
	if ( *pos >= dram_size ) return 0;

	// determine which physical page to temporarily map
	// and how far into that page to begin reading from 
	page_number = *pos / PAGE_SIZE;
	page_indent = *pos % PAGE_SIZE;
	
	// map the designated physical page into kernel space
	/*If kerel vesion is 2.6.32 or later, please use pfn_to_page() to get page, and include
	    asm-generic/memory_model.h*/
#if 0
       pp = pfn_to_page( page_number);
#else
	pp = &mem_map[ page_number ];
#endif
	
	from = kmap( pp ) + page_indent;
	
	// cannot reliably read beyond the end of this mapped page
	if ( page_indent + count > PAGE_SIZE ) count = PAGE_SIZE - page_indent;

	// now transfer count bytes from mapped page to user-supplied buffer 	
	more = copy_to_user( buf, from, count );
	
	// ok now to discard the temporary page mapping
	kunmap( pp );
	
	// an error occurred if less than count bytes got copied
	if ( more ) return -EFAULT;
	
	// otherwise advance file-pointer and report number of bytes read
	*pos += count;
	return	count;
}

loff_t my_llseek( struct file *file, loff_t offset, int whence )
{
	loff_t	newpos = -1;

	switch( whence )
		{
		case 0: newpos = offset; break;			// SEEK_SET
		case 1: newpos = file->f_pos + offset; break; 	// SEEK_CUR
		case 2: newpos = dram_size + offset; break; 	// SEEK_END
		}

	if (( newpos < 0 )||( newpos > dram_size )) return -EINVAL;
	file->f_pos = newpos;
	return	newpos;
}

MODULE_LICENSE("GPL");
module_init( dram_init );
module_exit( dram_exit );
//----------------------------------------------------------------
//	fileview.cpp
//
//	This program displays the contents of a specified file 
//	in hexadecimal and ascii formats (including any device
//	special files representing storage media).  A user may
//	navigate the file's contents using arrow-key commands,
//	or may adjust the format of the hexadecimal display to
//	select from among five data-sizes: byte (B), word (W), 
//	doubleword (D), quadword (Q) or octaword (O).  It also
//	is possible to seek to a specified position within the
//	file by hitting the <ENTER>-key and then typing in the
//	desired (hexadecimal) address.  Type <ESCAPE> to quit.  
//
//	       compile-and-link using: $ make fileview
//
//	programmer: ALLAN CRUSE
//	written on: 26 OCT 2002
//	revised on: 07 JUN 2006 -- removed reliance on 'ncurses' 
//----------------------------------------------------------------

#include <stdio.h>	// for printf(), perror(), fflush() 
#include <fcntl.h>	// for open()
#include <string.h>	// for strncpy()
#include <unistd.h>	// for read(), lseek64()
#include <stdlib.h>	// for exit()
#include <termios.h>	// for tcgetattr(), tcsetattr()

#define MAXNAME	80
#define BUFHIGH 16
#define BUFWIDE 16
#define BUFSIZE 256
#define ROW	6
#define COL	2

#define KB_SEEK 0x0000000A
#define KB_QUIT	0x0000001B
#define KB_BACK 0x0000007F
#define KB_HOME	0x00315B1B
#define KB_LNUP 0x00415B1B
#define KB_PGUP	0x00355B1B
#define KB_LEFT 0x00445B1B
#define KB_RGHT 0x00435B1B
#define KB_LNDN 0x00425B1B
#define KB_PGDN 0x00365B1B
#define KB_END  0x00345B1B
#define KB_DEL  0x00335B1B


char progname[] = "FILEVIEW";
char filename[ MAXNAME + 1 ];
char buffer[ BUFSIZE ];
char outline[ 80 ];

int main( int argc, char *argv[] )
{
	// setup the filename (if supplied), else terminate
	if ( argc > 1 ) strncpy( filename, argv[1], MAXNAME );
 	else { fprintf( stderr, "argument needed\n" ); exit(1); }

	// open the file for reading
	int	fd = open( filename, O_RDONLY );
	if ( fd < 0 ) { perror( filename ); exit(1); }

	// obtain the filesize (if possible)
	long long	filesize = lseek64( fd, 0LL, SEEK_END );
	if ( filesize < 0LL ) 
		{ 
		fprintf( stderr, "cannot locate \'end-of-file\' \n" ); 
		exit(1); 
		}

	long long	incmin = ( 1LL <<  8 );
	long long	incmax = ( 1LL << 36 );		
	long long	posmin = 0LL;
	long long	posmax = (filesize - 241LL)&~0xF;
	if ( posmax < posmin ) posmax = posmin;

	// initiate noncanonical terminal input
	struct termios	tty_orig;
	tcgetattr( STDIN_FILENO, &tty_orig );
	struct termios	tty_work = tty_orig;
	tty_work.c_lflag &= ~( ECHO | ICANON );  // | ISIG );
	tty_work.c_cc[ VMIN ]  = 1;
	tty_work.c_cc[ VTIME ] = 0;
	tcsetattr( STDIN_FILENO, TCSAFLUSH, &tty_work );	
	printf( "\e[H\e[J" );

	// display the legend
	int	i, j, k;
	k = (77 - strlen( progname ))/2;
	printf( "\e[%d;%dH %s ", 1, k, progname );
	k = (77 - strlen( filename ))/2;
	printf( "\e[%d;%dH\'%s\'", 3, k, filename );
	char	infomsg[ 80 ];
	sprintf( infomsg, "filesize: %llu (=0x%013llX)", filesize, filesize );
	k = (78 - strlen( infomsg ));
	printf( "\e[%d;%dH%s", 24, k, infomsg );
	fflush( stdout );

	// main loop to navigate the file
	long long	pageincr = incmin;
	long long	lineincr = 16LL;
	long long	position = 0LL;
	long long	location = 0LL;
	int		format = 1;
	int		done = 0;
	while ( !done )
		{
		// erase prior buffer contents
		for (j = 0; j < BUFSIZE; j++) buffer[ j ] = ~0;

		// restore 'pageincr' to prescribed bounds
		if ( pageincr == 0LL ) pageincr = incmax;
		else if ( pageincr < incmin ) pageincr = incmin;
		else if ( pageincr > incmax ) pageincr = incmax;
		
		// get current location of file-pointer position
		location = lseek64( fd, position, SEEK_SET );
		
		// try to fill 'buffer[]' with data from the file
		char	*where = buffer;
		int	to_read = BUFSIZE;
		while ( to_read > 0 )
			{
			int	nbytes = read( fd, where, to_read );
			if ( nbytes <= 0 ) break; 
			to_read -= nbytes;
			where += nbytes;
			}
		int	datalen = BUFSIZE - to_read; 

		// display the data just read into the 'buffer[]' array
		unsigned char		*bp;
		unsigned short		*wp;
		unsigned int		*dp;
		unsigned long long	*qp;
		for (i = 0; i < BUFHIGH; i++)
			{
			int	linelen;

			// draw the line-location (13-digit hexadecimal)
			linelen = sprintf( outline, "%013llX ", location );
			
			// draw the line in the selected hexadecimal format
			switch ( format )
				{
				case 1:	// 'byte' format
				bp = (unsigned char*)&buffer[ i*BUFWIDE ];
				for (j = 0; j < BUFWIDE; j++)
					linelen += sprintf( outline+linelen, 
						"%02X ", bp[j] );
				break;

				case 2:	// 'word' format
				wp = (unsigned short*)&buffer[ i*BUFWIDE ];
				for (j = 0; j < BUFWIDE/2; j++)
					linelen += sprintf( outline+linelen,
						" %04X ", wp[j] );
				break;

				case 4:	// 'dword' format
				dp = (unsigned int*)&buffer[ i*BUFWIDE ];
				for (j = 0; j < BUFWIDE/4; j++)
					linelen += sprintf( outline+linelen,
						"  %08X  ", dp[j] );
				break;

				case 8:	// 'qword' format
				qp = (unsigned long long*)&buffer[ i*BUFWIDE ];
				for (j = 0; j < BUFWIDE/8; j++)
					linelen += sprintf( outline+linelen,
						"    %016llX    ", qp[j] );
				break;

				case 16: // 'octaword'
				qp = (unsigned long long*)&buffer[ i*BUFWIDE ];
				linelen += sprintf( outline+linelen, "     " );
				linelen += sprintf( outline+linelen, 
					"   %016llX%016llX   ", qp[1], qp[0] );
				linelen += sprintf( outline+linelen, "     " ); 
				break;
				}

			// draw the line in ascii format
			for (j = 0; j < BUFWIDE; j++)
				{
				char	ch = buffer[ i*BUFWIDE + j ];
				if (( ch < 0x20 )||( ch > 0x7E )) ch = '.';
				linelen += sprintf( outline+linelen, "%c", ch);
				}

			// transfer this output-line to the screen 
			printf( "\e[%d;%dH%s", ROW+i, COL, outline );

			// advance 'location' for the next output-line
			location += BUFWIDE;
			} 	
		printf( "\e[%d;%dH", 23, COL );
		fflush( stdout );	
	
		// await keypress 	
		long long	inch = 0LL;
		read( STDIN_FILENO, &inch, sizeof( inch ) );
		printf( "\e[%d;%dH%60s", 23, COL, " " );	

		// interpret navigation or formatting command
		inch &= 0x00FFFFFFLL;
		switch ( inch )
			{
			// move to the file's beginning/ending
			case 'H': case 'h':
			case KB_HOME:	position = posmin; break;
			case 'E': case 'e':
			case KB_END:	position = posmax; break;

			// move forward/backward by one line
			case KB_LNDN:	position += BUFWIDE; break;
			case KB_LNUP:	position -= BUFWIDE; break;

			// move forward/packward by one page
			case KB_PGDN:	position += pageincr; break;
			case KB_PGUP:	position -= pageincr; break;

			// increase/decrease the page-size increment
			case KB_RGHT:	pageincr >>= 4; break;
			case KB_LEFT:	pageincr <<= 4; break;

			// reset the hexadecimal output-format
			case 'B': case 'b':	format = 1; break;
			case 'W': case 'w':	format = 2; break;
			case 'D': case 'd':	format = 4; break;
			case 'Q': case 'q':	format = 8; break;
			case 'O': case 'o':	format = 16; break;

			// seek to a user-specified file-position
			case KB_SEEK:
			printf( "\e[%d;%dHAddress: ", 23, COL );
			fflush( stdout );
			{
			char	inbuf[ 16 ] = {0};
				//tcsetattr( STDIN_FILENO, TCSANOW, &tty_orig );
			int	i = 0;
			while ( i < 15 )
				{
				long long	ch = 0;
				read( STDIN_FILENO, &ch, sizeof( ch ) );
				ch &= 0xFFFFFF;
				if ( ch == '\n' ) break;
				if ( ch == KB_QUIT ) { inbuf[0] = 0; break; }
				if ( ch == KB_LEFT ) ch = KB_BACK;
				if ( ch == KB_DEL ) ch = KB_BACK;
				if (( ch == KB_BACK )&&( i > 0 ))
					{ 
					inbuf[--i] = 0; 
					printf( "\b \b" ); 
					fflush( stdout );
					}
				if (( ch < 0x20 )||( ch > 0x7E )) continue;
				inbuf[ i++ ] = ch;
				printf( "%c", ch );
				fflush( stdout );
				}		
			printf( "\e[%d;%dH%70s", 23, COL, " " );
			fflush( stdout );
			position = strtoull( inbuf, NULL, 16 );
			position &= ~0xFLL;	// paragraph align
			}
			break;			

			// program termination 
			case KB_QUIT:	done = 1; break;

			default:	
			printf( "\e[%d;%dHHit <ESC> to quit", 23, 2 ); 
			}
		fflush( stdout );

		// insure that 'position' remains within bounds
		if ( position < posmin ) position = posmin;
		if ( position > posmax ) position = posmax;
		}	

	// restore canonical terminal behavior
	tcsetattr( STDIN_FILENO, TCSAFLUSH, &tty_orig );	
	printf( "\e[%d;%dH\e[0J\n", 23, 0 );
}

 

 

 

 

 

pmd_alloc/pte_alloc/pud_alloc设置页表 pte_t;} pmd_t;} pud_t; 阅读详情

相关推荐

MIPI DPHY接口(基于Xilinx FPGA实现)案例分析

在Xilinx FPGA实现MIPI DPHY接口的案例(包括CIS协议层)。截止目前为止,Xilinx仅在Ultrascale+及其以上版本的FPGA IO可直接支持MIPI 电平输入,其他的,都需要转换成LVDS来接收。

ONEFPGA的博客 5631

(实验)把虚地址转化为物理地址·

dram内核模块:通过mmap将物理内存中的数据映射到设备文件==中,我们通过对于这个设备文件进行访问,就可以达到访问物理内存的功能了;fileview:按照想要的格式阅读这种二进制文件;《Linux内核分析与应用》动手实践源码/2.4动手实践-把虚拟地址转换成物理地址/Access_Physical_Memory · ljrcore/linuxmooc - 码云 - 开源中国 (gitee.com)

abobob的博客 801

彻底清理Easy Connect残留:从服务到驱动的完整卸载指南

在企业网络接入场景中,远程客户端常因内核驱动、系统服务和网络过滤组件导致卸载残留,进程无法结束、文件被锁定、注册表项顽固存在。其原理在于驱动挂载网络协议栈,服务守护进程,卸载时资源占用导致清理不彻底。彻底清理的技术价值在于恢复系统纯净、避免网络异常和软件冲突。典型应用场景包括企业办公机、个人笔记本更换或重装客户端。本文针对深信服Easy Connect,提供从停止服务、删除驱动到清理注册表和虚拟网卡的分步操作链路,并给出验证方法和避坑经验。

weixin_31292729的博客 245

linux通过进程虚拟地址找到物理地址

通过进程id找到对应的task_struct,就得到了cr3中存储着的页表首地址,然后同ing过页表找到最后页的物理地址 static pte_t *get_pte(struct task_struct *task, unsigned long address) { pgd_t* pgd; p4d_t* p4d; pud_t* pud; pmd_t* pmd; pte_t* pte; struct mm_struct *mm = task->mm; // mm里面存储了最高级页

YLSnowy的博客 1038

40、基于指令的GPU编程:OpenACC与OpenMP的深入解析

本文深入解析了两种基于指令的GPU编程模型——OpenACC和OpenMP。从编程复杂度、数据处理方式和性能表现等多个方面对两者进行了对比分析,并结合实际应用场景提供了选择建议。通过详细的代码示例和性能测试结果,帮助开发者更好地理解和应用这两种编程模型,以充分发挥GPU的并行计算能力。

q5r6s7的博客 136

uniapp [安卓苹果App端] - 实现APP开发后台保活+息屏后台持续运行效果,强制后台保活手机黑屏后进程存活后台保持网络连接或持续定位等功能提供支持(app后台运行休眠仍然可以确保一直运行)

uniapp APP开发后台保活,Uniapp-APP后台保活插件(安卓苹果ios后台保活)保证应用在后台继续运行,uniapp实现app保活 保后台运行 避免系统主动杀进程,如何让安卓APP一直在后台运行?uniapp 安卓 APP 后台持续运行(保活)iOS App后台保活,Android 后台运行白名单实现保活,保活息屏后台运行,uniapp后台保活,uniapp熄屏 后台运行,Android/ios进程保活/息屏后后台保持定位、网络运行,uniapp在android ios保活,挂后台可运行,安卓A

🏆 前端领域优质创作者 1万+

linux内核编程之内存寻址

内存寻址:虚拟地址映射到物理地址x86虚拟地址到物理地址机制:分段+分页linux为了增加通用性,没有分段机制,64位系统采用四级分页机制,也就是有四级页表寻址过程:虚拟地址->第一级页目录->第二级页目录->第三级页目录->第四级页目录->页表项->物理地址。

weixin_45050132的博客 261

Docker 实战:使用 Docker Desktop 在 MacOS 上安装 Docker

在这采用 Docker Desktop 在 Mac 上来安装 Docker。Docker Desktop 是 Docker 的管理桌面 IDE,提供了一个直观的GUI(图形用户界面),允许您直接在本机上管理容器、应用程序以及镜像。适用于 Mac、Linux 以及 Windows 环境,允许您构建、共享和运行容器化应用程序和微服务。Docker Desktop 可以大大降低在复杂配置上的时间,因此您可以专注于编写代码。它负责端口映射、文件系统问题和其他默认设置,并定期更新 Bug 修复和安全更新。

SmartSi 8008

Linux操作系统实验1——地址转换

在分段时先用段选择符在相应的段描述符表中找到段描述符,也就是某一个段的基地址,再加上段内偏移量就得到了对应的线性地址,线性地址也称之为虚拟地址。1.在内核中先申请一个页面,使用内核提供的函数,按照寻页的步骤一步步的找到物理地址。(fileview.c)为了增加可移植性并没有完整的使用分段机制,它让所有的段都指向相同的段地址范围,段的基地址都为。命令卸载模块时,模块的卸载函数会自动被内核执行,完成与模块加载函数相反的功能。位中的情况一样,现在还未被使用,它的页目录项只有一个,线性地址中也没有它的索引位。

木子一个Lee的博客 3343

Python爬虫实战:深度解析B站弹幕异步采集技术与数据可视化

title=f"弹幕词云 Top 100 (BV: {bvid})", title_textstyle_opts=opts.TextStyleOpts(font_size=23).add(series_name="弹幕词云", data_pair=list(top_100_words.items()), word_size_range=[20, 100]).add_xaxis([f"{i}-{i+1}分" for i in range(len(time_distribution))])

2201_76125261的博客 640

电池模拟器【 电化学模型深度解析:PNGV模型原理与应用(专业科普版)】

1. 模型起源PNGV(Partnership for a New Generation of Vehicles)模型诞生于1993年美国“新一代汽车合作计划”,旨在提升混合动力汽车电池系统的建模精度。该模型最初针对铅酸电池设计,后经改进广泛用于锂离子电池、镍氢电池等动态特性仿真。2. 模型定位PNGV属于**等效电路模型(ECM)**的进阶版本,介于简单Rint模型与复杂电化学模型(如P2D模型)之间,兼顾计算效率与精度,是BMS(电池管理系统)中SOC/SOH估算的主流选择。PNGV模型凭借。

优快云-新能源汽车研发测试专业博主 1699

【Linux内核分析与应用-陈莉君老师】课程相关索引

1.【Linux内核分析与应用-陈莉君老师】动手实践-把虚拟内存转换成物理地址 2.【Linux内核分析与应用-陈莉君老师】从程序到进程 3.【linux内核分析与应用-陈莉君老师】进程的一生 4.【Linux内核分析与应用-陈莉君老师】进程的调度 ...

Edidaughter的博客 840

【异常】安装hermes-agent时提示GnuTLS recv error (-110): The TLS connection was non-properly terminated.

Git克隆仓库时出现TLS连接异常报错的解决方案摘要: 当执行git clone命令时遇到"GnuTLS recv error (-110)"报错,通常由网络不稳定、缓冲区不足或证书验证问题导致。主要解决方案包括:1)增大Git HTTP缓冲区(git config --global http.postBuffer 524288000);2)使用GitHub镜像站加速克隆;3)临时禁用SSL验证进行测试;4)检查并调整代理配置;5)改用SSH协议克隆。建议优先尝试增大缓冲区和镜像站方案,

奶爸的编程之路,也就一周冷个三天 1351

Linux内核分析与应用2-内存寻址

核心代码和数据所在的段的特权级都比较高,一般在ring0,而用户程序所在的段的特权级较低,一般在ring3。当低特权级的任务试图在未被允许的情况下访问高特权级的段时,将会产生常规保护错误。<16>. 保护模式提供了四个特权级,Linux使用了其中的2个,0级对应内核态,2级对应用户态 (错)段被分为了4个特权级,分别为0-3级,有时候我们也叫做ring0-ring3,其中,数值越小特权级越高。<3>.Intel8086的寻址范围是1MB,80386的寻址范围是 4GB (对)

techdashen的博客 233

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Linux进程地址空间管理

目录 1.    重要数据结构说明    2 2.    进程地址空间概览    3 3.    地址区间操作    4 3.1    地址区间查找    4 3.2    地址区间合并    4 3.3    地址区间插入    6 3.4    地址区间创建    6 4.    映射的创建    8 4.1    mmap    8

chenying126的博客 4723

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

linux 虚拟内存转物理内存,Linux虚拟内存地址转化成物理内存地址

背景现代手机这种SOC(system on chip),因为功耗、Modem等功能soc上集成了很多core,他们还可以是独立的系统在运转。比如ADSP简介ADSP(Application Digital Signal Processing)就是高通的Hexagon DSP ,就是独立运转的一个core+system。这样做不仅可以使用soc上的专用核处理专业的事情,比如上面说的ADSP就可以处理...

weixin_30194029的博客 491

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

linux 内核页表替换

linux 内核页表替换 一.linux本身关于页表查找机制的代码实现 逻辑地址到页表的查找过程 static int __follow_pte_pmd(struct mm_struct *mm, unsigned long address, unsigned long *start, unsigned long *end, ...

inquisiter 1455

Linux 获取虚拟地址对应的物理地址【转】

一个虚拟页映射到一个物理页。以常见的4KB的页大小为例,如果一个虚拟地址0xfe0000对应物理地址的0x40000,那么接下来的虚拟地址0xfe0000+i就对应物理地址的0x40000+i,i=0,1,2,...,4095。也就是说,在虚拟地址中连续的地址,在物理上可能是碎片似的分散在内存条的各个地方,但是在一个页内,地址是连续地一一对应的。从此处读取一个8字节的数据,先检查最高位'page present',如果是1,那么说明该页处于物理内存中,那么该8字节的第0-54位就是物理页号。

生活需要深度 1926

程序被加载到进程的哪个位置?

程序被加载器加载到内存后,通过/proc/$pid/maps文件,我们可以观测到程序被加载的内存位置。那么,通过打印进程内存的方式,让我们确认程序是不是真的加载到内存,以及加载到内存的程序和硬盘中的文件有没有区别。通过上述截图对比可知,程序的16进制被严格的加载到进程的内存空间,并且从进程的第一个vm_area_struct结构表达的内存处开始保存。此处只打印了进程第一个vm_area_struct表达的内存。这里只展示部分16进制,用于与内存中的16进制进行对比。编译启动,然后打印程序的16进制。

u010049696的专栏 409

【linux内核分析与应用-陈莉君】内核同步概述

1.内核同步机制的引入 如果把内核看做不断地对各种请求进行响应的服务器的话,那么正在CPU上执行的进程发出中断请求 的外部设备等相当于客户,正如服务器一直要响应客户的请求一样,内核 ...

Edidaughter的博客 553

【linux内核分析与应用-陈莉君】进程的一生

1.进程与线程 2.TASK_STRUCT结构的统一性与多样性 3.进程API的实现

Edidaughter的博客 430

【Linux驱动开发】004 物理内存与虚拟内存的转换

全称叫做 Memory Manage Unit,也就是在老版本的 Linux 中要求处理器必须有 MMU,但是现在Linux 内核已经支持无 MMU 的处理器了。MMU主要功能:①、完成虚拟空间到物理空间的映射。②、内存保护,设置存储器的访问权限,设置虚拟存储空间的缓冲特性。对于 32 位的处理器,虚拟地址范围是 2^32=4GB,我们的阿尔法开发板上有 512MB 的 DDR3,这 512MB 的内存就是物理内存(RAM),经过 MMU 可以将其映射到整个 4GB 的虚拟空间。

kashine的博客 2141

【linux内核分析与应用-陈莉君】进程用户空间管理机制

1.创建进程用户空间 用户空间如何被创建? 实际上是通过拷贝或者共享父进程的用户空间来实现的. 也就是说内核调用copy_mm函数来实现的. 这样就为新进程建立了所有的页表和mm_struct结构. 通常来说每个进程都有自己的用户空间,但是如果调用的clone函数 创建的内核线程它是共享父进程的用户空间, ...

Edidaughter的博客 459

【linux内核分析与应用-陈莉君】设备驱动概述

1.为什么要引入设备驱动程序 计算机中三个最基础的物质基础是: CPU 内存 输入输出设备. 与I/O设备相比,文件系统是一种逻辑意义上的存在,它只不过使得对设备的操作更方便,更有组织,更接近人类的 思维方式,可以说文件操作是对设备操作地组织与抽象,而设备操作时对文件操作的最终实现. 如何让机器般冰冷的硬件设备拥有灵性,从而使得控制设备像操作文件一样方便有效,这个是本章需要讨论的设备驱动问题. 2.设备驱动程序的作用 ...

Edidaughter的博客 518

【linux内核分析与应用-陈莉君】动手实践--内核多任务并发实例

1.内核多任务并发示意图 本节我们会用一个实例来看看在linux中是如何使用这些

Edidaughter的博客 847

【linux内核分析与应用-陈莉君】Linux内存管理机制

1.内存层次

Edidaughter的博客 885
上一篇: 【高级操作系统-陈渝】Architecture-计算机架构
下一篇: 【Linux内核分析与应用-陈莉君】从程序到进程
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值