core(5)

NAZWA
core - plik zrzutu pamięci

OPIS
Dla pewnych sygnałów domyślną akcją procesu jest zakończenie działania i utworzenie pliku zrzutu pamięci (core), czyli pliku zawierającego obraz pamięci procesu w czasie, gdy został on zakończony. Dowolny debugger (np. gdb(1)) może użyć tego obrazu do zbadania stanu programu w czasie jego zakończenia. Listę sygnałów powodujących utworzenie pliku core przez proces można znaleźć w signal(7).

Proces może ustawić miękki limit zasobów RLIMIT_CORE, aby ograniczyć maksymalny rozmiar pliku, który zostanie utworzony po otrzymaniu sygnału powodującego zrzut pamięci; szczegółowe informacje można znaleźć w getrlimit(2).

W następujących wypadkach plik zrzutu pamięci nie będzie utworzony:

•   Proces nie ma uprawnień, aby zapisać plik zrzutu pamięci (Domyślnie plik ten nazywa się core lub core.pid, gdzie pid jest identyfikatorem procesu który zrzuca pamięć i jest tworzony w bieżącym katalogu roboczym procesu. Nazwę tę można zmienić - patrz niżej). Zapisywanie pliku zrzutu nie powiedzie się również wtedy, gdy prawa katalogu, w którym ten plik miałby być utworzony, nie pozwalają na zapis do niego lub gdy plik o tej samej nazwie istnieje i nie jest zapisywalny lub nie jest zwykłym plikiem (ale np. katalogiem lub dowiązaniem symbolicznym).

•   Już istnieje (zapisywalny, zwykły) plik o tej samej nazwie, jaka zostałaby użyta dla pliku zrzutu pamięci, jednakże plik ten ma więcej niż jedno dowiązanie twarde.

•   System plików, w którym zostałby utworzony plik zrzutu pamięci, jest pełny, wyczerpał mu się limit i-węzłów, jest zamontowany w trybie tylko do odczytu albo użytkownik osiągnął przydział systemu plików.

•   Nie istnieje katalog, w którym miałby być utworzony plik zrzutu pamięci.

•   Limity zasobów RLIMIT_CORE (rozmiar pliku zrzutu pamięci) lub RLIMIT_FSIZE (rozmiar pliku) dla procesu są ustawione na zero; patrz getrlimit(2) i dokumentacja wbudowanego w powłokę polecenia ulimit (lub limit w powłoce csh(1)). RLIMIT_CORE będzie ignorowane, gdy system skonfigurowano do przekazywania zrzutów pamięci za pomocą potoku do programu.

•   Wykonywany przez proces plik binarny nie ma włączonych uprawnień do odczytu (jest to zabezpieczenie, w celu wykluczenia sytuacji, gdy plik wykonywany, którego odczyt jest niemożliwy, tworzyłby — potencjalnie odczytywalny — zrzut pamięci zawierający obraz pliku wykonywalnego).

•   Proces uruchomił program z flagą set-user-ID (set-group-ID), którego właścicielem jest użytkownik (grupa) inny niż rzeczywisty użytkownik (grupa) procesu lub proces wykonujący program ma przywileje plikowe (zob. capabilities(7)). (Jednakże patrz w prctl(2) opis operacji PR_SET_DUMPABLE oraz w proc(5) opis pliku /proc/sys/fs/suid_dumpable).

•   /proc/sys/kernel/core_pattern jest pusty i /proc/sys/kernel/core_uses_pid zawiera wartość 0 (pliki te opisano poniżej). Proszę zauważyć, że gdy /proc/sys/kernel/core_pattern jest pusty i /proc/sys/kernel/core_uses_pid zawiera wartość 1, to pliki zrzutu pamięci będą miały nazwy w postaci .pid, a pliki takie są ukryte, chyba że poda się opcję ls(1) -a.

•   (od Linuksa 3.7) Jądro zostało zbudowane bez opcji CONFIG_COREDUMP.

