¿Qué hace la llamada al sistema brk ()?


Según el manual de programadores de Linux:

Brk () y sbrk () cambian la ubicación de la interrupción del programa, que define el final del segmento de datos del proceso.

¿Qué significa el segmento de datos aquí? ¿Es solo el segmento de datos o datos, BSS y montón combinados?

Según wiki:

A veces las áreas de datos, BSS y heap se conocen colectivamente como el "segmento de datos".

No veo razón para cambiar el tamaño del segmento de datos. Si se trata de datos, BSS y heap colectivamente, entonces tiene sentido ya que heap obtendrá más espacio.

Lo que me lleva a mi segunda pregunta. En todos los artículos que he leído hasta ahora, el autor dice que el montón crece hacia arriba y la pila crece hacia abajo. Pero lo que no explican es que lo que sucede cuando montón ocupa todo el espacio entre montón y pila?

introduzca la descripción de la imagen aquí

9 answers

Veo muchas respuestas parciales pero ninguna completa. Aquí está esa foto que publicaste de nuevo:

imagen simplificada del diseño de memoria virtual

El "break"the la dirección manipulada por brk y sbrk is es la línea punteada en la parte superior del montón . La documentación que ha leído describe esto como el final del" segmento de datos "porque en Unix tradicional (pre-shared-libraries, pre - mmap) el segmento de datos era continuo con el montón; antes del inicio del programa, el núcleo cargaría el "texto" y " datos" se bloquea en la RAM a partir de la dirección cero (en realidad un poco por encima de la dirección cero, de modo que el puntero NULO realmente no apunta a nada) y establece la dirección de interrupción al final del segmento de datos. La primera llamada a malloc entonces usaría sbrkpara mover la ruptura y crear el montón entre la parte superior del segmento de datos y la nueva dirección de ruptura más alta, como se muestra en el diagrama, y el uso posterior de malloc lo usaría para hacer el montón más grande según sea necesario.

Mientras tanto, la pila comienza en la parte superior de la memoria y crece hacia abajo. La pila no necesita llamadas explícitas al sistema para hacerla más grande; o bien comienza con tanta RAM asignada como pueda tener (este era el enfoque tradicional) o hay una región de direcciones reservadas debajo de la pila, a la que el núcleo asigna automáticamente RAM cuando nota un intento de escribir allí (este es el enfoque moderno). De cualquier manera, puede haber o no una región de "guardia" en la parte inferior del espacio de direcciones que se puede utilizar para la pila. Si esta región existe (todos los sistemas modernos lo hacen) queda permanentemente sin mapear; si ya sea la pila o el montón intentan crecer en ella, se produce un error de segmentación. Tradicionalmente, sin embargo, el núcleo no hizo ningún intento de imponer un límite; la pila podría crecer en el montón, o el montón podría crecer en la pila, y de cualquier manera se garabatearían sobre los datos de cada uno y el programa se bloquearía. Si tuvieras mucha suerte, se estrellaría. inmediatamente.

No estoy seguro de dónde viene el número 512GB en este diagrama. Implica un espacio de direcciones virtuales de 64 bits, que es inconsistente con el mapa de memoria muy simple que tiene allí. Un verdadero espacio de direcciones de 64 bits se parece más a esto:

espacio de direcciones menos simplificado

