c / прерванный системный вызов / ответвление против потока - PullRequest
4 голосов
/ 17 мая 2011

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

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

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

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

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

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

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

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

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

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

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

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

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

UPDATE : мой основной файл, запускающий либо потокиили процессы (разветвление)

int main(void) {

port_config pConfig;

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

pid_t retval = fork();

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

/*
wint8 rc1, rc2 = 0;

pthread_t pc2wsu;
pthread_t wsu2pc;

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

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

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

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

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

update 05/30/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(&signal_mask);
sigprocmask(SIG_BLOCK, &signal_mask, NULL);

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

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

Ответы [ 2 ]

6 голосов
/ 17 мая 2011

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

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

Если вы тот, кто посылает сигнал, вы знаете, что делать: -)

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

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

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

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

3 голосов
/ 17 мая 2011

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

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

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

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

#include <signal.h>
#include <stdio.h>

int main()
{
    //block all incoming signals
    sigset_t signal_mask;
    sigfillset(&signal_mask);
    sigprocmask(SIG_BLOCK, &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(&signal_mask, &arrived_signal);

        switch(arrived_signal)
        {
             case SIGABRT: fprintf(stderr, "SIGABRT signal arrived\n"); break;
             case SIGALRM: fprintf(stderr, "SIGALRM signal arrived\n"); break;

             //continue for the rest of the signals defined in signal.h ...

             default: fprintf(stderr, "Unrecognized signal arrived\n");
        }
    }

    //clean-up your threads and anything else needing clean-up

    return 0;
}
...