C fork () и pipe (): процесс зависает при чтении канала? - PullRequest
0 голосов
/ 21 января 2020

Я практикуюсь с fork () и pipe, и у меня возникает вопрос: почему второй дочерний процесс застревает при чтении канала, если я не закрываю первый канал (первый дочерний - второй дочерний) в родительском процессе ?

#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>

void checkArgs(int n){
    if(n!=1){
        perror("Wrong parameters number");
        exit(-1);
    }
}

int isAConsonant(char c){
    char vowels[5]={'a','e','i','o','u'};
    int i=0,toRet=1;
    for(i=0;i<5 && toRet;i++){
        if(c==vowels[i]){
            toRet=0;
        }
    }
    return toRet;
}

int main(int argc,char** argv){
    checkArgs(argc-1);
    int pipe1[2],pipe2[2],sync[2];
    int pid1,pid2;

    if(pipe(pipe1)<0 || pipe(pipe2)<0 || pipe(sync)<0){
        perror("Error opening pipes");
        exit(-1);
    }

    if((pid1=fork())<0){
        perror("error during fork");
        exit(-1);
    }

    if(pid1==0){ //first child
        close(pipe1[0]);
        close(sync[0]);
        char buf[3];
        int fDes=open(argv[1],O_RDONLY);

        if(fDes<0){
            perror("Error opening file");
            exit(-1);
        }

        write(sync[1],"N",1);

        while(read(fDes,buf,3)==3){
            write(sync[1],"N",1);
            write(pipe1[1],buf,3);
        }

        write(sync[1],"S",1);
        close(sync[1]);
        close(fDes);
        close(pipe1[1]);
    }
    else{

        if((pid2=fork())<0){
            perror("error during fork");
            exit(-1);
        }
        if(pid2==0){ //second child
            close(pipe2[0]);
            close(pipe1[1]);
            char buf[3];

            while(read(pipe1[0],buf,3)==3){
                if(isAConsonant(buf[0])){
                    write(pipe2[1],buf,3);
                }
            }
            close(pipe2[1]);
            close(pipe1[0]);
        }
        else{   //parent
            close(pipe2[1]);
            close(sync[1]);

            //it does not work if not executed
            //close(pipe1[1]);
            //close(pipe1[0]);

            char toStart;
            read(sync[0],&toStart,1);
            while(toStart!='S'){
                read(sync[0],&toStart,1);
            }

            int fDes=open(argv[1],O_RDWR|O_APPEND,S_IRUSR|S_IWUSR);
            if(fDes<0){
                perror("Error opening file");
                exit(-1);
            }

            char buf[3];

            while(read(pipe2[0],buf,3)==3){
                write(fDes,buf,3);
                write(fDes," ",1);
            }

            close(pipe2[0]);
            close(sync[0]);
            close(fDes);
        }
    }
}

Эти вызовы корректно завершают программу

close(pipe1[1]);
close(pipe1[0]);

файл inputF:

abcdefghilmnopqrstuvz

Экран Strace для выполнения кода без закрытия (pipe1 [ 0]) и закройте (pipe1 [1]) в родительском процессе:

strace для родительского процесса