Dodatkowo zrzut pamięci może nie zawierać części przestrzeni adresowej procesu jeśli użyto flagi MADV_DONTDUMP madvise(2).

W systemach korzystającym z systemd(1) jako init, pliki zrzutu pamięci mogą być w zamian umieszczane w położeniu wskazanym przez systemd(1). Więcej szczegółów poniżej.

  Nazwy plików zrzutu pamięci
Domyślnie plik zrzutu pamięci nazywa się core, jednakże w pliku /proc/sys/kernel/core_pattern (od wersji 2.6 i 2.4.21 Linuksa) można zdefiniować szablon, który będzie użyty do nazywania plików zrzutu pamięci. Szablon ten może zawierać specjalne znaczniki zaczynające się od „%”, które podczas tworzenia pliku zrzutu będą zastąpione następującymi wartościami:

    %%  Pojedynczy znak %.
    %c  Miękki limit zasobu rozmiaru pliku zrzutu pamięci procesu zrzucającego pamięć (od Linuksa 2.6.24).
    %C  CPU the task ran on (since Linux 6.2).
    %d  Tryb zrzutu — taki sam jak wartość zwracana przez prctl(2) PR_GET_DUMPABLE (od Linuksa 3.7).
    %e  Wartość comm procesu lub wątku, zwykle równa nazwie pliku wykonywalnego (bez przedrostka ścieżki i obcięta do maksymalnie 15 znaków), lecz mogąca być również zmodyfikowana na inną wartość, zob. opis /proc/pid/comm i /proc/pid/task/tid/comm w proc(5).
    %E  Nazwa ścieżki pliku wykonywalnego, z ukośnikami („/”) zastąpionymi przez wykrzykniki („!”) (od Linuksa 3.0).
    %f  Actual filename of executable, which may differ from %e (since Linux 5.9).
    %F  PIDFD zrzucanego procesu (od Linuksa 6.16).
    %g  Numeryczny identyfikator grupy (GID) procesu, który zrzucił pamięć.
    %h  Nazwa komputera (taka sama jak nodename zwracane przez uname(2)).
    %i  identyfikator wątku (TID) który zainicjował zrzut pamięci, taki jak w przestrzeni nazw PID-ów w której wątek działa (od Linuksa 3.18).
    %I  identyfikator wątku (TID) który zainicjował zrzut pamięci, taki jak w początkowej przestrzeni nazw PID-ów (od Linuksa 3.18).
    %p  identyfikator zrzucanego procesu, taki jak w przestrzeni nazw PID-ów w której proces działa.
    %P  identyfikator zrzucanego procesu, taki jak w początkowej przestrzeni nazw PID-ów (od Linuksa 3.12).
    %s  Numer sygnału powodującego zrzut pamięci.
    %t  Czas zrzutu wyrażony w sekundach od początku epoki, czyli od 1970-01-01 00:00:00 +0000 (UTC).
    %u  Numeryczny identyfikator użytkownika (UID) procesu, który zrzucił pamięć.

Jeśli szablon kończy się pojedynczym znakiem %, to znak ten zostanie usunięty z nazwy pliku zrzutu. Podobnie zostaną usunięte wszelkie kombinacje % ze znakami innymi niż te, wymienione powyżej. Wszystkie inne znaki szablonu staną się częścią nazwy pliku zrzutu. Szablon może zawierać znaki „/”, które są interpretowane jako separatory nazw katalogów. Maksymalna długość wygenerowanej nazwy pliku wynosi 128 bajtów (64 bajty w wersjach wcześniejszych niż Linux 2.6.19). Domyślną wartością jest „core”. W celu zachowania wstecznej zgodności, jeśli /proc/sys/kernel/core_pattern nie zawiera %p, a /proc/sys/kernel/core_uses_pid (patrz niżej) ma niezerową wartość, to .PID będzie dołączony do nazwy pliku zrzutu.

