2010-12-04 41 views
1

我有一个共享多个线程的套接字连接池,一个死连接应该从池中删除,问题是我不知道哪个死从SIGPIPE sighandler,在这种情况下的任何建议?在SIGPIPE清理

回答

5

解决此问题的一种方法是忽略SIGPIPE。这意味着您的写入操作(write,sendmsg,无论什么)都会返回一个错误,并且只要您注意错误返回,就会知道哪个文件描述符失败 - 因为所有内容都是同步的。

+1

正是我所要发布。具体来说,如果你没有忽略它,写入操作会失败,'errno'会被设置为'EPIPE'。 – zwol 2010-12-04 04:53:06

0

以下程序与套接字无关,而是使用命名管道。只是在这里举办的程序,以演示我如何处理上述问题。它们是我为了更好地理解命名管道而编写的一对程序。处理所有错误条件。有两个可执行文件必须单独编译。在同一台PC上的两个独立终端中运行每个可执行文件。您运行它们的顺序并不重要。无论哪个先运行,都会创建命名管道,另一个启动时会注意到它已经创建(mkfifo错误处理)。每当read()返回时,写入者每秒钟都会向管道写入一次,并且读取器从管道中读取数据。在作者方面,SIGPIPE被忽略,而是处理EPIPE。在两个终端窗口(阅读器或书写器)中的任何一个的键盘上按'q',两者都将退出。

如果不忽略SIGPIPE,会发生什么情况是当我在写入器之前退出读取器(通过在读取器终端中按q键)write()将失败并触发写入器应用程序接收的SIGPIPE信号。这种默认行为是立即退出应用程序。这当然不是我将要使用此代码的多线程应用程序所需要的。所以我忽略了信号并检查了errno并正确处理了它。

writer.c:

#include <stdio.h>  //for printf() 
#include <stdlib.h>  //for malloc() 
#include <stdint.h>  //for definitions of uint8_t etc.. 
#include <unistd.h>  //for sleep() 
#include <sys/stat.h> //for S_IRUSR, S_IWUSR, mkfifo() 
#include <fcntl.h>  //for O_WRONLY 
#include <errno.h>  //for errno, perror() 
#include "kbhit.h" 
#include <sys/types.h> 
#include <signal.h>  //for signal() 

/* Named pipe considerations (http://www.unixguide.net/unix/programming/2.10.3.shtml): 
* 
* To use the pipe, you open it like a normal file, and use read() 
* and write() just as though it was a plain pipe. 
* However, the open() of the pipe may block. The following rules apply: 
* 
* If you open for both reading and writing (O_RDWR), then the open will not block. 
* 
* If you open for reading (O_RDONLY), the open will block until 
* another process opens the FIFO for writing, unless O_NONBLOCK is 
* specified, in which case the open succeeds. 
* 
* If you open for writing O_WRONLY, the open will block until 
* another process opens the FIFO for reading, unless O_NONBLOCK is 
* specified, in which case the open fails. 
* 
* When reading and writing the FIFO, the same considerations apply as for 
* regular pipes and sockets, i.e. read() will return EOF when all 
* writers have closed, and write() will raise SIGPIPE when 
* there are no readers. If SIGPIPE is blocked or ignored, the call 
* fails with EPIPE. 
* 
*/ 


static const char file_path[] = "/tmp/anurag"; 
static const char message[] = "Hello from writer!"; 

int main(void) { 
    int ret; 
    int fd=0; 
    char keypress=0; 

    /* 
    * (http://stackoverflow.com/questions/4351989/cleanup-in-sigpipe) 
    * EPIPE is returned if fd is connected to a pipe or socket whose reading end is closed. 
    * When this happens the writing process will also receive a SIGPIPE signal. 
    * (Thus, the write return value is seen only if the program catches, blocks or ignores this signal.) 
    */ 
    signal(SIGPIPE, SIG_IGN); 

    //Create the FIFO (named pipe) 
    ret = mkfifo(file_path, S_IRUSR | S_IWUSR); 

    if(ret == 0) { 
     printf("mkfifo(): Named pipe created.\n"); 
    } else { 
     if ((ret == -1) && (errno == EEXIST)) { 
      perror("mkfifo()"); 
     } else { 
      perror("mkfifo()"); 
     } 
    } 
    printf("Will now begin waiting on open()...\n"); 
    fd = open(file_path, O_WRONLY); 
    if(fd == -1) { 
     perror("open()"); 
    } else if (fd > 0) { 
     printf("open(): Named pipe file descriptor opened for writing.\n"); 
    } 


    while(keypress != 'q') { 
     if (kbhit()) { 
      keypress = getchar(); 
      printf("Exiting...\n"); 
     } else { 
      ret = write(fd, message, sizeof(message)); 
      if(ret > 0) { 
       printf("write(): %d bytes to pipe: %s\n",ret,message); 
      } else if (ret <=0) { 
       if(errno == EPIPE) { 
        printf("write(): got EPIPE, reader closed the pipe, exiting...\n"); 
        break; 
       } else { 
        perror("write()"); 
        break; 
       } 
      } 
     } 
     sleep(1); 
    }; 

    ret = close(fd); 
    if(ret == 0) { 
     printf("close(): Named pipe file descriptor closed.\n"); 
    } else { 
     perror("close()"); 
    } 

    ret = remove(file_path); 
    if(ret == 0) { 
     printf("remove(): Named pipe deleted.\n"); 
    } else { 
     perror("remove()"); 
    } 
    fflush(stdout); 
    fflush(stderr); 
    return EXIT_SUCCESS; 
} 

