精华内容
下载资源
问答
  • 内存管理(3)x86_64地址映射分析

    千次阅读 2014-03-06 16:47:37
    至于这些逻辑地址对应的物理内存在哪里,用户进程本身并不知道。  通过用户程序若想访问物理内存,我们需要通过内核才能实现。本文基于内核模块的方式,实现在Linux中用户态程序访问所有物理内存。 1、内核模块编写...

         

          用户态的程序都是在内存保护模式下使用内存,无法直接访问物理内存。同时用户程序使用的地址,也并不是物理地址,而是逻辑地址。至于这些逻辑地址对应的物理内存在哪里,用户进程本身并不知道。

          通过用户程序若想访问物理内存,我们需要通过内核才能实现。本文基于内核模块的方式,实现在Linux中用户态程序访问所有物理内存。

    1、内核模块编写

          通过文件读写的方式,实现物理地址访问。将物理地址,作为参数pos传递。 

        ssize_t my_read( struct file *file, char *buf, size_t count, loff_t *pos )

        内核代码中,是无法直接访问物理地址的,代码能访问的都是逻辑地址。此时我们需要先将物理地址转换成逻辑地址,才能在代码中对地址读写。

        物理地址转换成逻辑地址方法:

         1)根据物理地址,计算出对应的页面号和页内偏移

                 page_number = *pos / PAGE_SIZE;

                  page_indent = *pos % PAGE_SIZE;

         2)将页面号找到对应的页面指针

             注意在2.6.32及以上内核中,没有导出mem_map符号,只能通过

             pfn_to_page()来找到对应的页面指针。

               #if 0

                   pp =pfn_to_page( page_number);

                 #else

                  pp = &mem_map[ page_number ];

                #endif

        3)通过kmap映射成逻辑地址

             from = kmap( pp ) + page_indent;

          映射成逻辑地址后,我们直接通过from指针来访问物理地址pos了。

    2.x86_64  Address_mapping

          1.查看物理内存的工具fileview.c

     

    //----------------------------------------------------------------
    //	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)
     	unsigned 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 );
    }
    


            这是国外某位大神写的,可以在用户程序查看物理内存,原理很简单,但实现比较复杂(有些涉及到终端控制台的地方没怎么接触,反正会用就行)。通过向内核注册一个字符设备,本着linux“万物皆文件”的原则字符设备文件也不例外,通过把所有物理内存映射到这个字符设备这样就可以访问查看物理内存了。
       2.注册字符设备

    //-------------------------------------------------------------------
    //	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 
    unsigned long	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 = (unsigned long)num_physpages << PAGE_SHIFT;
    	printk( "<1>  ramtop=%08lX (%lu 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*/
            pp = pfn_to_page( page_number);
    	//pp = &mem_map[ page_number ];
    	
    	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 < count) 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 )
    {
    	unsigned long	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 );
    

    3.读取cr3

     #include <linux/module.h>    
     #include <linux/proc_fs.h>    
     #include <linux/sched.h>
     #include <asm/uaccess.h>
     #include <asm/desc.h>
     #include <asm/pgtable.h>
     #include <asm/desc.h>
     //#include <asm/system.h>
    
     static char modname[] = "sys_reg";
     struct gdtr_struct{
    	short limit;
    	unsigned long address __attribute__((packed));
      };
    
     static unsigned long cr0,cr3,cr4;
     static struct gdtr_struct gdtr;
    
     static int my_get_info( char *buf, char **start, off_t off, int count )
     {
    	int	len = 0;
    	struct mm_struct *mm;
    
    	mm = current->active_mm;
    	cr0 = read_cr0();
            cr3 = read_cr3();
    	cr4 = read_cr4();
    	asm(" sgdt %0\n":"=m"(gdtr));
    
    
    	len += sprintf( buf+len, "cr4=%lX  ", cr4 );
    	len += sprintf( buf+len, "PSE=%lX  ", (cr4>>4)&1 );
    	len += sprintf( buf+len, "PAE=%lX  ", (cr4>>5)&1 );
    	len += sprintf( buf+len, "\n" );
    	len += sprintf( buf+len, "cr3=%lX cr0=%lX\n",cr3,cr0);
    	len += sprintf( buf+len, "pgd:0x%lX\n",(unsigned long)mm->pgd);
    	len += sprintf( buf+len, "gdtr address:%lX, limit:%X\n", gdtr.address,gdtr.limit);
    
    	return	len;
    }
    
    
     int init_module( void )
     {
    	printk( "<1>\nInstalling \'%s\' module\n", modname );
    	create_proc_read_entry( modname, 0, NULL, my_get_info, NULL);
    	return	0;  
     }
    
    
     void cleanup_module( void )
     {
    	remove_proc_entry( modname, NULL );
    	printk( "<1>Removing \'%s\' module\n", modname );
     }
    
     MODULE_LICENSE("GPL");
    

    4.用户测试程序

     #include <stdio.h>
     #include <stdlib.h>
     #include <unistd.h>
     #include <fcntl.h>
    
     #define REGISTERINFO "/proc/sys_reg"
     #define BUFSIZE  4096
    
     static char buf[BUFSIZE];
     static unsigned long addr;
    
     #define FILE_TO_BUF(filename, fd) do{	\
    	static int local_n;	\
    	if (fd == - 1 && (fd = open(filename, O_RDONLY)) == - 1) {	\
    	fprintf(stderr, "Open /proc/register file failed! \n");	\
    	fflush(NULL);	\
    	_exit(102);	\
    	}	\
    	lseek(fd, 0L, SEEK_SET);	\
    	if ((local_n = read(fd, buf , sizeof buf -  1)) < 0) {	\
    	perror(filename);	\
    	fflush(NULL);	\
    	_exit(103);	\
    	}	\
    	buf [local_n] = 0;	\
     }while(0)
    
    
    
     int main()
     {
    	unsigned long tmp;
    	tmp = 0x12345678beaf5dde;
    	static int cr_fd = - 1;
    
    	printf("tmp address:0x%lX\n", (unsigned long)&tmp);
    	FILE_TO_BUF(REGISTERINFO, cr_fd);
    
    	printf("%s", buf );
    
    	while(1);
    
    	return 0;
     }
    

    564位地址映射分析
    5.1.段映射
    5.1.1、内核定义段描述符表
    arch/x86/kernel/cpu/common.c
     87 #ifdef CONFIG_X86_64
     88         /*
     89          * We need valid kernel segments for data and code in long mode too
     90          * IRET will check the segment types  kkeil 2000/10/28
     91          * Also sysret mandates a special GDT layout
     92          *
     93          * TLS descriptors are currently at a different place compared to i386.
     94          * Hopefully nobody expects them at a fixed place (Wine?)
     95          */
     96         [GDT_ENTRY_KERNEL32_CS]         = GDT_ENTRY_INIT(0xc09b, 0, 0xfffff),
     97         [GDT_ENTRY_KERNEL_CS]           = GDT_ENTRY_INIT(0xa09b, 0, 0xfffff),
     98         [GDT_ENTRY_KERNEL_DS]           = GDT_ENTRY_INIT(0xc093, 0, 0xfffff),
     99         [GDT_ENTRY_DEFAULT_USER32_CS]   = GDT_ENTRY_INIT(0xc0fb, 0, 0xfffff),
    100         [GDT_ENTRY_DEFAULT_USER_DS]     = GDT_ENTRY_INIT(0xc0f3, 0, 0xfffff),
    101         [GDT_ENTRY_DEFAULT_USER_CS]     = GDT_ENTRY_INIT(0xa0fb, 0, 0xfffff),
    102 #else
    103         [GDT_ENTRY_KERNEL_CS]           = GDT_ENTRY_INIT(0xc09a, 0, 0xfffff),
    104         [GDT_ENTRY_KERNEL_DS]           = GDT_ENTRY_INIT(0xc092, 0, 0xfffff),
    105         [GDT_ENTRY_DEFAULT_USER_CS]     = GDT_ENTRY_INIT(0xc0fa, 0, 0xfffff),
    106         [GDT_ENTRY_DEFAULT_USER_DS]     = GDT_ENTRY_INIT(0xc0f2, 0, 0xfffff),

    /*a是低32位,b是高32位*/
    37 #define GDT_ENTRY_INIT(flags, base, limit) { { { \
     38                 .a = ((limit) & 0xffff) | (((base) & 0xffff) << 16), \
     39                 .b = (((base) & 0xff0000) >> 16) | (((flags) & 0xf0ff) << 8) | \
     40                         ((limit) & 0xf0000) | ((base) & 0xff000000), \
     41         } } }

    上述代码段的段描述符就是:
    GDT_ENTRY_INIT(0xc0f3, 0, 0xfffff)就变为 0x00 cff3 000000 ffff

    5.1.2、实验验证


    linux/x86/include/asm/segment.h
    162 #define GDT_ENTRY_DEFAULT_USER32_CS 4
    163 #define GDT_ENTRY_DEFAULT_USER_DS 5
    164 #define GDT_ENTRY_DEFAULT_USER_CS 6
    165 #define __USER32_CS   (GDT_ENTRY_DEFAULT_USER32_CS*8+3)
    166 #define __USER32_DS     __USER_DS

    187 #define __KERNEL_CS     (GDT_ENTRY_KERNEL_CS*8)
    188 #define __KERNEL_DS     (GDT_ENTRY_KERNEL_DS*8)
    189 #define __USER_DS       (GDT_ENTRY_DEFAULT_USER_DS*8+3)
    190 #define __USER_CS       (GDT_ENTRY_DEFAULT_USER_CS*8+3)
    __USER_DS  = 5*8 + 3 = 43
    0000 0000 00101 011  (段索引是5)
    我们的测试代码中gdt的逻辑地址是gdtr address:0xFFFF88011FC84000对应的物理地址(内核start地址是0xFFFF880000000000):0x11fc84000(线性空间堆栈是直接映射到内核空间的)。以5为索引(每一项64位(8字节)),故物理地址(0x000011FC84028)处的内容即是段描述符信息:(FF FF 00 00 00 F3 CF 00),表示为64位段描述符即0x00 FCFF3 000000 FFFF(正是5.5.1中的ds段描述符信息
    可见段基地址是0(搞了半天,段映射根本没用呢),逻辑地址就是线性地址(虚拟地址)0x7FFF363E1158
    5.2.页映射
    tmp 48bit virtual address:0x7FFF363E1158
     011111111 111111100 110110001 111100001 000101011000
      0ff       1fc        1b1      1e1       158  
    计算过程:
    cr3=BD3F8000
    CR3寄存器的值为0xBD3F8000,这是第一级映射表的起始物理地址。
    1.(0ff*8)7f8 + 0xBD3F8000 = 0xBD3F87f8(第二级映射表存放在该物理地址处,*8表示每一项8字节(64位地址值))

    67 90 F1 A6 00 00 00 00 ------>  00000000 A6F19067 ----------->0xA6F19000(52位用作物理地址,后12位067是页属性)(第二级映射(上层页目录表)的物理地址)
    2.(1fc*8)fe0 + 0xA6F19000 = 0xA6F19fe0


    67 C0 D4 A6 00 00 00 00 ------>  00000000 A6D4C067 ----------->0xA6D4C000(52位用作物理地址,后12位067是页属性)(第三级映射(中间页目录表)的物理地址)
    3.(1b1*8)d88 + 0xA6D4C000 = 0xA6D4Cd88


    67 30 4C 8D 00 00 00 00 ------>  00000000 8D4C3067 ----------->0x8D4C3000(52位用作物理地址,后12位067是页属性)(第四级映射(页表)的物理地址)
    4.(1e1*8)f08 + 0x8D4C3000 = 0x8D4C3f08
    67 50 6A 76 00 00 00 00 ------>  00000000 766A5067 ----------->0x766A5000(52位用作物理地址,后12位067是页属性)(第四级映射(页表项(页面))的物理地址)
    5.158 + 0x766A5000 = 0x766A5158


    DE 5D AF BE 78 56 34 12 (0x12345678beaf5dde)
    得到验证了,果然是tmp的值,这就是整个逻辑地址到物理地址映射的全过程

    这是那个读cr3寄存器的内核实现

    arch/x86/include/asm/system.h

    312 #define read_cr3()      (native_read_cr3())
    
    246 static inline unsigned long native_read_cr3(void)
    247 {
    248         unsigned long val;
    249         asm volatile("mov %%cr3,%0\n\t" : "=r" (val), "=m" (__force_order));
    250         return val;
    251 }





    展开全文
  • C++对变量名不作存储,在汇编以后不会出现变量名; 变量名作用只是用于方便编译成汇编代码,是给编译器看的,是...编译器编译它时,产生类似mov [0x00410FC0],5的指令,即:把5放在该内存地址的空间上。其中并没有出现

    参考:http://blog.csdn.net/zx824/article/details/6677884

    C++对变量名不做存储

    • C++对变量名不作存储,在汇编以后不会出现变量名;

    • 变量名作用只是用于方便编译成汇编代码,是给编译器看的,是方便人阅读的。

    • 下面是一个例子

    int n=5;
    • 编译器编译它时,产生类似mov [0x00410FC0],5的指令,即:把5放在该内存地址的空间上。其中并没有出现n,n只是编译时供编译器识别的名字,是一个高级语言抽象出来的概念,在真实执行的程序中并不存在n,至于n的地址是0x00410FC0还是其他的什么,这是由连接器(linker)决定的,连接器把全局变量放在.exe文件中,执行.exe文件时全局变量在类似0x00430000左右的地址。局部变量在栈上,一般地址为0x00120000左右。
    • 地址0x00410FC0并不需要一个地址去存放它,因为在最后产生.exe文件它自己知道它需要的一个值存放在什么地址,所以就在它的二进制代码中把0xffbffb0c硬编码进来了
    展开全文
  • 存在逻辑上就是一个一个的格子,这些格子可以用来装东西(里面装的东西就是内存中存储的数),每个格子有一个编号,这个编号就是内存地址,这个内存地址(一个数字)和这个格子的空间(实质是一个空间)是一一对应...

    内存编址方法

    内存在逻辑上就是一个一个的格子,这些格子可以用来装东西(里面装的东西就是内存中存储的数),每个格子有一个编号,这个编号就是内存地址,这个内存地址(一个数字)和这个格子的空间(实质是一个空间)是一一对应且永久绑定的。这就是内存的编址方法。
    在程序运行时,计算机中CPU实际只认识内存地址,而不关心这个地址所代表的空间在哪里,怎么分布这些实体问题。因为硬件设计保证了按照这个地址就一定能找到这个格子,所以说内存单元的2个概念:地址和空间是内存单元的两个方面。

    内存编址是以字节为单位的

    我随便给一个数字(譬如说7),然后说这个数字是一个内存地址,然后问你这个内存地址对应的空间多大?这个大小是固定式,就是一个字节(8bit)。
    如果把内存比喻位一栋大楼,那么这个楼里面的一个一个房间就是一个一个内存格子,这个格子的大小是固定的8bit,就好象这个大楼里面所有的房间户型是一样的。

    内存和数据类型的关系

    C语言中的基本数据类型有:char short int long float double
    int 整形(整数类型,这个整就体现在它和CPU本身的数据位宽是一样的)譬如32位的CPU,整形就是32位,int就是32位。
    数据类型和内存的关系就在于:
    数据类型是用来定义变量的,而这些变量需要存储、运算在内存中。所以数据类型必须和内存相匹配才能获得最好的性能,否则可能不工作或者效率低下。
    在32位系统中定义变量最好用int,因为这样效率高。原因就在于32位的系统本身配合内存等也是32位,这样的硬件配置天生适合定义32位的int类型变量,效率最高。也能定义8位的char类型变量或者16位的short类型变量,但是实际上访问效率不高。
    在很多32位环境下,我们实际定义bool类型变量(实际只需要1个bit就够了)都是用int来实现bool的。也就是说我们定义一个bool b1;时,编译器实际帮我们分配了32位的内存来存储这个bool变量b1。编译器这么做实际上浪费了31位的内存,但是好处是效率高。
    问题:实际编程时要以省内存为大还是要以运行效率为重?答案是不定的,看具体情况。很多年前内存很贵机器上内存都很少,那时候写代码以省内存为主。现在随着半导体技术的发展内存变得很便宜了,现在的机器都是高配,不在乎省一点内存,而效率和用户体验变成了关键。所以现在写程序大部分都是以效率为重。

    内存对齐

    我们在C中int a;定义一个int类型变量,在内存中就必须分配4个字节来存储这个a。有这么2种不同内存分配思路和策略:

    第一种:0 1 2 3									对齐访问
    第二种:1 2 3 4	或者 2 3 4 5 或者 3 4 5 6 		非对齐访问
    

    内存的对齐访问不是逻辑的问题,是硬件的问题。从硬件角度来说,32位的内存它 0 1 2 3四个单元本身逻辑上就有相关性,这4个字节组合起来当作一个int硬件上就是合适的,效率就高。
    对齐访问很配合硬件,所以效率很高;非对齐访问因为和硬件本身不搭配,所以效率不高。(因为兼容性的问题,一般硬件也都提供非对齐访问,但是效率要低很多。)

    展开全文
  • 存在逻辑表示为一个一个的格子,而每个格子都有一个编号,这个编号就是内存地址内存地址(一个数字)和这个格子空间一一对应,永久绑定。 程序运行时,计算机cpu只认识内存地址,而不关心这个地址所代表的空间...

    内存的编址和寻址、内存对齐

    1.内存的编址方法

    • 内存在逻辑表示为一个一个的格子,而每个格子都有一个编号,这个编号就是内存地址。内存地址(一个数字)和这个格子空间一一对应,永久绑定。
    • 程序运行时,计算机cpu只认识内存地址,而不关心这个地址所代表的空间在哪里,怎么分配等问题。因为硬件设计保证了按照地址能够找到对应的格子。内存单元的2个概念:地址和空间是内存单元的两个方面。

    2.关键:内存的编址以字节为单位

    • 如果把内存比作大楼,那么一个个房间就是内存格子。其中格子的大小是固定的,即8bit。

      3.内存和数据类型的关系

    • c语言中的基本数据类型:char short int long float double
      int(整数类型,这个整就体现在它和CPU本身的数据位宽是一样的)

    • 数据类型和内存的关系在于:数据类型是用来定义变量的,而这些变量需要存储、运算在内存中。所以数据类型必须要和内存相匹配。否则可能效率低下。

    • 在32位系统中定义变量最好用int,因为这样效率高。原因就是32位的系统本身配合内存也是32位。这样的硬件配置天生适合定义32位的int类型变量。其他类型变量定义效率不高。

    • 在很多32位环境下,我们实际定义bool类型变量。(实际只需要1bit就够了)都是用int来实现bool。 bool
      b1;编辑器其实帮我们分配了32位的内存来存储这个bool变量b1,编译器这么做实际上浪费了31位的内存,但是效率高。

    4.内存对齐

    • 在C中定义一个int类型变量,在内存中就必须分配4个字节来存储这个a。那么有两种不同的内存分配思路和策略:
      第一种:0 1 2 3 对齐访问
      第二种:1 2 3 4或者 2 3 4 5 或者 3 4 5 6 非对齐访问
    • 逻辑上怎么存放都没有关系,所以内存的对齐不是逻辑问题,是硬件问题。从硬件角度上来说, 32位的内存0 1 2 3四个单元在硬件设计当初就是一个整体,这4个字节组合起来当做一个int,硬件效率就很高。例如,在酒店里,房间编号1 2 3 4看是连续的,但是实际上不一定连续,有可能到头后从新到另一边开始编号。因为兼容性问题,一般硬件也都是提供非对齐访问,但是效率要低很多。
    展开全文
  • 学习笔记 1、内存编址方法  内存在逻辑上是一个个格子,格子...有地址就一定能够找到对应的内存单元(内存单元:包括内存地址和空间)。地址是固定的,空间中存储的东西是变动的,这和数组做左右值相关?  关键...
  • 每个进程都有4GB内存,是真实存在的吗? 虚拟内存 在进程A中0x12345678的地址存放一... 那么存在哪里了呢?存放在物理内存中。 物理页 操作系统为了便于管理物理内存,将物理内存按照一定的单元来分类。 整个物
  • 程序内存在地址空间中的分布情况称为内存模型(Memory Model)。内存模型由操作系统构建,在Linux和Windows下有所差异,并且会受到编译模式的影响。 一、内核空间和用户空间 对于32位环境,理论上程序可以拥有 4GB...
  •  1,内存在逻辑上就是一个一个的格子,这些格子可以用来装东西(里面装的内容就是内存中存储的数),每个格子都有一个编号,这个编号就是内存地址,这个内存地址(就是一个数字)和这个格子的空间(实质是一个空间)是一 ...
  • 概念: 当进程运行时,先将其一部分数据和代码装入内存,另外一部分暂留在磁盘...虚拟内存在哪里: 把内存与磁盘有机的进行结合起来使用,从而得到一个容量很大的"内存",即虚拟内存 虚拟内存是对内存的抽象,建立在金
  • 【C++】C/C+内存管理

    2019-04-23 21:14:07
    【本节内容】 C/C++ 内存分配 ...对什么数据应该存在哪里,我再做一个更详细的分析(地址由高到低) 内核空间:用户代码不能进行读写 在32位机器中,程序的内存一般都是4G。其中0~3G由用户自由操作...
  • 本文描述进程控制块的地址管理以及linux分页存储机制。还包括linux的数据存储方式。想到哪里就写到哪里。进程缓冲区是为进程分配的一块缓冲区,内核缓冲区是内核自带的缓冲区,当进程需要调用数据时,先查找数据是否...
  • 一、java的六种存储地址及解释 1.寄存器(register):这是最快的存储区,因为它位于不同于其他存储区的地方——处理器内部。但是寄存器的数量极其有限,所以寄存器由编译器根据需求进行分配。你不能直接控制,也不能...
  • 数据存储在哪里?

    2019-04-19 22:52:00
    栈(stack) : 位于RAM(内存)中,速度仅次于寄存器,存储对象的引用(地址),指向堆中的对象. 基本类型直接存储值在栈中 堆(heap) : 也位于RAM中,比栈略慢,存储对象 常量存储 : 直接存放在程序代码内部 非RAM存储...
  • 声明一个User类型的user对象时,会在栈内存开辟一个空间,存放指向的堆内存地址。 那栈的这块空间是否还存放了user的类型User呢?如图红色箭头。user是被声明为User类型,那这个类型肯定是要被记录在内存中的,...
  • 之前每次看C++都是在这部分内容中学的迷迷糊糊,今天突然觉的好像有点懂了。 1、什么是地址? 我们在程序中定义的任何...所以变量的地址也是一个意思,就是这个变量真正存在哪,在哪里!这就是变量的地址。 下面我们用
  • 关于遗传算法的函数优化应用方面问题,出现了内存溢出,没找到哪里错误 参考的代码地址为http://blog.csdn.net/ebowtang/article/details/50938396 很多人应该是看这个代码入门遗传算法的 自己的运行代码如下 头文件...
  • 1.局部变量存在哪里(栈上)? x86体系, 32位linux内核给每一-个进程都分配4G大小的虚拟地址空间,有3G的用户态和1G的内核态,用户态主要存放我们应用程序定义的指令或者数据,局部变量存在于栈上,随着函数的运行,栈...
  • 我们在程序中定义的任何变量实际上都是存在内存中的,对吧?那么既然是存在的东西就应该有装他的地方。举个例子,小明家,肯定有小明家的地址,张三家也应该有张三家的地址。再来说一个,比如说谁家都有电视机,对吧...
  • 1. 系统上电 2. CPU初始化 CPU加电稳定后从0XFFFF0(x86系统,其他... 检测系统中内存和显卡等关键部件的存在和工作状态 c. 查找并执行显卡等接口卡BIOS,进行设备初始化; d. 执行系统BIOS,进行系统检测;(检测和
  • //内存分配 head->next=NULL; printf("\t\t\t----------个人通讯录管理系统-----------\t\t\t\n\n"); printf("\t\t\t------------1、添加通讯录----------\t\t\t\n"); printf("\t\t\t------------2、修改...
  • 自己DEBUG了很多遍内存,总是发现存在在二级指针中存在着一级指针0X00000000FDFDFDFD的问题,自己查阅了一些资料好像这与Windows内存填充有关,希望大佬们给看一下,本人比较菜,不要喷我,谢谢各位大佬了 ...
  • //findPtrInfo 在链表中寻找指定地址的元素是否存在 //存在返回iterator不存在返回尾 template, int size = 0> typename list<GCInfo<T>>::iterator GCptr, size>::findPtrInfo(T*ptr) { list ...
  • 页面大小和页表项的联系

    千次阅读 2020-05-13 13:51:44
    那页表项存在哪里呢? 操作系统为了管理内存,将内存分成了若干页,每一个页都是固定大小的。而页表项也是要存在内存中的。考虑一种情况:页面大小是4KB,每个页表项是4B。我们知道页表项用来将逻辑地址转化为物理...
  • 用户无需担心自己的程序存在哪里,反正输入,计算,输出的结果是一样的。让内存管理实现这种媒介透明的手段就是虚拟内存,提供给用户一个比物理主内存大的多的地址空间。    2.随着程序在内存与磁盘之间进行交换,...
  • 顾名思义就是存放名字的地方,比如,若变量x=1,1存放在内存里,那x存在哪里?名称空间就是存放名字x与1绑定关系的地方 x:内存地址 1所在的内存 名称空间共3种,分别如下: locals:是函数内的名称空间,包括...
  • 如何使用 malloc 函数 本文为转载内容,原文地址请点击 不要莫名其妙,其实上面这段小小的对话,就是malloc的使用过程。...分配好的内存在哪里? 如果这五点都确定,那内存就能分配。下面先看mal...
  • 简说程序运行流水线

    2020-04-23 20:42:33
    编译 源程序—> 目标模块 ...因为已经知道了物理地址所以不存在地址的转换,直接装入内存即可 可重定位装入方式 多道程序环境下,编译程序不可能预知经编译后所得的目标模块放在内存哪里。 ...
  • 寄存器与内存 寄存器与内存的区别在哪里呢? 寄存器和RAM的主要不同在于寄存器操作有副作用(side effect或...在X86处理器中存在IO空间的概念,IO空间是相对内存空间而言的,他们是彼此独立的地址空间,在32位的...
  • 存储区域

    2014-12-14 19:54:18
    类型定义后,存在内存的只读区域,不可见,无法取得地址。类变量记录的是地址偏移,与对象绑定才能取得地址。 变量: 全局变量存在全局数据区,全局静态变量存在全局数据区的全局静态变量段。(两者相临或相差几...

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 139
精华内容 55
关键字:

内存地址存在哪里