2010-03-13 22 views

Respuesta

2

Primero, ¿por qué querrías hacer esto? El propósito de los sistemas modernos de VM es eliminar al programador de la aplicación de la complejidad del diseño de la memoria physocal. Dándoles a cada uno su propio espacio de direcciones para hacer su vida más fácil.

Si usted quisiera hacer esto, casi seguramente necesitaría usar un módulo kernel. Obtenga la dirección virtual de la variable de la forma habitual, utilícela para indexar en las tablas de página de procesos y lea el valor que encuentre (la dirección física del marco). A continuación, agregue el desplazamiento de página para obtener la dirección física completa. Tenga en cuenta que no podrá usar esta dirección mientras la búsqueda está habilitada.

(Si tienes suerte, puede ser capaz de obtener la dirección del marco de una región VM desde el sistema de archivos/proc y por lo tanto molestaría requieren para escribir un módulo del núcleo.)

+4

... y salvo bloquea la página en la memoria, esa dirección física podría cambiar en cualquier momento. – caf

+1

No necesita escribir un módulo kernel: como explican los otros ejemplos, esto ya está expuesto a través de '/ proc/$ pid/pagemap'. – poolie

+0

Puede ser interesante en las arquitecturas NUMA conocer la dirección física de las variables – horro

-5

(edit: Si por "dirección física ", te refieres al nivel de" en qué módulo de RAM están mis bits almacenados ", entonces la siguiente respuesta es inapropiada.)

No necesita privilegios de root para hacer esto. Lo que necesita en su lugar es un depurador. Y aquí vamos (usando un sistema Linux en x86_64):

Primero necesitamos un pequeño programa para jugar. Éste accede a una variable global y la imprime dos veces seguidas. Tiene dos variables globales, que encontramos en la memoria más adelante.

 
#include <stdio.h> 

int a, b = 0; 

int main(void) 
{ 
    printf("a: "); 
    if (fscanf("%d", &a) < 1) 
     return 0; 

    printf("a = %d\n", myglobal); 

    printf("b: "); 
    if (fscanf("%d", &b) < 1) 
     return 0; 

    printf("a = %d, b = %d\n", a, b); 

    return 0; 
} 

Paso 1: Compilar el programa y tira toda la información de depuración de ella, por lo que no reciben ninguna pista desde el depurador que no se pueden conseguir en una situación de la vida real.

 
$ gcc -s -W -Wall -Os -o ab ab.c 

Paso 2: Ejecute el programa e ingrese uno de los dos números.

 
$ ./ab 
a: 123 
a = 123 
b: _ 

Paso 3: Encuentra el proceso.

 
$ ps aux | grep ab 
roland 21601 0.0 0.0 3648 456 pts/11 S+ 15:17 0:00 ./ab 
roland 21665 0.0 0.0 5132 672 pts/12 S+ 15:18 0:00 grep ab 

Paso 4: Adjunte un depurador al proceso (21601).

 
$ gdb 
... 
(gdb) attach 21601 
... 
(gdb) where 
#0 0x00007fdecfdd2970 in read() from /lib/libc.so.6 
#1 0x00007fdecfd80b40 in _IO_file_underflow() from /lib/libc.so.6 
#2 0x00007fdecfd8230e in _IO_default_uflow() from /lib/libc.so.6 
#3 0x00007fdecfd66903 in _IO_vfscanf() from /lib/libc.so.6 
#4 0x00007fdecfd7245c in scanf() from /lib/libc.so.6 
#5 0x0000000000400570 in ??() 
#6 0x00007fdecfd2f1a6 in __libc_start_main() from /lib/libc.so.6 
#7 0x0000000000400459 in ??() 
#8 0x00007fffd827da48 in ??() 
#9 0x000000000000001c in ??() 
#10 0x0000000000000001 in ??() 
#11 0x00007fffd827f9a2 in ??() 
#12 0x0000000000000000 in ??() 