execve("./pipe", ["./pipe", "inputF"], 0x7ffc458bc820 /* 54 vars */) = 0
brk(NULL)                               = 0x55ed3c675000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (File o directory non esistente)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=71886, ...}) = 0
mmap(NULL, 71886, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f7d0b895000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200l\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2000480, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7d0b893000
mmap(NULL, 2008696, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f7d0b6a8000
mmap(0x7f7d0b6cd000, 1519616, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7f7d0b6cd000
mmap(0x7f7d0b840000, 299008, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x198000) = 0x7f7d0b840000
mmap(0x7f7d0b889000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e0000) = 0x7f7d0b889000
mmap(0x7f7d0b88f000, 13944, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f7d0b88f000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7f7d0b894500) = 0
mprotect(0x7f7d0b889000, 12288, PROT_READ) = 0
mprotect(0x55ed3a7d0000, 4096, PROT_READ) = 0
mprotect(0x7f7d0b8d1000, 4096, PROT_READ) = 0
munmap(0x7f7d0b895000, 71886)           = 0
pipe([3, 4])                            = 0
pipe([5, 6])                            = 0
pipe([7, 8])                            = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f7d0b8947d0) = 11194
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f7d0b8947d0) = 11195
close(6)                                = 0
close(8)                                = 0
read(7, "N", 1)                         = 1
read(7, "N", 1)                         = 1
read(7, "N", 1)                         = 1
read(7, "N", 1)                         = 1
read(7, "N", 1)                         = 1
read(7, "N", 1)                         = 1
read(7, "N", 1)                         = 1
read(7, "N", 1)                         = 1
read(7, "S", 1)                         = 1
openat(AT_FDCWD, "inputF", O_RDWR|O_APPEND) = 6
read(5, "def", 3)                       = 3
write(6, "def", 3)                      = 3
write(6, " ", 1)                        = 1
read(5, "ghi", 3)                       = 3
write(6, "ghi", 3)                      = 3
write(6, " ", 1)                        = 1
read(5, "lmn", 3)                       = 3
write(6, "lmn", 3)                      = 3
write(6, " ", 1)                        = 1
read(5, "rst", 3)                       = 3
write(6, "rst", 3)                      = 3
write(6, " ", 1)                        = 1
read(5, 0x7ffffab4a6a5, 3)              = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=11194, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
read(5, 0x7ffffab4a6a5, 3)              = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
+++ killed by SIGINT +++

strace для первого дочернего процесса

execve("./pipe", ["./pipe", "inputF"], 0x7ffc458bc820 /* 54 vars */) = 0
brk(NULL)                               = 0x55ed3c675000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (File o directory non esistente)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=71886, ...}) = 0
mmap(NULL, 71886, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f7d0b895000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200l\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2000480, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7d0b893000
mmap(NULL, 2008696, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f7d0b6a8000
mmap(0x7f7d0b6cd000, 1519616, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7f7d0b6cd000
mmap(0x7f7d0b840000, 299008, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x198000) = 0x7f7d0b840000
mmap(0x7f7d0b889000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e0000) = 0x7f7d0b889000
mmap(0x7f7d0b88f000, 13944, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f7d0b88f000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7f7d0b894500) = 0
mprotect(0x7f7d0b889000, 12288, PROT_READ) = 0
mprotect(0x55ed3a7d0000, 4096, PROT_READ) = 0
mprotect(0x7f7d0b8d1000, 4096, PROT_READ) = 0
munmap(0x7f7d0b895000, 71886)           = 0
pipe([3, 4])                            = 0
pipe([5, 6])                            = 0
pipe([7, 8])                            = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f7d0b8947d0) = 11194
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f7d0b8947d0) = 11195
close(6)                                = 0
close(8)                                = 0
read(7, "N", 1)                         = 1
read(7, "N", 1)                         = 1
read(7, "N", 1)                         = 1
read(7, "N", 1)                         = 1
read(7, "N", 1)                         = 1
read(7, "N", 1)                         = 1
read(7, "N", 1)                         = 1
read(7, "N", 1)                         = 1
read(7, "S", 1)                         = 1
openat(AT_FDCWD, "inputF", O_RDWR|O_APPEND) = 6
read(5, "def", 3)                       = 3
write(6, "def", 3)                      = 3
write(6, " ", 1)                        = 1
read(5, "ghi", 3)                       = 3
write(6, "ghi", 3)                      = 3
write(6, " ", 1)                        = 1
read(5, "lmn", 3)                       = 3
write(6, "lmn", 3)                      = 3
write(6, " ", 1)                        = 1
read(5, "rst", 3)                       = 3
write(6, "rst", 3)                      = 3
write(6, " ", 1)                        = 1
read(5, 0x7ffffab4a6a5, 3)              = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=11194, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
read(5, 0x7ffffab4a6a5, 3)              = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
+++ killed by SIGINT +++
pepys@pepys:~/Scrivania/lso/C/pipe/prova strana$ ^C
pepys@pepys:~/Scrivania/lso/C/pipe/prova strana$ ls
es  es.c  inputF  pipe  pipe.c  trace.11193  trace.11194  trace.11195
pepys@pepys:~/Scrivania/lso/C/pipe/prova strana$ cat trace.11194
close(3)                                = 0
close(7)                                = 0
openat(AT_FDCWD, "inputF", O_RDONLY)    = 3
write(8, "N", 1)                        = 1
read(3, "abc", 3)                       = 3
write(8, "N", 1)                        = 1
write(4, "abc", 3)                      = 3
read(3, "def", 3)                       = 3
write(8, "N", 1)                        = 1
write(4, "def", 3)                      = 3
read(3, "ghi", 3)                       = 3
write(8, "N", 1)                        = 1
write(4, "ghi", 3)                      = 3
read(3, "lmn", 3)                       = 3
write(8, "N", 1)                        = 1
write(4, "lmn", 3)                      = 3
read(3, "opq", 3)                       = 3
write(8, "N", 1)                        = 1
write(4, "opq", 3)                      = 3
read(3, "rst", 3)                       = 3
write(8, "N", 1)                        = 1
write(4, "rst", 3)                      = 3
read(3, "uvz", 3)                       = 3
write(8, "N", 1)                        = 1
write(4, "uvz", 3)                      = 3
read(3, "", 3)                          = 0
write(8, "S", 1)                        = 1
close(8)                                = 0
close(3)                                = 0
close(4)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

strace для второго дочернего процесса

close(5)                                = 0
close(4)                                = 0
read(3, "abc", 3)                       = 3
read(3, "def", 3)                       = 3
write(6, "def", 3)                      = 3
read(3, "ghi", 3)                       = 3
write(6, "ghi", 3)                      = 3
read(3, "lmn", 3)                       = 3
write(6, "lmn", 3)                      = 3
read(3, "opq", 3)                       = 3
read(3, "rst", 3)                       = 3
write(6, "rst", 3)                      = 3
read(3, "uvz", 3)                       = 3
read(3, 0x7ffffab4a6a5, 3)              = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
+++ killed by SIGINT +++

Ответы [ 2 ]

2 голосов
/ 21 января 2020

Система не будет сообщать EOF по дескриптору чтения канала, пока существует какой-либо процесс, у которого открыт дескриптор записи канала. Это включает в себя текущий процесс. Вы должны убедиться, что трубы закрыты - много закрывается.


Правило большого пальца : Если вы dup2() один конец трубы в стандарте вход или стандартный вывод, закройте оба исходных дескриптора файла, возвращенных pipe(), как можно скорее. В частности, вы должны закрыть их перед использованием любого из семейства функций exec*().

Правило также применяется, если вы дублируете дескрипторы с помощью либо dup() или fcntl() с F_DUPFD


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

Обратите внимание, что опция O_CLOEXEC для open() и опции FD_CLOEXEC и F_DUPFD_CLOEXEC для fcntl() также могут учитывать это обсуждение.

Если вы используете posix_spawn() и его обширное семейство вспомогательных функций (всего 21 функция), вам нужно будет рассмотреть, как закрывать файловые дескрипторы в порожденном процессе (posix_spawn_file_actions_addclose(), et c .).

Обратите внимание, что использование dup2(a, b) безопаснее, чем использование close(b); dup(a); по ряду причин. Во-первых, если вы хотите, чтобы дескриптор файла был больше, чем обычно, dup2() - единственный разумный способ сделать это. Другое состоит в том, что если a совпадает с b (например, оба 0), то dup2() обрабатывает его правильно (он не закрывается b до дублирования a), тогда как отдельный close() и dup() ужасно терпит неудачу. Это маловероятное, но не невозможное обстоятельство.

1 голос
/ 21 января 2020

почему второй дочерний процесс застревает при чтении канала, если я не закрываю первый канал (первый дочерний - второй дочерний) в родительском процессе?

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

Таким образом, у вас остается три процесса и три канала. Каждый из ваших процессов как конец чтения и записи каждого из каналов.

Конец чтения канала передает сигнал EOF только тогда, когда все записывающие устройства закрыты. Я предположил, что это не так в вашей ситуации.

...