SIGPIPE в простой двухпроцессной программе

У меня есть простая настройка для вилки и трубы, которые я использовал раньше. Но на этот раз я получаю SIGPIPE в моем write вызов. Вот код

int fd[2];

int pid;

if (pipe(fd) == -1) {
    perror("pipe init error"); 
    exit(1);
}


// signal(SIGPIPE, SIG_IGN);
if ((pid = fork()) < -1) {
    perror("fork error"); exit(1);
}
// parent
else if (pid > 0) {
    close(fd[0]); 

    write(fd[1], "WHAT", MAXWORD); //SIGPIPE here

    close(fd[1]);
    int status;
    wait(&status);
}

// child
else {
    close(fd[1]);
    // void foo(char *dirname, int in, int out);
    // foo takes a path, reads from fd 'in' and outputs to 'fd' out
    foo("./some/path", fd[0], 1);
    close(fd[0]);
}

Вот функция foo:

void foo(char *dirname, int in, int out){

    int string_length;
    char word[MAXWORD];

    // to get rid of \n
    char* sep;
    sep = malloc(sizeof(char));


    // read from piped stdin until it's closed
    while ((string_length = read(in, word, MAXWORD)) > 0){

        // get rid of \n
        sep = strchr(word, '\n');
        *sep = '\0';

        printf("THe word is: %s\n", word);

    }
}

1 ответ

Решение

Если вы получаете SIGPIPE, когда пишете в канал, это означает, что нет процесса, который может читать из канала: ни текущий процесс (вы закрыли конец чтения канала - это хорошо; вместо этого вы будете заблокированы). мертвых, если вы не закрыли его), ни другой (дочерний) процесс.

Поскольку вы не показали, что функция foo() да, мы не можем больше рассказать вам о том, что не так.


Теперь, когда foo() было добавлено, не понятно в чем дело. Есть проблемы, но большинство не показывают пробки.

  1. аргументация dirname не используется.
  2. аргументация out не используется.
  3. Вы теряете память, выделенную для sep в петле.
  4. Вы не гарантируете, что строка, прочитанная из канала, заканчивается нулем. Это может привести к сбоям, что, в свою очередь, приведет к сбою записи.

Я подозреваю, что пункт 4 является неотложной проблемой; другие - больше вопросов аккуратности.

Отмечу, что в основном коде у вас есть:

write(fd[1], "WHAT", MAXWORD); //SIGPIPE here

Если не MAXWORD либо 4, либо 5, вы на пути проигрыша; Вы должны написать только 4 или 5 символов.

В сочетании с read()... чтение будет пытаться прочитать байты MAXWORD, но может получить меньше. Однако нет никаких признаков того, что записанные данные содержат новую строку, поэтому поиск новой строки во входных данных не будет работать надежно. Однако эта проблема должна проявиться и после успешной записи канала, а не до этого.

Замечу, что переменная int fd_parent_write_word[2]; не используется, и код использует переменную int fd[2] без объявления этого.

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


Этот код SSCCE компилируется чисто и работает нормально:

#include <assert.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

enum { MAXWORD = 5 };

static void foo(int in);

static void he_who_pays_the_piper(int signum)
{
    assert(signum == SIGPIPE);
    const char msg[] = "Received signal SIGPIPE\n";
    write(2, msg, sizeof(msg)-1);
    exit(1);
}

int main(void)
{
    int fd[2];
    int pid;

    if (pipe(fd) == -1) {
        perror("pipe init error"); 
        exit(1);
    }

    signal(SIGPIPE, he_who_pays_the_piper);
    if ((pid = fork()) < -1) {
        perror("fork error"); exit(1);
    }
    else if (pid > 0) {
        close(fd[0]); 
        write(fd[1], "WHAT", MAXWORD); //SIGPIPE here
        close(fd[1]);
        int status;
        pid = wait(&status);
        printf("Got status 0x%04X from %d\n", status, pid);
    }
    else {
        close(fd[1]);
        foo(fd[0]);
        close(fd[0]);
    }
    return 0;
}


static void foo(int in)
{
    int string_length;
    char word[MAXWORD];

    while ((string_length = read(in, word, MAXWORD)) > 0)
        printf("The word is: %.*s\n", string_length, word);
}

Пример вывода:

The word is: WHAT
Got status 0x0000 from 49458

Обратите внимание, что это работает, потому что '\0' в конце строки WHAT записывается в трубу и читается из трубы. Чаще всего вы не пишете строки, включая завершающий '\0',

Другие вопросы по тегам