Esto no es remotamente a escala, y no debe interpretarse como exactamente cómo cualquier sistema operativo dado hace cosas (después de dibujarlo descubrí que Linux en realidad pone el ejecutable mucho más cerca de la dirección cero que yo pensé que lo hizo, y las bibliotecas compartidas en direcciones sorprendentemente altas). Las regiones negras de este diagrama no están mapeadas any cualquier acceso causa un error de segmento inmediato {y son gigantescas en relación con las áreas grises. Las regiones de color gris claro son el programa y sus bibliotecas compartidas (puede haber docenas de bibliotecas compartidas); cada una tiene un segmento de texto y datos independiente (y segmento" bss", que también contiene datos globales pero se inicializa a todos los bits cero en lugar de ocupando espacio en el ejecutable o biblioteca en el disco). El montón ya no es necesariamente continuo con el segmento de datos del ejecutable I Lo dibujé de esa manera, pero parece que Linux, al menos, no hace eso. La pila ya no está vinculada a la parte superior del espacio de direcciones virtuales, y la distancia entre la pila y la pila es tan enorme que no tiene que preocuparse por cruzarla.

La ruptura sigue siendo el límite superior del montón. Sin embargo, lo que no mostré es que hay podrían ser docenas de asignaciones independientes de memoria en algún lugar negro, hechas con mmap en lugar de brk. (El sistema operativo tratará de mantenerlos lejos del área brk para que no choquen.)

 190
Author: zwol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-09 01:19:37

Ejemplo ejecutable mínimo

¿Qué hace la llamada al sistema brk ()?

Le pide al núcleo que le permita leer y escribir en un trozo de memoria contiguo llamado heap.

Si no preguntas, podría segmentarte.

Sin brk:

#define _GNU_SOURCE
#include <unistd.h>

int main(void) {
    /* Get the first address beyond the end of the heap. */
    void *b = sbrk(0);
    int *p = (int *)b;
    /* May segfault because it is outside of the heap. */
    *p = 1;
    return 0;
}

Con brk:

#define _GNU_SOURCE
#include <assert.h>
#include <unistd.h>

int main(void) {
    void *b = sbrk(0);
    int *p = (int *)b;

    /* Move it 2 ints forward */
    brk(p + 2);

    /* Use the ints. */
    *p = 1;
    *(p + 1) = 2;
    assert(*p == 1);
    assert(*(p + 1) == 2);

    /* Deallocate back. */
    brk(b);

    return 0;
}

Probado en Ubuntu 14.04.

Visualización del espacio de direcciones virtuales

Antes de brk:

+------+ <-- Heap Start == Heap End

Después brk(p + 2):

+------+ <-- Heap Start + 2 * sizof(int) == Heap End 
|      |
| You can now write your ints
| in this memory area.
|      |
+------+ <-- Heap Start

Después de brk(b):

+------+ <-- Heap Start == Heap End

Para comprender mejor los espacios de direcciones, debe familiarizarse con la paginación: ¿Cómo funciona la paginación x86?

Más información

brk solía ser POSIX, pero fue eliminado en POSIX 2001, por lo tanto la necesidad de _GNU_SOURCE para acceder a la envoltura glibc.

Es probable que la eliminación se deba a la introducción mmap, que es un superconjunto que permite asignar múltiples rangos y más asignaciones opcion.

Internamente, el núcleo decide si el proceso puede tener esa cantidad de memoria, y asigna páginas de memoria para ese uso.

brk y mmap son los mecanismos subyacentes comunes que libc utiliza para implementar malloc en sistemas POSIX.

Esto explica cómo se compara la pila con el montón: ¿Cuál es la función de las instrucciones push / pop utilizadas en los registros en el ensamblaje x86?

 12
Author: Ciro Santilli 新疆改造中心 六四事件 法轮功,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-10-01 15:37:03

Puede usar brk y sbrk usted mismo para evitar la "sobrecarga malloc" de la que todos se quejan. Pero no puede usar fácilmente este método en conjunción con malloc, por lo que solo es apropiado cuando no tiene que free nada. Además, debe evitar cualquier llamada a la biblioteca que pueda usar malloc internamente. IE. strlen es probablemente seguro, pero fopen probablemente no lo es.

Llama a sbrk al igual que llamarías a malloc. Devuelve un puntero al salto actual e incrementa el descanso por esa cantidad.

void *myallocate(int n){
    return sbrk(n);
}

Mientras que no puedes liberar asignaciones individuales (porque no hay malloc-overhead, recuerda), puedes liberar todo el espacio llamando a brk con el valor devuelto por la primera llamada a sbrk, por lo tanto rebobinando el brk.

void *memorypool;
void initmemorypool(void){
    memorypool = sbrk(0);
}
void resetmemorypool(void){
    brk(memorypool);
}

Incluso podría apilar estas regiones, descartando la región más reciente rebobinando la ruptura al inicio de la región.


Una cosa más ...

sbrk también es útil en code golf porque es 2 caracteres más corto que malloc.

 8
Author: luser droog,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-13 12:38:59

Hay una asignación de memoria privada anónima designada especial (tradicionalmente ubicada justo más allá de los datos/bss, pero Linux moderno realmente ajustará la ubicación con ASLR). En principio no es mejor que cualquier otra asignación que pueda crear con mmap, pero Linux tiene algunas optimizaciones que permiten expandir el final de esta asignación (usando la llamada syscall brk) hacia arriba con un costo de bloqueo reducido en relación con lo que incurriría mmap o mremap. Esto lo hace atractivo para malloc implementaciones a utilizar al implementar el montón principal.

 3
Author: R..,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-09 00:47:26

Puedo responder a su segunda pregunta. Malloc fallará y devolverá un puntero nulo. Es por eso que siempre busca un puntero nulo cuando asigna memoria dinámicamente.

 0
Author: Brian Gordon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-08 20:59:57

El montón se coloca en último lugar en el segmento de datos del programa. brk() se usa para cambiar (expandir) el tamaño del montón. Cuando el montón no puede crecer más, cualquier llamada malloc fallará.

 0
Author: Anders Abel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-08 21:00:19

El segmento de datos es la porción de memoria que contiene todos sus datos estáticos, leídos desde el ejecutable en el lanzamiento y generalmente llenos de cero.

 0
Author: monchalve,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-08 21:02:13

Malloc utiliza la llamada al sistema brk para asignar memoria.

Incluye

int main(void){

char *a = malloc(10); 
return 0;
}

Ejecute este sencillo programa con strace, llamará al sistema brk.

 0
Author: skanzariya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-12 16:44:12
  1. La llamada al sistema que maneja la asignación de memoria es sbrk(2). Aumenta o disminuye el espacio de direcciones del proceso en un número especificado de bytes.

  2. La función de asignación de memoria, malloc(3), implementa un tipo particular de asignación. malloc() función, que probablemente utilizará la llamada al sistema sbrk().

La llamada al sistema sbrk(2) en el núcleo asigna una porción adicional de espacio en nombre del proceso. La función de biblioteca malloc() administra este espacio desde nivel de usuario.

 0
Author: Yogeesh H T,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-30 07:19:59