c /中断系统调用/ fork与线程

我发现了线程实现的问题,这对我来说很奇怪。 也许有些人可以向我解释一下,会很棒。

我正在开发类似代理的程序,一个程序(在不同的机器上运行),它通过eth0接收数据包并通过ath0(无线)发送到另一台完全相同的机器。 实际上我完全不确定是什么导致了我的问题,那是因为我对一切,linux和c编程都是新手。

我开始两个线程,

  • 一个是在eth0上侦听(套接字)传入的数据包并通过ath0(也是套接字)发送出去
  • 另一个线程正在监听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); } } 

更新 :我的主文件,启动线程或进程(fork)

 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; } 

有帮助吗?

更新 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); 

这两个线程在相同的接口上工作,但在不同的端口上。 问题似乎仍然出现在同一个地方(请在第一个代码段中找到它)。 我不能再进一步了解如何解决这个问题。 也许你们中的一些人可以再次帮助我。

提前致谢。

EINTR本身并不表示错误。 这意味着你的进程在sendto系统调用中收到了一个信号,而且系统调用还没有发送任何数据(这很重要)。

在这种情况下你可以重试发送,但好的方法是找出导致中断的信号。 如果这是可重现的,请尝试使用strace

如果你是发送信号的人,那么,你知道该怎么做:-)

请注意,在linux上,即使您没有自己安装处理程序,也可以在sendto (以及其他一些函数)上接收EINTR 。 如果:

  • 进程停止(例如通过SIGSTOP)并重新启动(使用SIGCONT)
  • 你已经在套接字上设置了发送超时(通过SO_SNDTIMEO)
  • 有关更多详细信息,请参见signal(7)手册页(位于最底部)。

    因此,如果您“暂停”您的服务(或其他内容),则需要EINTR并且您应该重新启动呼叫。

    请记住,如果您正在使用带有信号的线程,那么当传送到进程时,给定信号可以传送到信号掩码未阻塞信号的任何线程。 这意味着如果您在一个线程中阻止了传入信号,而在另一个线程中没有阻塞传入信号,则非阻塞线程将接收信号,如果信号没有设置信号处理程序,您将最终使用该信号的默认行为整个过程的信号(即所有线程,信号阻塞线程和非信号阻塞线程)。 例如,如果信号的默认行为是终止进程,那么捕获该信号并执行它的默认行为的一个线程将终止所有线程的整个进程,即使某些线程可能已经屏蔽了该信号。 此外,如果您有两个未阻塞信号的线程,则确定哪个线程将处理该信号。 因此,混合信号和线程通常不是一个好主意,但规则有例外。

    您可以尝试的一件事是,因为生成线程的信号掩码是从生成线程inheritance的,所以要创建一个守护程序线程来处理信号,在程序开始时,您阻止所有传入的信号(或者至少所有信号)不重要的信号),然后产生你的线程。 现在,那些生成的线程将忽略父线程的阻塞信号掩码中的任何传入信号。 如果您需要处理某些特定信号,您仍然可以将这些信号作为主进程的阻塞信号掩码的一部分,然后生成线程。 但是当你产生线程时,将一个线程(甚至可能是它产生所有工作线程后的主进程线程)作为“守护进程”线程,使用sigwait()等待那些特定的传入(现在阻塞)信号。 然后,当该进程接收到给定信号时,该线程将调度所需的任何function。 这样可以避免信号在其他工作线程中中断系统调用,但仍允许您处理信号。

    您的分叉版本可能没有问题的原因是因为如果信号到达一个父进程,它不会传播到任何子进程。 所以,如果可以的话,我会尝试查看终止系统调用的信号,并在线程版本中阻止该信号,如果需要处理它,创建一个守护程序线程来处理该信号到达,其余线程阻止该信号。

    最后,如果您无法访问任何外部库或调试器等,以查看到达的信号,您可以设置一个简单的过程来查看可能到达的信号。 你可以试试这段代码:

     #include  #include  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; }