关闭链接系统中的管道- Linux C问题

wn9m85ua  于 2023-04-29  发布在  Linux
关注(0)|答案(1)|浏览(250)

我一直有一些关于管道连接的问题,我在寻找一些关于这个问题的澄清-下面是我使用的c程序的一个示例。
我有两个演示程序,它们将stdin从文件中取出,并将stdout / stderr放入一系列管道中。然后将这些管道送入两个比较程序,其中一个程序获取两个演示程序的stdout,另一个程序获取两个演示程序的stderr。
就目前情况而言,我不确定在适当地将每个子节点连接起来之后,应该关闭其中的哪些管道。任何指导将不胜感激。

jobStruct job_handler(char* jobLine, char* testProgram, char* filename, int jobNumber, int isQuiet, int counter) {
    jobStruct jobStatus;
    jobStatus.exitStatus = 0;

    int pipeOne[2];
    int pipeTwo[2]; 
    int pipeThree[2];
    int pipeFour[2];

    pipe (pipeOne);
    pipe (pipeTwo);
    pipe (pipeThree);
    pipe (pipeFour);

    pid_t pidList[4];

    for (int i = 0; i < PROGRAM_SIZE; i ++) {
        pid_t status = fork();
        if (!status) {
            if (i == 0) {
                int inputFile = open (filename, O_RDONLY);

                args[0] = "test-program-one";

                for (int i = 0; i < argCount; i ++) {
                    args[i + 1] = jobArgs[i];
                }

                args[argCount + 1] = NULL;

                // Pipes
                dup2(inputFile, STDIN_FILENO);

                dup2(pipeOne[WRITE_END], STDOUT_FILENO);
                dup2(pipeTwo[WRITE_END], STDERR_FILENO);

                // Close pipes here.

                // Exec
                execvp(args[0], args);

                // Job failed to load.
                job_failed(jobNumber);
            } else if (i == 1) {
                int inputFile = open (filename, O_RDONLY);

                args[0] = "test-program-two";

                for (int i = 0; i < argCount; i ++) {
                    args[i + 1] = jobArgs[i];
                }

                args[argCount + 1] = NULL;

                // Pipes
                dup2(inputFile, STDIN_FILENO);

                dup2(pipeThree[WRITE_END], STDOUT_FILENO);
                dup2(pipeFour[WRITE_END], STDERR_FILENO);

                // Close pipes here.
                
                // Exec
                execvp(args[0], args);

                // Job failed to load.
                job_failed(jobNumber);
            } else if (i == 2) {
                dup2(pipeOne[READ_END], 3);
                dup2(pipeThree[READ_END], 4);

                // Close pipes here.

                // Exec

                char* cmpArgs[3];
                char jobArg[14];             

                snprintf(jobArg, 14, "Job %i stdout", jobNumber);

                cmpArgs[0] = "comparison-program";
                cmpArgs[1] = jobArg;
                cmpArgs[2] = NULL;

                execvp("comparison-program", cmpArgs);

                // Job failed to load.
                job_failed(jobNumber);
            } else {
                dup2(pipeTwo[READ_END], 3);
                dup2(pipeFour[READ_END], 4);

                // Redirect to dev/null if isQuiet == 1
                int devNull = open("/dev/null", 0);

                if (isQuiet) {
                    dup2(devNull, STDOUT_FILENO);
                    dup2(devNull, STDERR_FILENO);
                } else {
                    close (devNull);
                }

                // Close pipes here.

                char* cmpArgs[3];
                char jobArg[14];             

                snprintf(jobArg, 14, "Job %i stderr", jobNumber);

                cmpArgs[0] = "comparison-program";
                cmpArgs[1] = jobArg;
                cmpArgs[2] = NULL;

                execvp("comparison-program", cmpArgs);

                // Job failed to load.
                job_failed(jobNumber);
            }
        } else {
            pidList[i] = status;
        }
    }

    ...
    // Further processing
}

我尝试关闭每个子进程的所有非dup 2管道末端,但这不起作用,反而导致程序超时。任何帮助/指导将不胜感激!

toe95027

toe950271#

这个问题可能是由于管道过早关闭,当调用pipe(pipeOne);pipe(pipeTwo);pipe(pipeThree)pipe(pipeFour);碰巧创建了文件描述符3和4,这些文件描述符被child 2和child 3重新分配。子级2和子级3需要避免过早关闭文件描述符3和4。
对于儿童2,更改:

dup2(pipeOne[READ_END], 3);
                dup2(pipeThree[READ_END], 4);

类似于:

if (pipeOne[READ_END] == 4) {
                    pipeOne[READ_END] = dup(pipeOne[READ_END]);
                    // old fd 4 will be closed by a dup2() call below
                }
                if (pipeThree[READ_END] == 3) {
                    pipeThree[READ_END] = dup(pipeThree[READ_END]);
                    // old fd 3 will be closed by a dup2() call below
                }
                dup2(pipeOne[READ_END], 3);
                dup2(pipeThree[READ_END], 4);
                // close old pipe fds except fd 3 and 4
                for (int i = 0; i < 8; i++) {
                    int fd;
                    switch (i) {
                    case 0: fd = pipeOne[0]; break;
                    case 1: fd = pipeOne[1]; break;
                    case 2: fd = pipeTwo[0]; break;
                    case 3: fd = pipeTwo[1]; break;
                    case 4: fd = pipeThree[0]; break;
                    case 5: fd = pipeThree[1]; break;
                    case 6: fd = pipeFour[0]; break;
                    case 7: fd = pipeFour[1]; break;
                    }
                    if (fd != 3 && fd != 4) {
                        close(fd);
                    }
                }

对于孩子3,更改:

dup2(pipeTwo[READ_END], 3);
                dup2(pipeFour[READ_END], 4);

类似于:

if (pipeTwo[READ_END] == 4) {
                    pipeTwo[READ_END] = dup(pipeTwo[READ_END]);
                    // old fd 4 will be closed by a dup2() call below
                }
                if (pipeFour[READ_END] == 3) {
                    pipeFour[READ_END] = dup(pipeFour[READ_END]);
                    // old fd 3 will be closed by a dup2() call below
                }
                dup2(pipeTwo[READ_END], 3);
                dup2(pipeFour[READ_END], 4);
                // close old pipe fds except fd 3 and 4
                for (int i = 0; i < 8; i++) {
                    int fd;
                    switch (i) {
                    case 0: fd = pipeOne[0]; break;
                    case 1: fd = pipeOne[1]; break;
                    case 2: fd = pipeTwo[0]; break;
                    case 3: fd = pipeTwo[1]; break;
                    case 4: fd = pipeThree[0]; break;
                    case 5: fd = pipeThree[1]; break;
                    case 6: fd = pipeFour[0]; break;
                    case 7: fd = pipeFour[1]; break;
                    }
                    if (fd != 3 && fd != 4) {
                        close(fd);
                    }
                }

dup2调用之后,子级0和1应关闭pipeOnepipeTwopipeThreepipeFour的两端。假设STDOUT_FILENOSTDERR_FILENO在调用pipe()之前已经打开,则不需要进行特殊检查以避免过早关闭管道,而上面的fd 3和4需要进行这种检查。
通过将pipeOnepipeTwopipeThreepipeFour替换为8个文件描述符的一维数组或4×2个文件描述符的二维数组,代码可以变得不那么冗长。

相关问题