reader.c:

#include <stdio.h>  //for printf() 
#include <stdlib.h>  //for malloc() 
#include <stdint.h>  //for definitions of uint8_t etc.. 
#include <unistd.h>  //for sleep() 
#include <sys/stat.h> //for S_IRUSR, S_IWUSR, mkfifo() 
#include <fcntl.h>  //for O_WRONLY 
#include <errno.h>  //for errno, perror() 
#include <string.h>  //for memset() 
#include "kbhit.h" 

/* Named pipe considerations (http://www.unixguide.net/unix/programming/2.10.3.shtml): 
* 
* To use the pipe, you open it like a normal file, and use read() 
* and write() just as though it was a plain pipe. 
* However, the open() of the pipe may block. The following rules apply: 
* 
* If you open for both reading and writing (O_RDWR), then the open will not block. 
* 
* If you open for reading (O_RDONLY), the open will block until 
* another process opens the FIFO for writing, unless O_NONBLOCK is 
* specified, in which case the open succeeds. 
* 
* If you open for writing O_WRONLY, the open will block until 
* another process opens the FIFO for reading, unless O_NONBLOCK is 
* specified, in which case the open fails. 
* 
* When reading and writing the FIFO, the same considerations apply as for 
* regular pipes and sockets, i.e. read() will return EOF when all 
* writers have closed, and write() will raise SIGPIPE when 
* there are no readers. If SIGPIPE is blocked or ignored, the call 
* fails with EPIPE. 
* 
*/ 


static const char file_path[] = "/tmp/anurag"; 
static char message[100] = {0}; 

int main(void) { 
    int ret; 
    int fd=0; 
    char keypress=0; 
    //Create the FIFO (named pipe) 
    ret = mkfifo(file_path, S_IRUSR | S_IWUSR); 

    if(ret == 0) { 
     printf("mkfifo(): Named pipe created.\n"); 
    } else { 
     if ((ret == -1) && (errno == EEXIST)) { 
      perror("mkfifo()"); 
     } else { 
      perror("mkfifo()"); 
     } 
    } 
    printf("Will now begin waiting on open()...\n"); 
    fd = open(file_path, O_RDONLY); 
    if(fd == -1) { 
     perror("open()"); 
    } else if (fd > 0) { 
     printf("open(): Named pipe file descriptor opened for reading.\n"); 
    } 


    while(keypress != 'q') { 
     if (kbhit()) { 
      keypress = getchar(); 
      printf("Exiting...\n"); 
     } else { 
      memset(message,0,100); 
      ret = read(fd, message, 100); 
      if(ret > 0) { 
       printf("read(): %d bytes from pipe: %s\n",ret, message); 
      } else if (ret == 0){ 
       printf("read(): got EOF, writer closed the pipe, exiting...\n"); 
       break; 
      } else if (ret < 0){ 
       perror("read()"); 
       break; 
      } 
     } 
     sleep(1); 
    }; 

    ret = close(fd); 
    if(ret == 0) { 
     printf("close(): Named pipe file descriptor closed.\n"); 
    } else { 
     perror("close()"); 
    } 

    ret = remove(file_path); 
    if(ret == 0) { 
     printf("remove(): Named pipe deleted.\n"); 
    } else { 
     perror("remove()"); 
    } 
    fflush(stdout); 
    fflush(stderr); 
    return EXIT_SUCCESS; 
} 

上述两个C文件利用的kbhit()的轮询字符从所接收的键盘。下面是该代码:

kbhit.c:

//Taken from: http://cboard.cprogramming.com/c-programming/63166-kbhit-linux.html 

#include <stdio.h> 
#include <termios.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include "kbhit.h" 

int kbhit(void) { 
    struct termios oldt, newt; 
    int ch; 
    int oldf; 

    tcgetattr(STDIN_FILENO, &oldt); 
    newt = oldt; 
    newt.c_lflag &= ~(ICANON | ECHO); 
    tcsetattr(STDIN_FILENO, TCSANOW, &newt); 
    oldf = fcntl(STDIN_FILENO, F_GETFL, 0); 
    fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); 

    ch = getchar(); 

    tcsetattr(STDIN_FILENO, TCSANOW, &oldt); 
    fcntl(STDIN_FILENO, F_SETFL, oldf); 

    if (ch != EOF) { 
     ungetc(ch, stdin); 
     return 1; 
    } 

    return 0; 
} 

kbhit.h:

#ifndef KBHIT_H_ 
#define KBHIT_H_ 

//Console related variables and functions 
int kbhit(void); 

#endif /* KBHIT_H_ */