pthreads_create PTHREAD_CREATE_DETACHED - skąd błąd naruszenia ochrony pamięci w systemie Linuks?

0

Witam,
proszę o wskazówki - co w poniższym programie może powodować naruszenie ochrony pamięci w systemie Linuks?
Inny system, na którym testowałem poniższą aplikację to FreeBSD.
W systemie Linuks testowałem aplikację z glibc oraz z musl-libc - w obu przypadkach otrzymywałem naruszenie ochrony pamięci.
W systemie FreeBSD (TrueOS) naruszenia pamięci nie było.
Liczba tworzonych wątków praktycznie nie ma wpływu na wstepowanie naruszenia ochrony pamięci - wpłwa jedynie na częstotliwość tego zjawiska.

Program:

#include <unistd.h>
#include <stdio.h>
#include <pthread.h>

#define QSIZE 100000
pthread_mutex_t lock;
int active = QSIZE;

static void *threadapp(void *o)
{
        pthread_mutex_lock(&lock);
        active--;
        pthread_mutex_unlock(&lock);
        pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
        pthread_attr_t attr;
        int i;
        pthread_mutex_init(&lock, NULL);
        if(pthread_attr_init(&attr) != 0)       {
                perror("attr init");
                return 1;
        }
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        for(i=0;;++i)   {
                int j;
                printf("%d: Start", i);
                fflush(stdout);
                active = QSIZE;
                for(j=0; j<QSIZE; ++j)  {
                        int status;
                        do      {
                                pthread_t ret;
                                status = pthread_create(&ret, &attr, &threadapp, NULL);
                        }
                        while(status != 0);
                }
                for(;;) {
                        pthread_mutex_lock(&lock);
                        if(active == 0) {
                                pthread_mutex_unlock(&lock);
                                break;
                        }
                        pthread_mutex_unlock(&lock);
                }
                printf("...Done\n");
        }
        if(pthread_attr_destroy(&attr) != 0)    {
                perror("attr destroy");
                return 1;
        }
        return 0;
}

Kompilacja i uruchomienie:

gcc multitest.c -o multitest -pthread
while ./multitest ; do sleep 0; done

Z tego co zaobserwowałem - dodanie dodatkowych poleceń do wątku powoduje dłuższe działanie aplikacji bez naruszenia ochrony
pamięci.
Będę wdzięczny za pomoc w namierzeniu błędu. Być może czegoś w obsłudze wątków nie rozumiem i przeoczyłem np. maskowanie
sygnałów.

0

Widzę, że nie wiemy co robimy.
Dodaj do pętli do-while po wywołaniu pthread_create linijkę:
printf("%d: pthreaded execuded \n",j);
a wszystko powinno stać się jasne.

0
Płynąca Woda napisał(a):

Widzę, że nie wiemy co robimy.
Dodaj do pętli do-while po wywołaniu pthread_create linijkę:
printf("%d: pthreaded execuded \n",j);
a wszystko powinno stać się jasne.

Proszę o wskazanie / poprawienie błędu. Być może zbyt długo to analizuję i nie widzę przyczyny naruszenia ochrony pamięci.
Pętla tworzy zdefiniowaną ilość wątków - jeśli pthread_create() ukończy się sukcesem do-while kończy działanie, jeśli nie to
powtarza próbę utworzenia wątku.

0

Pierwsza podstawowa sprawa to limit ilości wątków w procesie:
cat /proc/sys/kernel/threads-max
Dla przykładu u mnie jest 48 tyś z hakiem. Ty próbujesz odpalić 100tyś wątków, a potem je zakończyć. Nie wspomnę już, że dodatkowo robisz blokady.

A co z pamięcią stosu?
Nie odpalisz nie wiadomo ile podprocesów, chociaż możesz. Możesz odpalić tyle wątków ile masz pamięci ram fizycznej + ten swap, chociaż w tym zakresie może być jakieś ograniczenie.
Aby tego dokonać wystarczy że wpiszesz do odpowiednich zmiennych systemowych i plików właściwe wartości. Wszystko to jest możliwe i różne dla różnych systemów z rodziny unixowatych. Do tego jest to głupie, bo o ile modyfikacja wielkości wątków zapewne nic nie zmieni to inne wartości zależne mogą spowodować pewne szkody.