Ścieżki są interpretowane zgodnie z ustawieniami aktywnymi dla załamującego się procesu. Oznacza to: przestrzeń nazw montowania (zob. mount_namespaces(7)), jego bieżący katalog roboczy (odnaleziony za pomocą getcwd(2)) oraz jego katalog główny (zob. chroot(2)).

Od Linuksa 2.4, Linux dostarcza również bardziej prymitywną metodę kontrolowania nazwy pliku zrzutu pamięci. Gdy plik /proc/sys/kernel/core_uses_pid zawiera wartość 0, plik zrzutu pamięci ma po prostu nazwę core. Gdy plik ten zawiera wartość niezerową, plik zrzutu pamięci będzie zawierał w swojej nazwie ID procesu, w postaci core.PID.

Od Linuksa 3.6 jeśli /proc/sys/fs/suid_dumpable ustawiono na 2 („suidsafe”) to wzorzec musi być albo ścieżką absolutną (zaczynającą się znakiem „/”) lub potokiem, zgodnie z poniższą definicją.

  Przekazywanie zrzutów pamięci potokiem do programu
Od wersji Linuksa 2.6.19, Linux obsługuje alternatywną składnię pliku /proc/sys/kernel/core_pattern. Jeśli pierwszym znakiem pliki jest symbol potoku (|), to wszystko, co po nim występuje, jest interpretowane jako wiersz polecenia dla programu w przestrzeni użytkownika (lub skryptu), który ma być wykonany.

Od Linuksa 5.3.0, szablon potoku jest dzielony na spacjach na listę argumentów przed rozwinięciem parametrów szablonu. We wcześniejszych jądrach parametry szablonu były najpierw rozwijane, a wynikowy łańcuch był dzielony na spacjach na listę argumentów. Oznacza to, że we wcześniejszych jądrach, nazwy plików wykonywalnych dodawane za pomocą parametrów szablonu %e i %E mogły ulec podzieleniu na wiele argumentów. Procedura obsługi zrzutu pamięci musi zatem umieszczać nazwy plików wykonywalnych jako ostatni argument i upewnić się, że połączy wszystkie składowe nazwy pliku wykonywalnego używającego spacji. Nazwy plików wykonywalnych zawierające wiele spacji nie były prawidłowo reprezentowane we wcześniejszych jądrach, co oznacza, że procedura obsługi zrzutu pamięci musi używać mechanizmów do odnalezienia nazwy pliku wykonywalnego.

Zamiast zostać zapisanym na dysku, zrzut pamięci jest przekazywany na standardowe wejście programu. Proszę zauważyć, że:

•   Program musi być podany używając ścieżki bezwzględnej (lub ścieżki względnej w stosunku do korzenia systemu plików, czyli katalogu /) i musi występować bezpośrednio po znaku „|”.

•   Argumenty linii poleceń mogą zawierać dowolne z wymienionych powyżej specyfikatorów %. Na przykład, aby przekazać identyfikator procesu zrzucającego pamięć, należy podać %p jako argument.

•   Proces tworzony do wykonania programu jest wykonywany jako użytkownik i grupa root.

•   Uruchomienie jako root nie pomija w żaden sposób procedur bezpieczeństwa. Przede wszystkim, LSM-y (np. SELinux) są wciąż aktywne i mogą zapobiec uzyskaniu szczegółów poprzez /proc/pid, przez procedurę obsługi, na temat załamanego procesu.

•   Ścieżka programu jest interpretowana zgodnie z początkową przestrzenią nazw montowań, jako że zawsze jest on tu wykonywany. Nie mają na nią wpływu ustawienia (np. katalog główny, przestrzeń nazw montowań, bieżący katalog roboczy) załamującego się procesu.

•   Proces działa w swoich pierwotnych przestrzeniach nazw (PID, montowania, użytkownika itd.), a nie w przestrzeniach nazw załamującego się procesu. Można użyć znaczników, takich jak %P, aby odnaleźć właściwy katalog /proc/pid i sprawdzić/wejść w przestrzenie nazw załamującego się procesu, jeśli jest taka potrzeba.

