c / прерванный системный вызов / разветвление против потока

#c #sockets #pthreads #fork

#c #сокеты #pthreads #форк

Вопрос:

Я обнаружил проблему с реализацией потока, которая кажется мне странной. Может быть, кто-нибудь из вас сможет мне это объяснить, было бы здорово.

Я работаю над чем-то вроде прокси, программы (запущенной на разных машинах), которая получает пакеты по eth0 и отправляет их через ath0 (беспроводную связь) на другую машину, которая делает точно то же самое. На самом деле я совсем не уверен, что является причиной моей проблемы, это потому, что я новичок во всем, в программировании на Linux и c.

Я запускаю два потока,

  • один прослушивает (сокет) на eth0 входящие пакеты и отправляет их через ath0 (также сокет)
  • а другой поток прослушивает ath0 и отправляет через eth0.

Если я использую потоки, я получаю ошибку, подобную этой:

 sh-2.05b# ./socketex 
Failed to send network header packet.
: Interrupted system call
  

Если я использую fork(), программа работает так, как ожидалось.
Кто-нибудь может объяснить мне такое поведение?

Просто чтобы показать реализацию sender, здесь приведен фрагмент кода:

 while(keep_going) {
    memset(amp;buffer[0], '', sizeof(buffer));

    recvlen = recvfrom(sockfd_in, buffer, BUFLEN, 0, (struct sockaddr *) amp;incoming, amp;ilen);
    if(recvlen < 0) {
        perror("something went wrong / incomingn");
        exit(-1);
    }

    strcpy(msg, buffer);
    buflen = strlen(msg);

    sentlen = ath_sendto(sfd, amp;btpinfo, amp;addrnwh, amp;nwh,  buflen, msg, amp;selpv2, amp;depv);

    if(sentlen == E_ERR) {
        perror("Failed to send network header packet.n");
        exit(-1);
    }
}
  

ОБНОВЛЕНИЕ: мой основной файл, запускающий либо потоки, либо процессы (форк)

 int main(void) {

port_config pConfig;

memset(amp;pConfig, 0, sizeof(pConfig));
pConfig.inPort = 2002;
pConfig.outPort = 2003;

pid_t retval = fork();

if(retval == 0) {
    // child process
    pc2wsuThread((void *) amp;pConfig);
} else if (retval < 0) {
    perror("fork not successfuln");
} else {
    // parent process
    wsu2pcThread((void *) amp;pConfig);
}

/*
wint8 rc1, rc2 = 0;

pthread_t pc2wsu;
pthread_t wsu2pc;

rc1 = pthread_create(amp;pc2wsu, NULL, pc2wsuThread, (void *) amp;pConfig);
rc2 = pthread_create(amp;wsu2pc, NULL, wsu2pcThread, (void *) amp;pConfig);

if(rc1) {
    printf("error: pthread_create() is %dn", rc1);
    return(-1);
}

if(rc2) {
    printf("error: pthread_create() is %dn", rc2);
    return(-1);
}

pthread_join(pc2wsu, NULL);
pthread_join(wsu2pc, NULL);
*/
return 0;
}
  

Помогает ли это?

обновление от 30.05.2011

 -sh-2.05b# ./wsuproxy 192.168.1.100
mgmtsrvc
mgmtsrvc
Failed to send network header packet.
: Interrupted system call
13.254158,75.165482,DATAAAAAAmgmtsrvc
mgmtsrvc
mgmtsrvc
  

Как вы можете видеть выше, прерванный системный вызов по-прежнему выполняется.
Я заблокировал все сигналы следующим образом:

 sigset_t signal_mask;
sigfillset(amp;signal_mask);
sigprocmask(SIG_BLOCK, amp;signal_mask, NULL);
  

Два потока работают на одних и тех же интерфейсах, но на разных портах. Проблема, похоже, все еще возникает в том же месте (пожалуйста, найдите ее в первом фрагменте кода). Я не могу идти дальше и не имею достаточных знаний о том, как решить эту проблему. Может быть, кто-нибудь из вас сможет помочь мне здесь снова.

Заранее спасибо.

Комментарии:

1. Каково определение buffer ? Это локальная переменная?

2. buffer это char buffer[BUFLEN]; и это локальная переменная, я разделил реализацию на два файла, один отвечает за pc -> wlanstation, другой — соответственно. для wlanstation -> пк.

3. Тот же вопрос о msg — и зачем это копировать? Но я думаю, вам нужно опубликовать больше кода.

4. копировать не нужно, это правда.

5. я отправляю через сокет ath0, фактически в то же время второй поток получает именно эти данные, потому что это широковещательная передача, может ли это вызвать такое поведение? два потока обращаются к одному и тому же оборудованию, один отправляет, другой получает?!

Ответ №1:

EINTR сам по себе не указывает на ошибку. Это означает, что ваш процесс получил сигнал, когда он находился в sendto системном вызове, и что системный вызов еще не отправил никаких данных (это важно).

В этом случае вы могли бы повторить попытку отправки, но неплохо было бы выяснить, какой сигнал вызвал прерывание. Если это воспроизводимо, попробуйте использовать strace .

Если вы тот, кто посылает сигнал, что ж, вы знаете, что делать 🙂