0

Dzięki za odpowiedź.
To jest tylko aplikacja testowa (okrojona do minimum w celu znalezienia błędu, który gdzieś popełniam). W rzeczywistej aplikacji wątki będą tworzone w miarę potrzeby. Warunki zewnętrzne decydują ile i jak często. Praktycznie nie mam wpływu na ich ilość. Zdaję sobie sprawę z ograniczonych w systemie zasobów.

cat /proc/sys/kernel/threads-max
38659

Aplikacja wykrzacza się już przy 1000 wątków - przy czym jest to końcowa wartość uruchomionych wątków. Równolegle nigdy tyle nie działało, gdyż bardzo szybko następuje ich kończenie. W przytoczonym przykładzie dla QSIZE=1000 funkcja pthread_create() zawsze zwracała status 0 - mimo to uzyskiwałem naruszenie ochrony pamięci.
W rzeczywistej aplikacji sztucznie ograniczam liczbę wątków poprzez zapisanie zadań w puli i stopniowe jej przetwarzanie. Do tego ustawiam odpowiednio pthread_attr_setstacksize() na atrybucie wykorzystywanym przy pthread_create() (starsze wersje musl-libc rezerwowały znacznie mniej pamięci na stos wątku niż glibc i faktycznie generowało to podobne problemy). Podczas inicjalizacji aplikacji ustawiane też są odpowiednio limity przy pomocy setrlimit(). Te, które można na RLIM_INFINITY, pozostałe na wielokrotność domyślnej wartości. System docelowy ma odpowiednio poustawiane główne parametry w /proc/sys/*. Dla testowego przykładu też ustawiałem przytoczone limity zgodnie z opisem, ale nie wyeliminowało to problemu.
Poza tym, czy pthread_create() nie powinien zwracać statusu błędu w przypadku braku zasobów na utworzenie kolejnego wątku (EAGAIN)? Podczas tworzenia wątku bez flagi PTHREAD_CREATE_DETACHED dokładnie tak to działa. Wywołanie pthread_join() powoduje zwolnienie zasobów ukończonego wątku i możliwość tworzenia kolejnego.
Mutex ma zapewnić prawidłową modyfikację zmiennej w wątku przy jednoczesnym zapewnieniu, że w procesie głównym podczas sprawdzania jej wartości nie nastąpi jej zmiana. Dodatkowo mutex zabezpiecza przed agresywną optymalizacją kompilatora (tak jak volatile), co samo zdefiniowanie zmiennej jako volatile przy procesorach wielordzeniowych nie daje zupełnej gwarancji atomowej operacji na volatile. W tym przypadku jest to tylko jedna zmienna - w programie docelowym może być struktura lub więcej zmiennych / operacji. Nie zależy mi na prędkości / wdajności a jedynie na stabilności i pewności że aplikacja będzie działać bez awarii.
Pozostaje jeszcze problem przytoczonych zasobów i limitów systemowych. Jak można oszacować zapotrzebowanie aplikacji / wątku na pamięć stosu? Ile w przytoczonym przykładzie potrzebuje tworzony wątek na stos? (praktycznie brak jakichkolwiek zmiennych lokalnych dla wątku).
Mam nadzieję, że wskażecie błąd, popełniony w przytoczonym przykładzie, który powoduje naruszenie pamięci. Czy aplikacja wywołująć pthread_create() w przypadku braku dostępnych zasobów ma prawo naruszać ochronę pamięci, czy raczej pthread_create() powinno zgłosić błąd EAGAIN?

0

Witam,
wróciłem do zagadnienia z rozmiarem stosu na wątek - jako najbardziej prawdopodobne źródło problemu.
Faktycznie - zmiana stosu wpływa na zachowanie aplikacji i eliminację błędu naruszenia pamięci. Wcześniej tego nie zauważyłem,
ponieważ próbowałem zwiekszyć limit rozmiaru stosu od domyślnej wartości - a rozwiązaniem dla tego przykładu jest jego ograniczenie.
Sprawdzę jeszcze w freebsd jaki deklarują domyślny rozmiar stosu i sprawdzę wszystkie przetestowane warianty dla tej wartości.
Brakuje mi jednak informacji, jak dobierać wartość dla rozmiaru stosu. Czy przed każdym wywołanie pthread_cread() testować jak się zachowa pthread_attr_setstacksize()? Co, jeśli nie zaakceptuje proponowanego rozmiaru stosu?
Dla przykładu:
rozmiar stosu ustawiony na 32768 nie generuje naruszenia ochrony pamięci, wartość 8388608 już tak. Przy czym pthread_attr_setstacksize() w żadnym przypadku nie zwrócił błędu, podobnie pthread_create(). Co ciekawe, dla niektórych testów
konieczne było zwiększenie rozmiaru stosu względem wartości domyślnej, dla innych jego ograniczenie.
Być może jest to jednak zbieg okoliczności i prawdziwa przyczyna leży jeszcze gdzieś indziej?

0

Do tego co napisałem dodaj do wnętrza wątku wyświetlanie zmiennej active.

Do tego:

                        do      {
                                pthread_t ret; <---- Tutaj jest błąd
                                status = pthread_create(&ret, &attr, &threadapp, NULL);
                        }
                        while(status != 0);
 

Przepełniasz pamięć. Zadeklaruj ret jako globalne to "podziała" trochę dłużej. Chcesz wiedzieć więcej? Uruchom to w strace.
Chcesz zobaczyć jaka linijka powoduje błąd?
gcc -O0 -g3 -pthread -Wall -pedantic main.c
i potem
valgrind ./a.out

0
Płynąca Woda napisał(a):

Do tego co napisałem dodaj do wnętrza wątku wyświetlanie zmiennej active.

Do tego:

                        do      {
                                pthread_t ret; <---- Tutaj jest błąd
                                status = pthread_create(&ret, &attr, &threadapp, NULL);
                        }
                        while(status != 0);
 

Przepełniasz pamięć. Zadeklaruj ret jako globalne to "podziała" trochę dłużej. Chcesz wiedzieć więcej? Uruchom to w strace.
Chcesz zobaczyć jaka linijka powoduje błąd?
gcc -O0 -g3 -pthread -Wall -pedantic main.c
i potem
valgrind ./a.out

ok,
z ret w postaci globalnej tablicy (o ilości elementów równej liczbie wątków) testy już wykonywałem. Podobnie z attr[] - dla każdego wątku osobna zmienna globalna. Nadal aplikacja wywalała się. Nie wywoływałem jej w pętli nieskończonej, a jedynie jeden obieg uruchomienia zadanej liczby wątków.
Zmienną active też wyświetlałem (maksymalna uzyskana wartość to około 200).
Błąd wykonuje pthread_create() - tylko dlaczego? Przeniesienie ret do tablicy globalnej ret[QSIZE] nie rozwiązuje problemu.
Pobawię się valgrindem tak jak sugerujesz - ale czy to pomoże - nie wiem.
Z tego co zaobserwowałem: ograniczenie rozmiaru stosu dla wątku rozwiązało problem naruszenia ochrony pamięci - nadal nie widzę tu sensu i
przczyny, dlaczego pthread_create() nie zwraca błędu o braku zasobów dla kolejnego wątku.
Pod freebsd domyślny stos wątku ustawiony jest na 2097152 i dla tego parametru program testowy działa bez przerwy.
Testowałem też inne wartości z zakresu od 4096 do 409600000 i również aplikacja działała stabilnie. Dopiero dla wartości 2048 pojawiło się
naruszenie ochrony pamięci. (pod freebsd).

Możesz wyjaśnić, dlaczego w zapisie pthread_t ret; jest błąd?

0

Przetestowałem aplikację valgrindem w 2 wariantach:
Pierwszy test z domyślnym rozmiarem stosu dla wątku (64 wątki)

#include <unistd.h>
#include <stdio.h>
#include <pthread.h>

#define QSIZE 64
pthread_mutex_t lock;
int active = QSIZE;

static void *threadapp(void *o)
{
        pthread_mutex_lock(&lock);
        active--;
        pthread_mutex_unlock(&lock);
        pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
        int i;
        pthread_attr_t attr;
        pthread_mutex_init(&lock, NULL);
        if(pthread_attr_init(&attr) != 0)       {
                perror("attr init");
                return 1;
        }
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        /*pthread_attr_setstacksize(&attr, 32768);*/
        for(i=0;i<1;++i)        {
                int j;
                printf("%d: Start", i);
                fflush(stdout);
                active = QSIZE;
                for(j=0; j<QSIZE; ++j)  {
                        int status;
                        do      {
                                pthread_t ret;
                                status = pthread_create(&ret, &attr, &threadapp, NULL);
                        }
                        while(status != 0);
                }
                for(;;) {
                        pthread_mutex_lock(&lock);
                        if(active == 0) {
                                pthread_mutex_unlock(&lock);
                                break;
                        }
                        pthread_mutex_unlock(&lock);
                }
                printf("...Done\n");
        }
        if(pthread_attr_destroy(&attr) != 0)    {
                perror("attr destroy");
                return 1;
        }
        return 0;
}

