首页 > 解决方案 > 通过管锁扫描

问题描述

我有一个练习,我需要通过管道与 C 程序进行交互。

我有以下来源,我无法修改。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int number;
    int answer;

    number = rand() % 100;
    printf("Print the double of the number %d\n", number);
    scanf("%d", &answer);
    if(number * 2 == answer)
        printf("Success\n");
    else
        printf("Error\n");
}

我试图用这段代码与这个程序交互

#include <unistd.h>
#include <stdio.h>

int main(int argc, char **argv, char **env)
{
        int STDIN_PIPE[2];
        int STDOUT_PIPE[2];

        pipe(STDIN_PIPE);
        pipe(STDOUT_PIPE);
        pid_t pid = fork();
        if(pid == 0)
        {
                char *path = "/path/to/binary";
                char *args[2];
                args[0] = path;
                args[1] = NULL;
                close(STDIN_PIPE[1]);
                close(STDOUT_PIPE[0]);
                dup2(STDIN_PIPE[0], STDIN_FILENO);
                dup2(STDOUT_PIPE[1], STDOUT_FILENO);
                execve(path, args, env);
        }
        else
        {
                char buf[128];
                close(STDIN_PIPE[0]);                                                                                                   
                close(STDOUT_PIPE[1]);                                                                                                  
                while(read(STDOUT_PIPE[0], buf, 1))                                                                                         
                    write(1, buf, 1);                                                                                           
        }                                                                                                               
}  

但是当我运行它时,它会陷入无限循环而没有打印任何内容。

标签: c

解决方案


我已经修复了您代码中的一些问题,添加了很多错误检查并完成了它,以便达到最终目标。

在子进程中,srand()必须调用初始化随机数生成器否则你总是得到相同的值。

在子进程中,您必须flush(stdout)在打印问题后才能将其真正写入管道。

最后,scanf()必须检查返回值。

在主流程中,我添加了很多错误检查。我写了一个readLine函数来——猜猜是什么——从管道中读取一行。行由行尾字符 终止\n

还有一些改进的空间......

我使用为 gcc 配置并在 Ubuntu 20.04 下运行的 Visual Studio Code 测试了我的代码。

这是子进程源:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
    int number;
    int answer;
    time_t t;

    srand((unsigned)time(&t));
    number = rand() % 100;
    printf("Print the double of the number %d\n", number);
    fflush(stdout);
    int n = scanf("%d", &answer);
    if (n != 1) {
        printf("Invalid input\n");
        return 1;
    }
    if ((number * 2) == answer) {
        printf("Success\n");
        return 0;
    }
    printf("Error %d is not 2 * %d\n", answer, number);
    return 1;
}

这是主要的流程来源:

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

int readLine(int fd, char *buf, int bufSize);

int main(int argc, char **argv, char **env)
{
        int STDIN_PIPE[2];
        int STDOUT_PIPE[2];

        if (pipe(STDIN_PIPE))
        {
                perror("pipe(STDIN_PIPE)");
                return 1;
        }
        if (pipe(STDOUT_PIPE)) {
                perror("pipe(STDOUT_PIPE)");
                return 1;
        }
        pid_t pid = fork();
        if (pid == 0)
        {
                char *path = "../Child/Child"; // Path to child process, adapt to your environment
                char *args[2];
                args[0] = path;
                args[1] = NULL;
                if (dup2(STDIN_PIPE[0], STDIN_FILENO) == -1) {
                        perror("dup2(STDIN) failed");
                        return 1;
                }
                if (dup2(STDOUT_PIPE[1], STDOUT_FILENO) == -1) {
                        perror("dup2(STDIN) failed");
                        return 1;
                }
                // Close all pipe ends
                close(STDIN_PIPE[0]);    // Close read end of STDIN_PIPE
                close(STDIN_PIPE[1]);    // Write end of STDIN_PIPE
                close(STDOUT_PIPE[0]);   // Read end of STDOUT_PIPE
                close(STDOUT_PIPE[1]);   // Close write end of STDOUT_PIPE

                if (execve(path, args, env) == -1) {
                        perror("execve failed");
                        return 1;
                }
        }
        else
        {
                char buf[128];
                int bufSize = sizeof(buf) / sizeof(buf[0]);
                int i;

                // Read the question asked by child process
                if (readLine(STDOUT_PIPE[0], buf, bufSize) < 0) {
                        printf("readLine failed.\n");
                        return 1;
                }

                // We receive something like "Print the double of the number 83"
                printf("Child process question is \"%s\".\n", buf);

                // Extract the number at end of string
                i = strlen(buf) - 1;
                while ((i >= 0) && isdigit(buf[i]))
                        i--;
                int value = atoi(buf + i + 1);

                // Write our answer to write end of STDIN_PIPE
                char answer[128];
                int answerSize = sizeof(answer) / sizeof(answer[0]);
                int answerLen = snprintf(answer, answerSize, "%d\n", value * 2);

                printf("Our answer is \"%d\".\n", value * 2);

                if (write(STDIN_PIPE[1], answer, answerLen) != answerLen) {
                        printf("write failed.\n");
                        return 1;
                }

                // Read the response (success or failure) sent by child process
                if (readLine(STDOUT_PIPE[0], buf, bufSize) < 0) {
                        printf("readLine failed.\n");
                        return 1;
                }

                if (strcasecmp(buf, "Success") == 0)
                        printf("Child process returned success.\n");
                else
                        printf("Child process returned failure.\n");

                // Close all pipe ends
                close(STDIN_PIPE[0]);    // Close read end of STDIN_PIPE
                close(STDIN_PIPE[1]);    // Write end of STDIN_PIPE
                close(STDOUT_PIPE[0]);   // Read end of STDOUT_PIPE
                close(STDOUT_PIPE[1]);   // Close write end of STDOUT_PIPE
        }

        return 0;
}

// Read a line from file descriptor
// A line is any characters until \n is received or EOF
// \n is not kept
// Return the number of characters read or <0 if error:
//    -1 => Input buffer overflow
//    -2 => read() failed and errno has the error 
int readLine(int fd, char *buf, int bufSize)
{
        int i = 0;
        while (1)
        {
                // Check if enough room in the buffer
                if (i >= bufSize) {
                        printf("Input buffer overflow\n");
                        return -1;
                }
                // Read one character from the pipe
                ssize_t n = read(fd, buf + i, 1);
                if (n == -1)
                {
                        perror("read() failed");
                        return -2;
                }
                if (n == 0)
                {
                        // EOF received, that's OK
                        return i;
                }

                // NUL terminate the buffer
                buf[i + 1] = 0;

                // Check for end of line character
                if (buf[i] == '\n') {
                        buf[i] = 0;     // Remove ending \n
                        return i;
                }
                i++;
        }
}

推荐阅读