•   Proces uruchamia się ze swoim bieżącym katalogiem roboczym jako katalogiem głównym. Jeśli to wskazane, można zmienić katalog roboczy zrzucanego procesu za pomocą wartości podanej przez znacznik %P, aby zmienić położenie zrzucanego procesu za pomocą /proc/pid/cwd.

•   Można przekazać do programu argumenty linii poleceń (od Linuksa 2.6.24), rozdzielając je spacjami (aż do wyczerpania limitu całkowitej długości linii wynoszącego 128 bajtów).

•   Limit RLIMIT_CORE nie jest pilnowany w przypadku zrzutów pamięci, które są przekazywane potokiem za pomocą tego mechanizmu.

  /proc/sys/kernel/core_pipe_limit
Przy zbieraniu zrzutów pamięci za pomocą potoku do program w przestrzeni użytkownika, może okazać się przydatne zbieranie danych o załamującym się procesie z katalogu /proc/pid procesu. Aby uczynić to bezpiecznie, jądro musi zaczekać na wyjście przez program zbierający zrzut pamięci, aby nie usuwać przedwcześnie plików /proc/pid załamującego się procesu. Z drugiej jednak strony, daje to możliwość blokowania dorzynania (ang. reaping) załamanego procesu, przez niepoprawnie działający program zbierający, który może nigdy nie wyjść.

Od Linuksa 2.6.32, można użyć /proc/sys/kernel/core_pipe_limit do obrony przed taką ewentualnością. Wartość pliku definiuje, jak wiele współbieżnych załamujących się procesów można przekazać równolegle, potokiem, do programów w przestrzeni użytkownika. Po przekroczeniu tej wartości, załamujące się programy niemieszczące się w tej wartości są odnotowywane w dzienniku jądra, a ich zrzuty pamięci są pomijane.

Wartość 0 w tym pliku ma specjalne znaczenie. Oznacza, że równolegle może być przechwytywana nieograniczona liczba procesów, ale nie zajdzie żadne oczekiwanie (tj. program zbierający nie ma gwarancji dostępu do /proc/<załamujący-się-PID>). Domyślną wartością pliku jest 0.

  Kontrolowanie mapowań zapisywanych do pliku zrzutu pamięci
Od Linuksa 2.6.23, można użyć specyficznego dla Linuksa pliku /proc/PID/coredump_filter do kontrolowania, które segmenty pamięci zostaną zapisane do pliku zrzutu pamięci, w przypadku gdy pamięć jest zrzucana przez proces o podanym identyfikatorze.

Wartość w tym pliku jest maską bitową typów mapowań pamięci (patrz mmap(2)). Jeśli bit jest ustawiony w masce, to odpowiadające mu mapowanie jest zapisywane w pliku, w przeciwnym wypadku - nie jest. Bity w masce mają następujące znaczenia:

    bit 0
        Zrzucanie anonimowych mapowań prywatnych.
    bit 1
        Zrzucanie anonimowych mapowań współdzielonych.
    bit 2
        Zrzucanie prywatnych mapowań plików.
    bit 3
        Zrzucanie współdzielonych mapowań plików.
    bit 4 (od Linuksa 2.6.24)
        Zrzucanie nagłówków ELF.
    bit 5 (od Linuksa 2.6.28)
        Zrzucanie prywatnych dużych stron.
    bit 6 (od Linuksa 2.6.28)
        Zrzucanie współdzielonych dużych stron.
    bit 7 (od Linuksa 4.4)
        Zrzucanie prywatnych stron DAX.
    bit 8 (od Linuksa 4.4)
        Zrzucanie współdzielonych stron DAX.

Domyślnie ustawione są następujące bity: 0, 1, 4 (jeśli jest włączona opcja CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS konfiguracji jądra) oraz 5. Tę wartość domyślną można zmodyfikować przy rozruchu za pomocą opcji coredump_filter.

Wartość tego pliku jest wyświetlana szesnastkowo (z tego powodu domyślna wartość jest wyświetlana jako 33).

