Do czego służy wywołanie systemowe brk ()?

Zgodnie z instrukcją programistów Linuksa:

Brk () i sbrk() zmieniają lokalizację przerwania programu, co określa koniec segmentu danych procesu.

Co oznacza tutaj segment danych? Czy to tylko segment danych czy data, BSS i heap razem wzięte?

Według wiki:

Czasami obszary danych, BSS i sterty są zbiorczo określane jako "segment danych".

Nie widzę powodu do zmiany rozmiar tylko segmentu danych. Jeśli są to dane, BSS i sterta razem, to ma to sens, ponieważ sterta dostanie więcej miejsca.

Co sprowadza mnie do drugiego pytania. We wszystkich artykułach, które do tej pory czytałem, autor mówi, że sterta rośnie w górę, a stos rośnie w dół. Ale to, czego nie wyjaśniają, to to, co się dzieje, gdy sterta zajmuje całą przestrzeń między stertą a stosem?

Tutaj wpisz opis obrazka

9 answers

Widzę wiele częściowych odpowiedzi, ale żadnej pełnej odpowiedzi. Oto to zdjęcie, które wrzuciłeś ponownie:

uproszczony obraz układu pamięci wirtualnej

"break"--adres manipulowany przez brk i sbrk--jest linią przerywaną na górzesterty . Dokumentacja, którą przeczytałeś opisuje to jako koniec "segmentu danych", ponieważ w tradycyjnym (Pre-shared-libraries, pre-mmap) Uniksie segment danych był ciągły ze stertą; przed uruchomieniem programu jądro ładowało "tekst" i " dane" blokuje DO PAMIĘCI RAM poczynając od adresu zero (w rzeczywistości nieco powyżej adresu zero, tak aby wskaźnik NULL nie wskazywał na nic) i ustawia adres przerwy na koniec segmentu danych. Pierwsze wywołanie malloc użyje sbrk, aby przesunąć podziały i utworzyć stertę pomiędzy górną częścią segmentu danych i nowym, wyższym adresem przerwy, jak pokazano na diagramie, a następnie użycie malloc użyje go, aby zwiększyć stertę, jeśli jest to konieczne.

, stos zaczyna się na szczycie pamięci i rośnie w dół. Stos nie wymaga jawnych wywołań systemowych, aby go powiększyć; albo zaczyna się z przydzieloną do niego ilością pamięci RAM, jaką kiedykolwiek może mieć (było to tradycyjne podejście), albo pod stos znajduje się Region zarezerwowanych adresów, do którego jądro automatycznie przydziela pamięć RAM, gdy zauważy próbę zapisu (jest to nowoczesne podejście). Tak czy inaczej, na dole przestrzeni adresowej może znajdować się obszar "straży" które mogą być używane do stosu. Jeśli ten region istnieje (wszystkie nowoczesne systemy tak robią), to jest on trwale nieodmapowany; jeśli albo stos lub sterta spróbuje się do niego urosnąć, dostajesz błąd segmentacji. Tradycyjnie jednak, jądro nie próbowało wyegzekwować granicy; stos mógł urosnąć w stertę, albo sterta mogła urosnąć w stertę, i tak czy inaczej, bazgroły na danych innych i program uległby awarii. Gdybyś miał szczęście, to by się rozbiło. natychmiast.

Nie wiem, skąd się wzięła liczba 512GB na tym diagramie. Oznacza to 64-bitową wirtualną przestrzeń adresową, która jest niezgodna z bardzo prostą mapą pamięci, którą tam masz. Prawdziwa 64-bitowa przestrzeń adresowa wygląda mniej więcej tak:

mniej uproszczona przestrzeń adresowa

Nie jest to zdalnie skalowane i nie powinno być interpretowane jako dokładnie to, jak dany OS robi rzeczy (po narysowaniu go odkryłem, że Linux faktycznie umieszcza plik wykonywalny znacznie bliżej adresu zero niż ja myślałem, że tak, a Biblioteki dzielone pod zaskakująco wysokimi adresami). Czarne obszary tego diagramu nie są mapowane - każdy dostęp powoduje natychmiastowy segfault - i są one gigantyczne w stosunku do szarych obszarów. Jasnoszare regiony to program i jego biblioteki współdzielone (mogą być dziesiątki bibliotek współdzielonych); każda z nich ma niezależny segment tekstu i danych (oraz segment "bss", który również zawiera dane globalne, ale jest inicjowany na wszystkie bity-zero, a nie na wszystkie bity. zajmowanie miejsca w pliku wykonywalnym lub bibliotece na dysku). Sterta nie jest już koniecznie ciągła z segmentem danych wykonywalnych -- narysowałem to w ten sposób, ale wygląda na to, że Linux przynajmniej tego nie robi. Stos nie jest już powiązany z wierzchołkiem wirtualnej przestrzeni adresowej, a odległość między stosem a stosem jest tak ogromna, że nie musisz się martwić o jego przekroczenie.