Обратите внимание, что в Linux вы можете получать EINTR on sendto (и некоторые другие функции), даже если вы сами не установили обработчик. Это может произойти, если:

  • процесс останавливается (например, с помощью SIGSTOP) и перезапускается (с помощью SIGCONT)
  • вы установили тайм-аут отправки на сокете (через SO_SNDTIMEO)

    Смотрите signal(7) справочную страницу (в самом низу) для получения более подробной информации.

    Итак, если вы «приостанавливаете» свою службу (или что-то еще), это EINTR ожидается, и вам следует перезапустить вызов.

  • Комментарии:

    1. Это должно произойти, только если вы намеренно установили обработчик прерывающего сигнала…

    2. На станциях wlan (где должна выполняться моя программа) это встроенная система Linux, я не могу установить дополнительные пакеты (strace не установлен).

    3. @R..: не обязательно в Linux.

    4. Работают ли станции wlan под управлением ядер Linux 2.4 или 2.6? Если 2.4, смотрите Мой комментарий о библиотеках pthread и сигналах выше.

    5. версия ядра Linux, используемая блоками wlan, равна 2.6.9

    Ответ №2:

    Имейте в виду, если вы используете потоки с сигналами, что данный сигнал при доставке в процесс может быть доставлен в любой поток, сигнальная маска которого не блокирует сигнал. Это означает, что если вы заблокировали входящие сигналы в одном потоке, а не в другом, неблокирующий поток получит сигнал, и если для сигнала не настроен обработчик сигнала, вы получите поведение этого сигнала по умолчанию для всего процесса (т. Е. для всех потоков, как потоков, блокирующих сигнал, так и потоков, не блокирующих сигнал). Например, если поведение сигнала по умолчанию заключалось в завершении процесса, один поток, перехватывающий этот сигнал и выполняющий его поведение по умолчанию, завершит весь процесс для всех потоков, даже если некоторые потоки, возможно, маскировали сигнал. Также, если у вас есть два потока, которые не блокируют сигнал, не определено, какой поток будет обрабатывать сигнал. Поэтому обычно смешивать сигналы и потоки — плохая идея, но из этого правила есть исключения.

    Единственное, что вы можете попробовать, поскольку маска сигнала для созданного потока наследуется от генерирующего потока, — это создать поток-демон для обработки сигналов, где в начале вашей программы вы блокируете все входящие сигналы (или, по крайней мере, все неважные сигналы), а затем создаете свои потоки. Теперь эти порожденные потоки будут игнорировать любые входящие сигналы в маске заблокированного сигнала родительского потока. Если вам нужно обработать некоторые конкретные сигналы, вы все равно можете сделать эти сигналы частью маски заблокированных сигналов для основного процесса, а затем создать свои потоки. Но когда вы создаете потоки, оставьте один поток (может даже быть основным потоком процесса после того, как он породил все рабочие потоки) в качестве «демонического» потока, ожидающего этих конкретных входящих (и теперь заблокированных) сигналов с использованием sigwait() . Затем этот поток будет отправлять любые необходимые функции, когда данный сигнал будет получен процессом. Это позволит избежать сигналов от прерывания системных вызовов в других ваших рабочих потоках, но при этом позволит вам обрабатывать сигналы.

    Причина, по которой в вашей разветвленной версии может не возникать проблем, заключается в том, что если сигнал поступает в один родительский процесс, он не распространяется ни на какие дочерние процессы. Итак, я бы попытался, если вы можете, посмотреть, какой сигнал завершает ваш системный вызов, и в вашей многопоточной версии заблокируйте этот сигнал, и если вам нужно его обработать, создайте поток-демон, который будет обрабатывать поступление этого сигнала, а остальные потоки будут блокировать этот сигнал.

    Наконец, если у вас нет доступа к каким-либо внешним библиотекам или отладчикам и т.д. чтобы увидеть, какие сигналы поступают, вы можете настроить простую процедуру для просмотра того, какие сигналы могут поступать. Вы можете попробовать этот код:

     #include <signal.h>
    #include <stdio.h>
    
    int main()
    {
        //block all incoming signals
        sigset_t signal_mask;
        sigfillset(amp;signal_mask);
        sigprocmask(SIG_BLOCK, amp;signal_mask, NULL);
    
        //... spawn your threads here ...
    
        //... now wait for signals to arrive and see what comes in ...
    
        int arrived_signal;
    
        while(1) //you can change this condition to whatever to exit the loop
        {
            sigwait(amp;signal_mask, amp;arrived_signal);
    
            switch(arrived_signal)
            {
                 case SIGABRT: fprintf(stderr, "SIGABRT signal arrivedn"); break;
                 case SIGALRM: fprintf(stderr, "SIGALRM signal arrivedn"); break;
    
                 //continue for the rest of the signals defined in signal.h ...
    
                 default: fprintf(stderr, "Unrecognized signal arrivedn");
            }
        }
    
        //clean-up your threads and anything else needing clean-up
    
        return 0;
    }
      

    Комментарии:

    1. Извините, что не отвечал так долго. Я только что попробовал код, но все равно получаю interrupted system call , пожалуйста, смотрите дополнительную информацию вверху.

    2. В дополнение к коду для блокировки всех сигналов, мой ответ включал код для определения того, какие сигналы поступали… смогли ли вы добавить этот код, чтобы увидеть, с какими сигналами вы на самом деле имеете дело?