Что произойдет, если два потока вызовут fork () одновременно - PullRequest
0 голосов
/ 20 июня 2019

У меня есть процесс с несколькими потоками. Я зарегистрировал функцию prepare и родительский обработчик, используя __register_atfork (blocksigprof, restoresigprof, NULL, NULL); функция. Теперь давайте предположим, что два потока одновременно вызывают fork. И у меня есть счетчик приращения в blockigprof и счетчик приращения в restoresigprof.

Принимая во внимание приведенный выше сценарий, всегда ли будут вызываться парный блок и восстановленный код в паре? Есть ли механизм блокировки, который по своей сути сделан в __register_atfork.

#define NUM_THREADS 8
static int go=0;
static int exec = 1;
static int ev_od = 0;

static void *
test_thread (void *arg) {
    int j;
    pid_t c, d;
    while(!go) // Wait, so that all threads are here.
        continue;
    // All will fork, hopefully at same time because of go signal wait.
    while(exec) {
        c = fork();
        if (c < 0) {
            printf("SANJAY: fork() failed.\n");
            exit(1);
        } else if (c == 0) { // Child
            exit(0);
        }
        else { // parent
            d = waitpid(c, NULL, 0); 
        }
    }   
    return NULL;
}

extern int __register_atfork(void (*)(void),void (*)(void),void (*)(void),void *); 
static sigset_t s_new;
static sigset_t s_old;
static int count = 0;
static void blocksigprof(void){
    count++;
#ifdef SYS_gettid
    pid_t tid = syscall(SYS_gettid);
    if (tid % 2) {
        printf("sleep.\n");
        usleep(1);
    }
#else
#error "SYS_gettid unavailable on this system"
#endif
    printf("Pre-fork. Count should be one. %d\n", count);
}
static void restoresigprof(void){
    printf("Post-fork. Count should be one. %d\n", count);
    count--;
}
int
main () {
    pthread_t t[NUM_THREADS];
    void *ptr;
    long size = 500 * 1024 * 1024;
    int i, m;
    volatile int result = 0;
    int g_iters = 100;

    (void) __register_atfork(blocksigprof,restoresigprof,NULL,NULL);
    // Increase size, so fork takes time.
    printf("SANJAY: Increasing process size.\n");
    ptr = malloc(size);
    memset(ptr, 0,  size);
    ptr = malloc(size);
    memset(ptr, 0,  size);
    ptr = malloc(size);
    memset(ptr, 0,  size);
    ptr = malloc(size);
    memset(ptr, 0,  size);
    ptr = malloc(size);
    memset(ptr, 0,  size);
    ptr = malloc(size);
    memset(ptr, 0,  size);
    ptr = malloc(size);
    memset(ptr, 0,  size);
    ptr = malloc(size);
    memset(ptr, 0,  size);
    ptr = malloc(size);
    memset(ptr, 0,  size);
    // Create threads.
    for (i = 0; i < NUM_THREADS; ++i) {
        pthread_create(&t[i], NULL, test_thread, NULL);
    }

    printf("SANJAY: Killing time.\n");
    // Kill time, so that all threads are at same place post it, waiting for go. 100M cycles.
    for (m = 0; m < 1000000; ++m)
        for (i = 0; i < g_iters; ++i )
            result ^= i;
    // Give all threads go at same time.
    printf("SANJAY: Let threads execute.\n");
    go = 1;
    usleep(10000000); // Wait for 10 sec.
    exec = 0;
    // Wait for all threads to finish.
    for (i = 0; i < NUM_THREADS; ++i) {
        pthread_join(t[i], NULL);
    }
    printf("SANJAY: Done.\n");

    return 0;
}

1 Ответ

0 голосов
/ 20 июня 2019
Спецификация

pthread_atfork не требует своей реализации для сериализации вызовов к обработчикам prepare и parent, поэтому можно предположить, что синхронизация отсутствует.

Реализация

glibc блокирует внутренний мьютекс , который не позволяет нескольким потокам входить в обработчики параллельно. Однако это деталь реализации. Комментарии в коде говорят, что такая реализация не совместима с POSIX, потому что POSIX требует, чтобы pthread_atfork был безопасным для асинхронного сигнала, а использование мьютекса делает его не безопасным для асинхронного сигнала.

Чтобы сделать ваш код устойчивым, я рекомендую использовать атомарные или мьютексы для защиты вашего общего состояния от состояния гонки.

...