Strony wejścia/wyjścia mapowane w pamięci, takie jak bufor ramki, nigdy nie są zrzucane, a wirtualne strony DSO (vdso(7)) są zawsze zrzucane, niezależnie od wartości coredump_filter.

Proces-dziecko utworzony przez fork(2) dziedziczy wartość coredump_filter od swojego rodzica; wartość ta jest także zachowywana podczas execve(2).

Może być użyteczne ustawienie coredump_filter w powłoce przed uruchomieniem programu, na przykład:

    $ echo 0x7 > /proc/self/coredump_filter
    $ ./some_program

Plik istnieje, jeśli jądro zostało zbudowane z włączoną opcją konfiguracji CONFIG_ELF_CORE.

  Zrzuty pamięci i systemd
W systemach korzystających z init w postaci systemd(1), zrzuty pamięci mogą być umieszczane w położeniu określanym przez systemd(1). systemd(1) używa do tego funkcji core_pattern, która pozwala na przekazywanie potokiem zrzutów pamięci do programu. Można to zweryfikować, sprawdzając czy zrzuty pamięci są przekazywane potokiem do programu systemd-coredump(8):

    $ cat /proc/sys/kernel/core_pattern;
    |/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %e

W tym przypadku, zrzuty pamięci będą umieszczane w położeniu skonfigurowanym dla systemd-coredump(8), zwykle jako pliki skompresowane lz4(1), w katalogu /var/lib/systemd/coredump/. Można wypisać listę zarejestrowanych przez systemd-coredump(8) zrzutów pamięci za pomocą coredumpctl(1):

    $ coredumpctl list | tail -5;
    Wed 2017-10-11 22:25:30 CEST  2748 1000 1000 3 present  /usr/bin/sleep
    Thu 2017-10-12 06:29:10 CEST  2716 1000 1000 3 present  /usr/bin/sleep
    Thu 2017-10-12 06:30:50 CEST  2767 1000 1000 3 present  /usr/bin/sleep
    Thu 2017-10-12 06:37:40 CEST  2918 1000 1000 3 present  /usr/bin/cat
    Thu 2017-10-12 08:13:07 CEST  2955 1000 1000 3 present  /usr/bin/cat

Informacje pokazywane dla każdego zrzutu pamięci obejmują datę i czas zrzutu, identyfikatory: procesu, użytkownika i grupy zrzucającego procesu, numer sygnału powodującego zrzut i ścieżka do pliku wykonywalnego, który był uruchomiony przez zrzucany proces. Można podać do coredumpctl(1) wiele opcji, które pozwalają wskazać plik zrzutu pamięci, który ma być wyciągnięty z lokalizacji systemd(1) do określonego pliku. Na przykład, aby wydobyć zrzut pamięci dla procesu o PID 2955 pokazanego powyżej, do pliku nazwanego core w bieżącym katalogu można użyć polecenia:

    $ coredumpctl dump 2955 -o core;

Więcej szczegółów znajduje się w podręczniku systemowym coredumpctl(1).

Aby (na stałe) wyłączyć mechanizm systemd(1) archiwizujący zrzuty pamięci, przywracając bardziej tradycyjne zachowanie linuksowe, można przesłonić mechanizm systemd(1), za pomocą konstrukcji podobnej do poniżej:

    # echo "kernel.core_pattern=core.%p" > \;
                   /etc/sysctl.d/50-coredump.conf
    # /lib/systemd/systemd-sysctl;

Można również tymczasowo (do następnego przeładowania systemu) zmienić ustawienie core_pattern poleceniem podobnym do poniższego (które powoduje włączenie do nazw zrzutu pamięci również nazwy pliku wykonywalnego i numer sygnału wyzwalające zrzut pamięci):

    # sysctl -w kernel.core_pattern="%e-%s.core";

UWAGI
Aby uzyskać zrzut pamięci działającego procesu, można użyć polecenia gcore programu gdb(1).

