#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
(и некоторые другие функции), даже если вы сами не установили обработчик. Это может произойти, если:
Смотрите 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. В дополнение к коду для блокировки всех сигналов, мой ответ включал код для определения того, какие сигналы поступали… смогли ли вы добавить этот код, чтобы увидеть, с какими сигналами вы на самом деле имеете дело?