Попытка установить связь между двумя процессами с помощью pipe () прерывает работу программы. - PullRequest
0 голосов
/ 24 апреля 2020

Я пытаюсь установить связь между двумя процессами в C, используя канал. Все работает нормально, пока не будет напечатано "привет \ n". Вывод:

(8841) Child here stopping self
(8841) SAYS: 19
DATA WRITED
C: 8
(8841) CONTINUING

Это упрощенная версия программы. Я точно знаю, что часть чтения работает, но кажется, что вызов записи - нет, потому что она никогда не печатает "hi \ n". Есть какие-нибудь подсказки, почему это так?

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>

volatile sig_atomic_t sigchld = 0;

void sigchldHandler(){
    sigchld = 1;
    return;
}

int main(){

    sigset_t mask,prev;
    signal(SIGCHLD, sigchldHandler);
    sigemptyset(&mask);
    sigaddset(&mask, SIGCHLD);

    int pid = fork();

    int fd[2];
    pipe(fd);

    sigprocmask(SIG_BLOCK, &mask, &prev);
    if (pid == 0){
        dup2(STDIN_FILENO,fd[0]);
        printf("(%d) Child here stopping self\n",getpid());
        raise(SIGSTOP);
        printf("(%d) CONTINUING\n",getpid());
        char* hello = malloc(sizeof("hi\n"));
        read(STDIN_FILENO,hello,sizeof("hi\n"));
        printf("%s",hello);
        exit(0);
    }
    sleep(0.1);
    sigprocmask(SIG_SETMASK, &prev,NULL);
    while(1){
        if (sigchld){
            int status;
            int p = waitpid(-1,&status,WNOHANG|WUNTRACED);
            if (WIFSTOPPED(status)){
                if (WSTOPSIG(status) == SIGSTOP){
                    printf("(%d) SAYS: %d\n",p, WSTOPSIG(status));
                    kill(pid,SIGCONT);
                    printf("DATA WRITED\n");
                    char* h = "hi\n";
                    int c=write(fd[1],h,sizeof(h));
                    printf("C: %i\n",c);
                    break;
                }
            }
            sigchld = 0;
        }
    }
} 

1 Ответ

2 голосов
/ 24 апреля 2020

Основная проблема

Ваша ключевая проблема заключается в том, что вы звоните pipe() после того, как вы позвонили fork(). Это означает, что два процесса имеют совершенно разные каналы; они не разговаривают друг с другом.

Вторичные проблемы

Конечно, есть и другие проблемы.

  • У вас есть (в родителе) : int c=write(fd[1],h,sizeof(h));. Вы пишете 8 байтов (ваш вывод включает C: 8, потому что переменная h является указателем размера 8 (вы в 64-битной системе). Однако строка указывает только на 4 байта - вы следует использовать strlen() или около того, чтобы ограничить объем записываемых данных.

  • Вы не закрываете достаточно файловых дескрипторов для удобства.

  • У вас есть аргументы для dup2() в обратном порядке. Это тоже крайне важно.

  • Кажется странным использовать динамическое распределение c только для 4 байтов данных, но это должно работать .

  • Вы должны напечатать PID вместе со значением в hello в дочернем (для согласованности, если ничего больше). Хорошо, что вы делаете это с другой печатью.

  • Вероятно, родитель должен ждать ребенка после l oop (после закрытия трубы).

  • Функция sleep() принимает целое число; вызов sleep(0.1) спит в течение нуля секунд. Для сна менее секунды вам нужно nanosleep() или может быть. usleep() (более старый, больше не является частью POSIX, но широко доступен и прост в использовании).

Вот рабочий код:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

volatile sig_atomic_t sigchld = 0;

static void sigchldHandler(int signum)
{
    sigchld = signum;
}

int main(void)
{
    sigset_t mask, prev;
    signal(SIGCHLD, sigchldHandler);
    sigemptyset(&mask);
    sigaddset(&mask, SIGCHLD);

    int fd[2];
    pipe(fd);

    int pid = fork();

    sigprocmask(SIG_BLOCK, &mask, &prev);

    if (pid == 0)
    {
        /* Child */
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);
        close(fd[1]);
        printf("(%d) Child here stopping self\n", getpid());
        raise(SIGSTOP);
        printf("(%d) CONTINUING\n", getpid());
        char *hello = malloc(sizeof("hi\n"));
        int nbytes = read(STDIN_FILENO, hello, sizeof("hi\n"));
        printf("(%d) received %d bytes: %.*s\n", getpid(), nbytes, nbytes, hello);
        exit(0);
    }

    /* Parent */
    close(fd[0]);
    nanosleep(&(struct timespec){.tv_sec = 0, .tv_nsec = 100000000}, NULL);
    sigprocmask(SIG_SETMASK, &prev, NULL);
    while (1)
    {
        if (sigchld)
        {
            int status;
            int p = waitpid(-1, &status, WNOHANG | WUNTRACED);
            if (WIFSTOPPED(status))
            {
                if (WSTOPSIG(status) == SIGSTOP)
                {
                    printf("(%d) SAYS: %d\n", p, WSTOPSIG(status));
                    kill(pid, SIGCONT);
                    char *h = "hi\n";
                    int c = write(fd[1], h, strlen(h));
                    printf("DATA WRITTEN: %i\n", c);
                    close(fd[1]);
                    break;
                }
            }
            sigchld = 0;
        }
    }
    int corpse;
    int status;
    while ((corpse = wait(&status)) > 0)
        printf("PID %d exited with status 0x%.4X\n", corpse, status);
    return 0;
}

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

(66589) Child here stopping self
(66589) SAYS: 17
DATA WRITTEN: 3
(66589) CONTINUING
(66589) received 3 bytes: hi

PID 66589 exited with status 0x0000

Разница между 17 (на Ma c под управлением macOS Mojave 10.14.6) и 19 (на Linux коробке) является нормальной; POSIX не стандартизирует фактические значения номеров сигналов (хотя сигналы от 1 SIGHUP до 15 SIGTERM одинаковы для всех систем, поскольку они были стандартными в 7-м издании Unix).

...