简体   繁体   中英

cleanup in SIGPIPE

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

One way to deal with this is to ignore SIGPIPE. This means that your writing operation ( write , sendmsg , whatever) will return with an error, and as long as you pay attention to the error returns, you'll know which file descriptor fails - because everything is synchronous.

The following programs have nothing to do with sockets, instead they use named pipes. Just putting up the program here to demonstrate how I went about handling the above issue. They are a pair of programs I wrote up to better understand named pipes. All error conditions are handled. There are two executables which must be compiled separately. Run each executable in two separate terminals on the same PC. The order in which you run them is not important. Whichever is run first, will create the named pipe and the other one when started will notice that its already created (mkfifo error handling). The writer write to the pipe once every second and the reader reads from the pipe whenever read() returns. On the writer side, SIGPIPE is ignored and instead EPIPE is handled. Press 'q' on the keyboard in any of the two terminal windows (reader or writer) and both will quit.

If SIGPIPE is not ignored, what would happen is that when I quit the reader before the writer (by pressing q in reader's terminal) the write() would fail and trigger the SIGPIPE signal to be received by the writer application. The default behaviour of this is to exit the application immediately. This of course is not what I need in the multithreaded application that was going to be using this code. So I ignored the signal and checked for the errno and handled it appropriately.

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

Both the above c files make use of kbhit() to poll for character received from the keyboard. Here is the code for that:

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_ */

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM