¿Quiero encontrar la dirección física de una variable definida en un proceso de espacio de usuario? ¿Hay alguna forma de hacerlo utilizando privilegios de root?¿Cómo encontrar la dirección física de una variable del espacio de usuario en Linux?
Respuesta
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.)
... y salvo bloquea la página en la memoria, esa dirección física podría cambiar en cualquier momento. – caf
No necesita escribir un módulo kernel: como explican los otros ejemplos, esto ya está expuesto a través de '/ proc/$ pid/pagemap'. – poolie
Puede ser interesante en las arquitecturas NUMA conocer la dirección física de las variables – horro
(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 main
scanf
, 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 $
Esto le da su dirección virtual, no física. –
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:
/proc/$pid/maps
proporciona una lista de asignaciones de direcciones virtuales, junto con información adicional, como el archivo correspondiente para archivos asignados./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.
Ejemplo mínimo con pruebas: https://stackoverflow.com/questions/17021214/how-to-decode-proc-pid-pagemap-entries-in-linux/45126141#45126141 –
#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;
}
- 1. Cómo probar si una dirección es legible en la aplicación de espacio de usuario de linux
- 2. ¿Cómo escribir la memoria del espacio del kernel (dirección física) en un archivo usando O_DIRECT?
- 3. Pobre rendimiento memcpy en el espacio de usuario para memoria física mmap'ed en Linux
- 4. Asignación de direcciones físicas a la dirección virtual de Linux
- 5. ¿Cómo traducir una dirección de memoria virtual a una dirección física?
- 6. ¿Cómo acceder a la memoria de espacio de usuario desde el kernel de Linux?
- 7. Búsqueda de la dirección IP del usuario
- 8. Linux controlador de dispositivo del núcleo de DMA desde un dispositivo de memoria en el espacio de usuario
- 9. ¿Cómo habilita PAE (extensión de dirección física) un espacio de direcciones de más de 4 GB?
- 10. Algoritmos que reconocen la dirección física en una página web
- 11. Espacio de dirección del proceso frente a la memoria virtual
- 12. ¿Cómo obtener la dirección física de los datos asociados desde una página de estructura?
- 13. traducción de dirección virtual del kernel
- 14. cómo encontrar el uso de espacio de disco por usuario?
- 15. ¿Cómo obtengo la dirección física (MAC) de una dirección IP usando C#?
- 16. Manejo de interrupción de Linux en el espacio de usuario
- 17. Cómo obtener la dirección IPv6 de una interfaz en Linux
- 18. ¿Cómo acceder al espacio del kernel desde el espacio del usuario?
- 19. Encontrar la dirección de carga de una biblioteca compartida en Linux
- 20. Cómo obtener programáticamente la dirección del montón en Linux
- 21. Cómo mapear un buffer del kernel de Linux al espacio del usuario?
- 22. Cómo imprimir/registrar la dirección de una variable en NDK
- 23. Obtener la dirección IP del usuario
- 24. ¿Hay alguna forma de encontrar la dirección de una referencia?
- 25. Del kernel al espacio de usuario (DMA)
- 26. dirección C de una dirección de una variable
- 27. Variable de dirección fija en C
- 28. ¿Cómo puedo imprimir la dirección de memoria de una variable?
- 29. Obtener la dirección IP de una interfaz en Linux
- 30. ¿Cómo obtener la memoria física total en Bash para asignarla a una variable?
Probablemente con/dev/mem? – user2284570
Alguna información relacionada en http://stackoverflow.com/questions/5748492/is-there-any-api-for-determining-the-physical-address-from-virtual-address-in-li –