Przerwa jest nadal górną granicą sterty. Nie pokazałem jednak, że tam mogą to być dziesiątki niezależnych przydziałów pamięci w czerni, zrobionych mmap zamiast brk. (System operacyjny będzie starał się trzymać je z dala od brk obszaru, aby nie kolidowały.)

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-09 01:19:37

Minimal runnable przykład

Do czego służy funkcja systemowa brk?

Prosi jądro, aby umożliwiło Ci odczyt i zapis do sąsiedniej części pamięci zwanej stertą.

Jeśli nie zapytasz, może cię to rozłączyć.

BEZ 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;
}

Z 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;
}

Testowane na Ubuntu 14.04.

Wizualizacja wirtualnej przestrzeni adresowej

Przed brk:

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

Po brk(p + 2):

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

Po brk(b):

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

Aby lepiej zrozumieć przestrzenie adresowe, powinieneś zapoznać się z pagingiem: Jak działa paging x86?

Więcej informacji

brk kiedyś był to POSIX, ale został usunięty w POSIX 2001, stąd potrzeba _GNU_SOURCE dostępu do wrappera glibc.

Usunięcie jest prawdopodobnie spowodowane wprowadzeniem mmap, który jest supersetem, który umożliwia przydzielanie wielu zakresów i większą alokację opcje.

Wewnętrznie, jądro decyduje, czy proces może mieć tyle pamięci, a do tego używa się stron pamięci .

brk i {[12] } są powszechnymi mechanizmami bazowymi, których libc używa do implementacji malloc w systemach POSIX.

To wyjaśnia jak stos porównuje się do sterty: Jaka jest funkcja instrukcji push / pop używanych w rejestrach w x86 assembly?

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-01 15:37:03

Możesz użyć brk i sbrk siebie, aby uniknąć "malloc overhead", na które wszyscy zawsze narzekają. Ale nie można łatwo użyć tej metody w połączeniu z malloc, więc jest to właściwe tylko wtedy, gdy nie musisz free niczego. Ponieważ nie możesz. powinieneś także unikać wywołań bibliotek, które mogą używać malloc wewnętrznie. Ie. strlen jest prawdopodobnie bezpieczny, ale fopen prawdopodobnie nie jest.

Zadzwoń sbrk tak jak ty zadzwonisz malloc. Zwraca wskaźnik do bieżącej przerwy i przyrosty przerwa o taką kwotę.

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

Podczas gdy nie możesz zwolnić pojedynczych alokacji (ponieważ nie ma malloc-overhead, pamiętaj), możesz uwolnić całą przestrzeń przez wywołaniebrk z wartością zwróconą przez pierwsze wywołanie do sbrk, w ten sposób przewijając BRK .

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

Można nawet układać te regiony, odrzucając ostatni region przez przewinięcie przerwy do początku regionu.


Jeszcze jedno. ...

sbrk jest również przydatny w kodzie golf, ponieważ jest o 2 znaki krótszy od 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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-13 12:38:59

Istnieje specjalne anonimowe mapowanie pamięci prywatnej (tradycyjnie zlokalizowane tuż za data/bss, ale nowoczesny Linux faktycznie dostosuje lokalizację za pomocą ASLR). W zasadzie nie jest to lepsze niż jakiekolwiek inne mapowanie, które można utworzyć za pomocą mmap, ale Linux ma pewne optymalizacje, które umożliwiają rozszerzenie końca tego mapowania (przy użyciu syscall brk) w górę z obniżonym kosztem blokowania w stosunku do tego, co mmap lub mremap poniosłoby. To czyni go atrakcyjnym dla malloc implementacje do wykorzystania przy implementacji sterty głównej.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-09 00:47:26

Mogę odpowiedzieć na twoje drugie pytanie. Malloc zawiedzie i zwróci wskaźnik null. Dlatego zawsze sprawdzasz wskaźnik null podczas dynamicznego przydzielania pamięci.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-08 20:59:57

Sterta jest umieszczana jako ostatnia w segmencie danych programu. brk() służy do zmiany (rozwinięcia) rozmiaru sterty. Gdy sterta nie może już rosnąć żadne wywołanie malloc nie powiedzie się.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-08 21:00:19

Segment danych jest częścią pamięci, która przechowuje wszystkie dane statyczne, wczytane z pliku wykonywalnego podczas uruchamiania i zwykle wypełnione zero.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-08 21:02:13

Malloc używa wywołania systemowego brk do przydzielania pamięci.

Include

int main(void){

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

Uruchom ten prosty program ze strace, wywoła system 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/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-12 16:44:12
  1. Wywołanie systemowe, które obsługuje alokację pamięci, to sbrk(2). Zwiększa lub zmniejsza przestrzeń adresową procesu o określoną liczbę bajtów.

  2. Funkcja alokacji pamięci, malloc(3), implementuje jeden szczególny rodzaj alokacji. malloc() funkcja, która prawdopodobnie użyje wywołania systemowego sbrk().

Wywołanie systemowe sbrk(2)w jądrze przydziela dodatkowy kawałek miejsca w imieniu procesu. Funkcja biblioteki malloc() zarządza tą przestrzenią z poziomu użytkownika .

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-30 07:19:59