Daje wynik:
==24771== Memcheck, a memory error detector
==24771== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==24771== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==24771== Command: ./multitest
==24771==
0: Start==24771== Invalid read of size 1
==24771== at 0x4E3EC1E: pthread_create@@GLIBC_2.2.5 (in /usr/lib/libpthread-2.24.so)
==24771== by 0x4009DD: main (multitest.c:37)
==24771== Address 0x97f9d13 is not stack'd, malloc'd or (recently) free'd
==24771==
==24771==
==24771== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==24771== Access not within mapped region at address 0x97F9D13
==24771== at 0x4E3EC1E: pthread_create@@GLIBC_2.2.5 (in /usr/lib/libpthread-2.24.so)
==24771== by 0x4009DD: main (multitest.c:37)
==24771== If you believe this happened as a result of a stack
==24771== overflow in your program's main thread (unlikely but
==24771== possible), you can try to increase the size of the
==24771== main thread stack using the --main-stacksize= flag.
==24771== The main thread stack size used in this run was 8388608.
==24771==
==24771== HEAP SUMMARY:
==24771== in use at exit: 1,588 bytes in 4 blocks
==24771== total heap usage: 27 allocs, 23 frees, 8,380 bytes allocated
==24771==
==24771== LEAK SUMMARY:
==24771== definitely lost: 0 bytes in 0 blocks
==24771== indirectly lost: 0 bytes in 0 blocks
==24771== possibly lost: 0 bytes in 0 blocks
==24771== still reachable: 1,588 bytes in 4 blocks
==24771== suppressed: 0 bytes in 0 blocks
==24771== Rerun with --leak-check=full to see details of leaked memory
==24771==
==24771== For counts of detected and suppressed errors, rerun with: -v
==24771== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
24771 Naruszenie ochrony pamięci (zrzut pamięci) valgrind ./multitest

