2012-12-20 17 views
7

Piszę bibliotekę C, która potrzebuje fork() podczas inicjalizacji. Dlatego chcę potwierdzić(), że kod aplikacji (który jest poza moją kontrolą) wywołuje mój kod inicjujący bibliotekę z jednego kontekstu wątków (aby uniknąć dobrze znanego "threads and fork don't mix" problemu). Po zainicjowaniu mojej biblioteki jest ona bezpieczna dla wątków (i oczekuje się, że kod poziomu aplikacji może tworzyć wątki). Zajmuję się tylko obsługą pthreadów.pthreads: jak uruchomić kod działa w jednym kontekście gwintowanym

Wydaje się niemożliwe, aby policzyć liczbę wątków w bieżącej przestrzeni procesu za pomocą pthreads. Rzeczywiście, nawet googletest implementuje tylko GetThreadCount() w systemie Mac OS i QNX.

Biorąc pod uwagę, że nie mogę policzyć nici, czy jest możliwe, że mogę zamiast tego podać jeden wątek?

Wyjaśnienie: Jeśli to możliwe, chciałbym uniknąć używania "/ proc" (nieprzenośny), dodatkowej zależności bibliotek (jak libproc) i stylu LD_PRELOAD pthread_create wrapperów.

Klasyfikacja nr 2: W moim przypadku użycie wielu procesów jest konieczne, ponieważ pracownicy w mojej bibliotece mają stosunkowo dużą wagę (za pomocą pakietu internetowego) i mogą ulec awarii. Jednak chcę, aby oryginalny proces przetrwał awarie pracowników.

+0

Wiem jej nie przenośny, ale jej początek: W systemie Linux istnieje [/ proc/self/stat] (http: //www.kernel.org/doc/man-pages/online/pages/man5/proc.5.html). – ArjunShankar

+0

Co powiesz na "if (pthread_self()! = NULL) ..."? Niestety, to nie jest przydatne, jeśli chcesz sprawdzić, czy nie ma INNYCH wątków innych niż pierwszy, oczywiście. Przepraszam. Nadal myślę ... –

+1

W sprawie dalszego dochodzenia, nie jestem pewien, czy istnieje prosty sposób rozwiązania tego problemu. Być może po prostu "udokumentuj to wyraźnie i pozwól użytkownikowi cierpieć, jeśli nie czytasz dokumentów?" –

Odpowiedz

2

Można oznaczyć funkcję inicjalizacji biblioteki, która będzie uruchamiana przed aplikacją main(). Na przykład za pomocą GCC,

static void my_lib_init(void) __attribute__((constructor)); 

static void my_lib_init(void) 
{ 
    /* ... */ 
} 

Inną opcją jest użycie posix_spawn() do stołu i wykonać procesy robocze jako oddzielne pliki binarne slave.

edytowane ADD:

Wydaje mi się, że jeśli chcesz, aby ustalić, czy proces został już utworzony (rzeczywiste Kernel-based) nici, trzeba będzie polegać na kodzie OS specyficzne.

W przypadku systemu Linux określenie jest proste i bezpieczne w przypadku innych systemów operacyjnych. Jeżeli nie można określić liczbę wątków używanych przez bieżący proces, funkcja zwróci -1:

#include <unistd.h> 
#include <sys/types.h> 
#include <dirent.h> 
#include <errno.h> 

int count_threads_linux(void) 
{ 
    DIR   *dir; 
    struct dirent *ent; 
    int   count = 0; 

    dir = opendir("/proc/self/task/"); 
    if (!dir) 
     return -1; 

    while (1) { 

     errno = 0; 
     ent = readdir(dir); 
     if (!ent) 
      break; 

     if (ent->d_name[0] != '.') 
      count++; 
    } 

    if (errno) { 
     const int saved_errno = errno; 
     closedir(dir); 
     errno = saved_errno; 
     return -1; 
    } 

    if (closedir(dir)) 
     return -1; 

    return count; 
} 

Istnieją pewne przypadki (jak chroot bez /proc/), kiedy to test zawiedzie nawet w Linuksie, więc -1 zwracana wartość powinna zawsze być traktowana jako nieznana zamiast błąd (chociaż errno będzie wskazywać rzeczywistą przyczynę niepowodzenia).

Patrząc na strony man FreeBSD, zastanawiam się, czy odpowiednie informacje są w ogóle dostępne.

Wreszcie:

Zamiast próbować wykrywania problematyczny przypadek, ja poważnie polecić fork() i exec() (lub posix_spawn()) procesów podrzędnych, używając tylko asynchroniczny sygnał bezpieczne funkcje (patrz man 7 signal) w procesie potomnym (przed exec()), unikając w ten sposób rozwidlenia() - komplikacje wątku. Przed rozwidleniem można nadal tworzyć segmenty pamięci współużytkowanej, pary gniazd itp.(). Jedyną wadą, jaką widzę, jest to, że musisz używać oddzielnych plików binarnych dla pracowników niewolniczych. Co, biorąc pod uwagę twój opis, nie brzmi dla mnie jak wada.

+0

sztuczka init/constructor nie zapobiegnie problemowi @ahochhaus, jeśli biblioteka jest załadowana 'dlopen()' – ydroneaud

+0

@ydroneaud: Myślałem, że powiedzenie "run before the main()" uczyniłoby to oczywistym. To nie tak, że kod może cofnąć się w czasie. –

+0

Czytanie "/ proc" nie jest idealne, ale zgadzam się, że jedynym sposobem liczenia wątków jest specyficzne dla systemu operacyjnego, więc tego typu rozwiązanie jest bliskie najlepszych, jakie możemy uzyskać. Dzięki @Nominal Animal. – ahochhaus

1

Jeśli wyślesz sygnał SIGINFO do procesu kontrolującego tty, proces powinien opisywać status wątków. Z opisu powinno wynikać, czy zostały utworzone wątki.

Być może trzeba będzie utworzyć małe narzędzie, które jest wywoływane przez popen, aby odczytać wyjście z powrotem do biblioteki.

Dodany przykładowy kod Pią 21 grudnia 14:45

Uruchom prosty program, który tworzy pięć wątków. Wątki w zasadzie śpią. Przed wyjściem programu wyślij sygnał SIGINFO, aby uzyskać status wątków.

openbsd> cat a.c 
#include <unistd.h> 
#include <pthread.h> 

#define THREADS 5 

void foo(void); 

int 
main() 
{  
    pthread_t thr[THREADS]; 
    int j; 

    for (j = 0; j < THREADS; j++) { 
     pthread_create(&thr[j], NULL, (void *)foo, NULL); 
    } 

    sleep(200);        

    return(0); 
}    

void 
foo() 
{ 
    sleep(100); 
}    
openbsd> gcc a.c -pthread 
openbsd> a.out & 
[1] 1234 
openbsd> kill -SIGINFO 1234 
0x8bb0e000 sleep_wait 15 -c---W---f 0000   
0x8bb0e800 sleep_wait 15 -c---W---f 0000   
0x8bb0e400 sleep_wait 15 -c---W---f 0000   
0x7cd3d800 sleep_wait 15 -c---W---f 0000   
0x7cd3d400 sleep_wait 15 -c---W---f 0000   
0x7cd3d000 sleep_wait 15 -c---W---f 0000 main 
+1

Czy masz jakieś odniesienia na ten temat? – ydroneaud

+0

"man pthreads" w moim systemie (OpenBSD) ma to. Ma być zgodny z interfejsem wątku POSIX 1003.1c. –

+0

czy istnieje przykład? – ydroneaud

-1

Możesz użyć funkcji pthread_once(), aby upewnić się, że żaden inny wątek nie robi tego samego: w ten sposób nie musisz przejmować się, że wiele wątków wywołuje twoją funkcję inicjalizacji, tylko jedna będzie naprawdę wykonana.

Spraw, aby funkcja publicznej inicjalizacji wykonywała prywatną inicjalizację przez pthread_once().

static pthread_once_t my_initialisation_once = PTHREAD_ONCE_INIT; 

static void my_initialisation(void) 
{ 
    /* do something */ 
} 

int lib_initialisation(void) 
{ 
    pthread_once(&my_initialisation_conce, my_initialisation); 

    return 0; 
} 

Inny przykład można znaleźć here.

Linki

+1

ydroneaud, Dzięki - to ciekawy pomysł. Jednak, jak najlepiej mogę stwierdzić, nie gwarantuje to, że kod aplikacji nie zainicjował już innego wątku (który nigdy nie wejdzie do mojego kodu biblioteki, ale nadal może spowodować zakleszczenie mutex) przed zainicjowaniem biblioteki. – ahochhaus

+0

@ahochhaus, jeśli chcesz również chronić przed użytkownikiem nie wywołującym funkcji init przed inną funkcją, * po prostu * wywołaj funkcję init we wszystkich innych funkcjach: 'pthread_once (& my_initialisation_conce, my_initialisation);' – ydroneaud

+0

@ahochhaus i nadal możesz używać jedna atomowa zmienna globalna ustawiona tylko wtedy, gdy inicjalizacja została całkowicie wykonana – ydroneaud