W wersjach jądra Linux do 2.6.27 włącznie, jeżeli pamięć zrzuca proces wielowątkowy (albo - bardziej precyzyjnie - proces, który dzieli swą pamięć z innym procesem utworzonym z flagą CLONE_VM funkcji clone(2)), to identyfikator procesu zawsze będzie dołączony do nazwy pliku zrzutu, chyba że ów identyfikator procesu już występuje w nazwie pliku, ponieważ w pliku /proc/sys/kernel/core_pattern użyto specyfikatora p (Jest to szczególnie użyteczne podczas stosowania przestarzałej implementacji LinuxThreads, w której każdy wątek procesu ma inny PID).

PRZYKŁADY
Poniższy program pokazuje użycie składni potoku w pliku /proc/sys/kernel/core_pattern. Poniższa sesja powłoki demonstruje użycie tego programu (skompilowanego do pliku o nazwie core_pattern_pipe_test):

    $ cc -o core_pattern_pipe_test core_pattern_pipe_test.c
    $ su
    Hasło:
    # echo "|$PWD/core_pattern_pipe_test %p UID=%u GID=%g sig=%s" > \
    /proc/sys/kernel/core_pattern
    # exit
    $ sleep 100
    ^\                     # wpisz control+odwrotny ukośnik
    Quit (core dumped)
    $ cat core.info
    argc=5
    argc[0]=</home/mtk/core_pattern_pipe_test>
    argc[1]=<20575>
    argc[2]=<UID=1000>
    argc[3]=<GID=100>
    argc[4]=<sig=3>
    Całkowita liczba bajtów pliku core: 282624

  Kod źródłowy programu
    /* core_pattern_pipe_test.c */

    #define _GNU_SOURCE
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <limits.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>

    #define BUF_SIZE 1024

    int
    main(int argc, char *argv[])
    {
        ssize_t nread, tot;
        char buf[BUF_SIZE];
        FILE *fp;
        char cwd[PATH_MAX];

        /* Zmienia bieżący katalog roboczy na katalog procesu
           który generuje zrzut pamięci.  */

        snprintf(cwd, PATH_MAX, "/proc/%s/cwd", argv[1]);
        chdir(cwd);

        /* Zapisuje wyjście do pliku "core.info" w tym katalogu.  */

        fp = fopen("core.info", "w+");
        if (fp == NULL)
            exit(EXIT_FAILURE);

        /* Wyświetla argumenty wiersza poleceń przekazane do programu
           filtrującego "core_pattern".  */

        fprintf(fp, "argc=%d\n", argc);
        for (size_t j = 0; j < argc; j++)
            fprintf(fp, "argc[%zu]=<%s>\n", j, argv[j]);

        /* Zlicza bajty na standardowym wejściu (daje rozmiar
           zrzutu pamięci).  */

        tot = 0;
        while ((nread = read(STDIN_FILENO, buf, BUF_SIZE)) > 0)
            tot += nread;
        fprintf(fp, "Całkowita liczba bajtów pliku core: %zd\n", tot);

        fclose(fp);
        exit(EXIT_SUCCESS);
    }

ZOBACZ TAKŻE
bash(1), coredumpctl(1), gdb(1), getrlimit(2), mmap(2), prctl(2), sigaction(2), elf(5), proc(5), pthreads(7), signal(7), systemd-coredump(8)

TŁUMACZENIE
Tłumaczenie niniejszej strony podręcznika: Robert Luberda <robert@debian.org> i Michał Kułach <michal.kulach@gmail.com>

Niniejsze tłumaczenie jest wolną dokumentacją. Bliższe informacje o warunkach licencji można uzyskać zapoznając się z GNU General Public License w wersji 3 (https://www.gnu.org/licenses/gpl-3.0.html) lub nowszej. Nie przyjmuje się ŻADNEJ ODPOWIEDZIALNOŚCI.

Błędy w tłumaczeniu strony podręcznika prosimy zgłaszać na adres listy dyskusyjnej manpages-pl-list@lists.sourceforge.net.