Dla drugiego wariantu (z ograniczonym rozmiarem stosu dla wątku do 32768) z dodaną funkcją:

        pthread_attr_setstacksize(&attr, 32768);

==26297== Memcheck, a memory error detector
==26297== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==26297== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==26297== Command: ./multitest
==26297==
0: Start...Done
==26297==
==26297== HEAP SUMMARY:
==26297== in use at exit: 1,588 bytes in 4 blocks
==26297== total heap usage: 13 allocs, 9 frees, 4,572 bytes allocated
==26297==
==26297== LEAK SUMMARY:
==26297== definitely lost: 0 bytes in 0 blocks
==26297== indirectly lost: 0 bytes in 0 blocks
==26297== possibly lost: 0 bytes in 0 blocks
==26297== still reachable: 1,588 bytes in 4 blocks
==26297== suppressed: 0 bytes in 0 blocks
==26297== Rerun with --leak-check=full to see details of leaked memory
==26297==
==26297== For counts of detected and suppressed errors, rerun with: -v
==26297== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

0
jogi napisał(a):

Możesz wyjaśnić, dlaczego w zapisie pthread_t ret; jest błąd?

Źle zrozumiałeś. ret jest zmienną istniejącą na stosie, do tego jej widoczność jest ograniczona do zasięgu jednego obrotu "do while". Ty przekazujesz adres tej zmiennej do pthread_create. Przecież pthread_create może użyć tej zmiennej juz w kontekście nowego wątku. Ponieważ ta zmienna istnieje na stosie a jej widoczność jest ograniczona do obrotu pętli to kompilator może tak ułożyć kod, że pod tym adresem będzie już coś innego (Najprawdopodobniej jednak będzie do dalej ret wykorzystany jednak dla następnego obrotu pętli, co i tak jest błędem, bo każdy wątek w takim przypadku może pisać pod ten sam adres).