El marco interesante es el número 5, ya que es entre un cierto código llamando a la función y la función mainscanf, por lo que debe ser nuestra función main. Continuando con la sesión de depuración:

 
(gdb) frame 5 
... 
(gdb) disassemble $pc $pc+50 
... 
0x0000000000400570 :  test %eax,%eax 
0x0000000000400572 :  jle 0x40058c <[email protected]+372> 
0x0000000000400574 :  mov 0x2003fe(%rip),%edx  # 0x600978 <[email protected]+2098528> 
0x000000000040057a :  mov 0x2003fc(%rip),%esi  # 0x60097c <[email protected]+2098532> 
0x0000000000400580 :  mov $0x40068f,%edi 
0x0000000000400585 :  xor %eax,%eax 
0x0000000000400587 :  callq 0x4003f8 <[email protected]> 
... 

Ahora sabemos que la función printf obtendrá tres parámetros, y dos de que sólo hay cuatro bytes de distancia el uno del otro. Esa es una buena señal de que estas dos son nuestras variables a y b. Entonces la dirección de a es 0x600978 o 0x60097c. Vamos a averiguar por tratar:

 
(gdb) x/w 0x60097c   
0x60097c <[email protected]+2098532>: 0x0000007b 
(gdb) x/w 0x600978 
0x600978 <[email protected]+2098528>: 0x00000000 

Así a, la variable que se lee en la primera, es decir en la dirección 0x60097c (porque 0x0000007b es la representación hexadecimal de 123, que entramos), y b está en 0x600978.

Todavía en el depurador, podemos modificar la variable a ahora y luego continuar el programa.

 
(gdb) set *(int *)0x60097c = 1234567 
(gdb) continue 

De vuelta en el programa que nos pide que introduzca dos números:

 
$ ./ab 
a: 123 
a = 123 
b: 5 
a = 1234567, b = 5 
$ 
+11

Esto le da su dirección virtual, no física. –

16

Como parte respondió antes, los programas normales no deberían tener que preocuparse acerca de las direcciones físicas, ya que correr en un espacio de direcciones virtuales con todas sus comodidades. Además, no todas las direcciones virtuales tienen una dirección física, pueden pertenecer a archivos mapeados o páginas intercambiadas. Sin embargo, a veces puede ser interesante ver este mapeo, incluso en el sitio de usuario.

Para este propósito, el kernel de Linux expone su mapeo a un usuario a través de un conjunto de archivos en el /proc. La documentación se puede encontrar en here. Breve resumen:

  1. /proc/$pid/maps proporciona una lista de asignaciones de direcciones virtuales, junto con información adicional, como el archivo correspondiente para archivos asignados.
  2. /proc/$pid/pagemap proporciona más información sobre cada página asignada, incluida la dirección física, si existe.

This website proporciona un programa en C que elimina las asignaciones de todos los procesos en ejecución mediante esta interfaz y una explicación de lo que hace.

+0

Ejemplo mínimo con pruebas: https://stackoverflow.com/questions/17021214/how-to-decode-proc-pid-pagemap-entries-in-linux/45126141#45126141 –

14
#include "stdio.h" 
#include "unistd.h" 
#include "inttypes.h" 

uintptr_t vtop(uintptr_t vaddr) { 
    FILE *pagemap; 
    intptr_t paddr = 0; 
    int offset = (vaddr/sysconf(_SC_PAGESIZE)) * sizeof(uint64_t); 
    uint64_t e; 

    // https://www.kernel.org/doc/Documentation/vm/pagemap.txt 
    if ((pagemap = fopen("/proc/self/pagemap", "r"))) { 
     if (lseek(fileno(pagemap), offset, SEEK_SET) == offset) { 
      if (fread(&e, sizeof(uint64_t), 1, pagemap)) { 
       if (e & (1ULL << 63)) { // page present ? 
        paddr = e & ((1ULL << 54) - 1); // pfn mask 
        paddr = paddr * sysconf(_SC_PAGESIZE); 
        // add offset within page 
        paddr = paddr | (vaddr & (sysconf(_SC_PAGESIZE) - 1)); 
       } 
      } 
     } 
     fclose(pagemap); 
    } 

    return paddr; 
} 
Cuestiones relacionadas