Aprender el diseño de memoria de los programas de espacio de usuario según el bloqueo

En las máquinas de 32 bits, hay un total de espacio de direcciones virtuales 4G, de los cuales 0-3G es para aplicaciones y 3-4G es para el núcleo.

En máquinas de 64 bits, los anchos de dirección de 64 bits aún no son totalmente compatibles. Las longitudes de dirección comunes son 39 (512 GB) y 48 bits (256 TB). El emulador que uso actualmente utiliza un ancho de dirección de 39 bits. En este caso, el usuario El espacio y el espacio del kernel ocupan cada uno 512 GB de espacio de direcciones.

 

Cuando una aplicación se ejecuta cuando el usuario se está ejecutando, ¿cómo se ejecuta normalmente? Un simple ejemplo explicará en detalle.

#include <stdio.h>
#include <malloc.h>

static int global_data=1;
static int global_data1;
int bss_data;
int bss_data1;

int main()
{
    int stack_data = 1;
    int stack_data1 = 2;
    int data[200*1024];

    static int data_val=1;

    int* malloc_data=malloc(10);
    int* malloc_data1=(int*)malloc(300);
    int* malloc_data2=(int*)malloc(300*1024);

    // stack segment
    printf("stack segment!\n");
    printf("\t stack_data=0x%lx\n",&stack_data);
    printf("\t stack_data1=0x%lx\n",&stack_data1);

    // heap segment
    printf("heap segment!\n");
    printf("\t malloc_data=0x%lx\n",malloc_data);
    printf("\t malloc_data1=0x%lx\n",malloc_data1);
    printf("\t malloc_data2=0x%lx\n",malloc_data2);

    //code segment
    printf("code segment!\n");
    printf("\t code_data=0x%lx\n",main);

    //data segment
    printf("data segment!\n");
    printf("\t global_data=0x%lx\n",&global_data);
    printf("\t global_data1=0x%lx\n",&global_data1);
    printf("\t data_val=0x%lx\n",&data_val);

    //bss segment
    printf("bss segment!\n");
    printf("\t bss_data=0x%lx\n",&bss_data);
    printf("\t bss_data1=0x%lx\n",&bss_data1);

    return 0;
}

Para un mejor experimento, necesitamos ejecutar el ejemplo de prueba anterior en una máquina ARM64. Luego imprima la dirección de cada segmento.

root:/ # ./data/vma
stack segment!
         stack_data=0x7fe8a41e24
         stack_data1=0x7fe8a41e20
heap segment!
         malloc_data=0x356db9d0
         malloc_data1=0x356db9f0
         malloc_data2=0x6ff3187010
code segment!
         code_data=0x400620
data segment!
         global_data=0x48b960
         global_data1=0x48d380
         data_val=0x48b964
bss segment!
         bss_data=0x48e448
         bss_data1=0x48e44c

Utilizamos una imagen para describir la ubicación de cada segmento en función de la dirección impresa por cada segmento. La descripción actual es la arquitectura ARM64, que puede no ser la misma para diferentes arquitecturas.

Ampliamos el espacio de usuario de ARM64, podemos ver claramente la posición de cada segmento en todo el espacio de usuario.

  • El segmento de código es la posición más baja del espacio de direcciones virtuales del usuario, y el segmento de código es donde se encuentra nuestro código
  • Encima de la ubicación del segmento de código se encuentra el segmento de datos, y el segmento de datos es la variable inicializada globalmente.
  • La ubicación del segmento de datos es el segmento BSS, y el segmento BSS es una variable global no inicializada.
  • El segmento de pila es la variable local llamada por la función, o la matriz local definida en la función. Se puede ver que la pila crece desde la dirección alta hacia abajo.
  • El segmento Heap es el área de aplicación correspondiente de malloc. A partir de los resultados experimentales, el segmento Heap se encuentra en el medio del espacio del usuario y crece de abajo hacia arriba.
  • El área Mmap es el área que usamos mmap para mapear. Cuando la aplicación malloc es mayor a 128K, se usará el área mmap.

El experimento anterior se basa en los resultados experimentales de la arquitectura ARM64. Si está interesado, puede estudiar el sistema de 32 bits. Aquí daré directamente los resultados del sistema 32, por supuesto, también es el resultado del experimento, que es el resultado de la máquina ubuntu creada hace 32 años.

Los resultados correspondientes son los siguientes

Se puede ver que el rendimiento es el mismo que ARM64.

 

VMA (Área de memoria virtual)

Los segmentos mencionados anteriormente aún deben asignarse a una memoria física específica, y se usa VMA para describir cada segmento en el núcleo. Podemos usar el comando cat / proc / pid / maps para corresponder a los resultados experimentales anteriores

Puede verificar si la dirección se encuentra en el área correspondiente.

El núcleo describe cada segmento a través de vma, y ​​cada vma se vinculará a través de una lista vinculada o un árbol rojo-negro, y el encabezado de la lista vinculada se colocará en la estructura mm_struct.

 

No describiré vma en detalle aquí, si está interesado, puede consultar el código relacionado para verlo. Describa aproximadamente la definición de vma

Aquí solo necesitamos comprender el diseño de cada segmento en el espacio del usuario, teniendo en cuenta la ubicación del segmento de código, segmento de datos, pila y segmento de montón. Y cada segmento es descrito por vma en el kernel, y cada vma está vinculado por una lista vinculada o un árbol rojo-negro. El encabezado de la lista vinculada se montará en el mmap de mm_struct, y el encabezado del árbol rojo-negro se montará en el mmap_rb de mm_struct.

La lista vinculada es para una inserción fácil, y el árbol rojo-negro es para una búsqueda fácil.

 

 

187 artículos originales publicados · ganó 108 · 370,000 visitas

Supongo que te gusta

Origin blog.csdn.net/longwang155069/article/details/105390216
Recomendado
Clasificación