0
nalik napisał(a):
jogi napisał(a):

Możesz wyjaśnić, dlaczego w zapisie pthread_t ret; jest błąd?

Źle zrozumiałeś. ret jest zmienną istniejącą na stosie, do tego jej widoczność jest ograniczona do zasięgu jednego obrotu "do while". Ty przekazujesz adres tej zmiennej do pthread_create. Przecież pthread_create może użyć tej zmiennej juz w kontekście nowego wątku. Ponieważ ta zmienna istnieje na stosie a jej widoczność jest ograniczona do obrotu pętli to kompilator może tak ułożyć kod, że pod tym adresem będzie już coś innego (Najprawdopodobniej jednak będzie do dalej ret wykorzystany jednak dla następnego obrotu pętli, co i tak jest błędem, bo każdy wątek w takim przypadku może pisać pod ten sam adres).

Ok, to jest jasne. Myślałem, że przydziałem pamięci dla wątku zajmuje się system (nie kompilator), a zmienna ret w tym przypadku
jest tylko na moje potrzeby - gdbym chciał odczytać wynik działania wątku. W chwili, kiedy wątek typu DETACHED kończy działanie, zasoby zajęte przez pthread_t są zwalniane. Oczywiście byłem w błędzie - w tym przypadku ja podaje wskaźnik - a pthread_create()
z niego korzysta.
W przpadku qnx dozwolone było przekazanie NULL zamiast wskaźnika na pthread_t - stąd mój błąd.
Zmodyfikowałem program i ret[QSIZE] zadeklarowałem jako zmienną globalną. Wykorzystuję też ją tylko dla jednego obiegu pętli by ponownie nie wykorzystywać tych samych elementów tablicy. Program nadal narusza ochronę pamięci przy QSIZE w okolicy 100.
Dla niższych wartości testy jeszcze trwają.

0

Pamięć przydziela system na skutek syscalla sbrk albo mmap (po stronie aplikacji opakowanego jeszcze w allocator, tj malloca). Ale instrukcje wykorzystania pamięci i alokacji - mówiąc w skrócie - generuje kompilator. Stos głównego wątku zostanie zaalokowany podczas uruchomiania, z tego co pamiętam przez program ładujący (ld, ale mogę się mylić). Ale to jak będzie wykorzystyway zależy od tego co wygenerował kompilator.

To raczej nie jest powód segfaulta, bo na szczęście pod ten adres jest wpisany jedynie adres prawdziwej struktury ( https://fossies.org/dox/glibc-2.24/pthread__create_8c_source.html ).

0
nalik napisał(a):

Pamięć przydziela system na skutek syscalla sbrk albo mmap (po stronie aplikacji opakowanego jeszcze w allocator, tj malloca). Ale instrukcje wykorzystania pamięci i alokacji - mówiąc w skrócie - generuje kompilator. Stos głównego wątku zostanie zaalokowany podczas uruchomiania, z tego co pamiętam przez program ładujący (ld, ale mogę się mylić). Ale to jak będzie wykorzystyway zależy od tego co wygenerował kompilator.

To raczej nie jest powód segfaulta, bo na szczęście pod ten adres jest wpisany jedynie adres prawdziwej struktury ( https://fossies.org/dox/glibc-2.24/pthread__create_8c_source.html ).

To co w takim razie jest przyczną segfaulta? Wykonałem testy z attr w tablicy globalnej (podobnie jak ret) i nadal brak poprawy.
Testowane dla 100 wątków.
Kod programu:

#include <unistd.h>
#include <stdio.h>
#include <pthread.h>

#define QSIZE 100
pthread_mutex_t lock;
int active = QSIZE;
pthread_t ptr[QSIZE];
pthread_attr_t attr[QSIZE];

static void *threadapp(void *o)
{
        pthread_mutex_lock(&lock);
        active--;
        pthread_mutex_unlock(&lock);
        pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
        int j;
        if(pthread_mutex_init(&lock, NULL) != 0)        {
                perror("mutex init");
                return 1;
        }
        printf("Start");
        fflush(stdout);
        active = QSIZE;
        for(j=0; j<QSIZE; ++j)  {
                int status;
                do      {
                        if(pthread_attr_init(&attr[j]) != 0)    {
                                perror("attr init");
                                return 1;
                        }
                        pthread_attr_setdetachstate(&attr[j], PTHREAD_CREATE_DETACHED);
                        //pthread_attr_setstacksize(&attr[j], 32768);
                        status = pthread_create(&ptr[j], &attr[j], &threadapp, NULL);
                        if(pthread_attr_destroy(&attr[j]) != 0) {
                                perror("attr destroy");
                                return 1;
                        }
                }
                while(status != 0);
        }
        for(;;) {
                pthread_mutex_lock(&lock);
                if(active == 0) {
                        pthread_mutex_unlock(&lock);
                        break;
                }
                pthread_mutex_unlock(&lock);
        }
        printf("...Done\n");
        return 0;
}

0

SIGSEGV występuje gdy próbujesz skorzystać z pamięci, do której nie masz dostępu. Przykładowo zrobić dereferencję (odczyt, zapis) adresu który nie jest w zakresie przydzielonym dla danego procesu (np. NULL, virtualny adres 0x0 znajduje się w pierwszej stronie przydzielonej pamięci, która zwyczajowo jest chroniona/niezalakowana).

Nie wiem co jest przyczyną w twoim przypadku, bo dopaliłem to na linuxie i osx i nie wywaliło mi się. Może spróbuj odpalić z gdb albo załadować cora.

0
nalik napisał(a):

SIGSEGV występuje gdy próbujesz skorzystać z pamięci, do której nie masz dostępu. Przykładowo zrobić dereferencję (odczyt, zapis) adresu który nie jest w zakresie przydzielonym dla danego procesu (np. NULL, virtualny adres 0x0 znajduje się w pierwszej stronie przydzielonej pamięci, która zwyczajowo jest chroniona/niezalakowana).

Nie wiem co jest przyczyną w twoim przypadku, bo dopaliłem to na linuxie i osx i nie wywaliło mi się. Może spróbuj odpalić z gdb albo załadować cora.

Zmieniłem ilość wątków ze 100 na 64. Po jakimś czasie działania cały system się powiesił (mysz, klawiatura - laptop na nic nie reagował). Być może to przypadek - a może coś nie tak z kernelem. Nie każde uruchomienie programu generuje segfault. Testową aplikację odpalam w pętli konsoli do momentu wywalenia:
while ./multitest; do sleep 0; done
Przy QSIZE 100 lub 64 segfault mam dopiero po jakimś czasie działania pętli.
Testowałem na kernelach: lts (4.4.34), 4,8.10 oraz 4.9-rc6

Testowałem też przy pomoc gdb - program się nie wykładał. Testowanie core wskazało na pthread_create() - dalej nie analizowałem.
Ponieważ ten sam problem dotyczy testów z glibc jak i musl-libc, jedyne części wspólne to kernel i sam program.
We freebsd jedyny segfault jaki się pojawił dotyczył sytuacji redukcji stosu wątku do 2048 bajtów. Zwiększenie rozmiaru stosu nie powowdowało pojawienia się segfault. W systemie linux: zwiększenie rozmiaru stosu wątku przśpieszało pojawienie się segfault, redukcja oddalała (ale nie wyelimowała całkowicie).
Możesz jeszcze uruchomić test w pętli i zobaczyć czy w ciągu kilku - kilkunastu minut się nie wywali?

0

Juz to zrobiłem i rzeczywście się wywaliło. W __pthread_create_2_1. wskaźnik na pthread jest lewy.

0

Zmieniłem ilość wątków ze 100 na 64. Po jakimś czasie działania cały system się powiesił (mysz, klawiatura - laptop na nic nie reagował).

Brzmi jak lokalny DOS. Sprawdź jeszcze na innej maszynie. Ja testowałem to u siebie (FreeBSD, Fedora) i nie dostałem żadnego sigsegv. Może to być też jakiś problem Twojego sprzętu.

Edit:
Wycinka z manuala do pthread_attr_setstacksize:

EINVAL The stack size is less than PTHREAD_STACK_MIN (16384) bytes.

W BSD przy krótkim stosie też jest SIGSEGV.

1

https://sourceware.org/bugzilla/show_bug.cgi?id=20116

Dokładnie ten bug, linijka "(pd->stopped_start)", pd wskazuje w kosmos.

(gdb) bt
#0  __pthread_create_2_1 (newthread=<optimized out>, attr=<optimized out>, start_routine=<optimized out>, arg=<optimized out>) at pthread_create.c:713
#1  0x0000000000400b2c in main (argc=2, argv=0xfff000538) at pthread.c:42
(gdb) p pd
$1 = (struct pthread *) 0xaa3f700
(gdb) p *pd
Cannot access memory at address 0xaa3f700
(gdb) list pthread_create.c:713
708	      if (retval == ENOMEM)
709		retval = EAGAIN;
710	    }
711	  else
712	    {
713	      if (pd->stopped_start)
714		/* The thread blocked on this lock either because we're doing TD_CREATE
715		   event reporting, or for some other reason that create_thread chose.
716		   Now let it run free.  */
717		lll_unlock (pd->lock, LLL_PRIVATE);
0

Testowałem też na maszynie wirtualnej na innym serwerze - podobne rezultaty (z tym, że tam zainstalowan jest Alpinelinux z musl-libc zamiast glibc) Inna też liczba wątków wywalała aplikację (przy 100 odnotowałem problem, przy 64 nie doczekałem się) aktualnie testuję na wirtualizacji wariant z QSIZE 64 i cierpliwie poczekam czy do jutra test zostanie przerwany przez segfault).

0
nalik napisał(a):

https://sourceware.org/bugzilla/show_bug.cgi?id=20116

Dokładnie ten bug, linijka "(pd->stopped_start)", pd wskazuje w kosmos.

Dzięki za linka.
Na swoim laptopie mam glibc 2.24 - wskazany błąd poprawili na 2.22. Czy 2.24 też ma powyższy błąd? Co ze starszymi wersjami glibc?

Inna sprawa - na systemie bez glibc - ale z musl-libc też pthread_create() się wykładał. Dodatkowo, jeśli w wątku
dodałem nawiązanie połączenie z bazą danych i przerwanie połączenia bez dodatkowych zapytań, to w efekcie doszło
do zatrzymania pracy samej bazy danych (mariadb-10.1.19). Z tym, że ostatnio tyle zmian wprowadzałem w testach, że nie mam
pewności czy z musl-libc jeszcze inny problem nie powodował błędu. Powtórzę testy i sprawdzę łatkę z glibc-2.22 na 2.24.

logi z segfault na musl-libc

[162239.236786] multitest[13866]: segfault at 0 ip 00007f12025fee94 sp 00007fff8f037c40 error 6 in ld-musl-x86_64.so.1[7f12025ad000+88000]

[181964.051999] mysqld[11075]: segfault at 7f1873e04afc ip 00007fdd4a4e5fe9 sp 00007fdd35cb90c0 error 4 in ld-musl-x86_64.so.1[7fdd4a495000+88000]

Na musl-libc aktualnie nie mogę ponownie odtworzyć tych błędów - staram się znaleźć powtarzalne warunki w których będą częste
naruszenia ochrony pamięci. Z tym, że to już temat na inne forum / bugzille.

Czyli podsumowując, proszę jeszcze o potwierdzenie:

  1. Czy mogę użyć lokalną zmienną pthread_t jako argument pthread_create() wątku tyu DETACHED ?
  2. Czy atrybut wątku pthread_attr_t, w przypadku kiedy każdy tworzony wątek korzysta z tych samych atrybutów, może być zmienną globalną, zainicjalizowaną jeden raz na starcie programu i potem używaną przy każdym wywołaniu funkcji pthread_create() ?
  3. Czy w 2-3 wątkach mogę tworzyć kolejne wątki korzystając z tej samej zmiennej globalnej atrybutu pthread_attr_t?

1 użytkowników online, w tym zalogowanych: 0, gości: 1