【问题标题】:How to use pipe and dup2 in c如何在c中使用pipe和dup2
【发布时间】:2021-05-13 18:05:15
【问题描述】:

我必须使用 c 中的管道来模拟这个命令: echo "" |公元前-lq。 进程 A 必须读取一个字符串并将其发送给进程 B; 进程B执行“bc -lq”命令并将结果返回给A。

代码是这样的,但我不明白为什么它不起作用;特别是,“bc”命令似乎无法从标准输入读取表达式。

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

#define N 1024

#define LEGGI(stringa)  if(scanf("%s", stringa) == 0)                 \
                        {                                             \
                            perror("Impossibile leggere la stringa"); \
                            exit(EXIT_FAILURE);                       \
                        }                                             \

void closePipe(int pipeFd);
void Dup2(int pipeTempFd, int fd);
void Write(int pipeTempFd, char stringa[], int n);
void Read(int pipeTempFd, char stringa[], int n);

int main()
{

    char stringa[N];
    LEGGI(stringa);

    int pipeFd[2];
    int pRicezioneFd[2];

    if(pipe(pipeFd) == -1 || pipe(pRicezioneFd) == -1)
    {
        perror("impossibile eseguire la pipe");
        exit(EXIT_FAILURE);
    }

    if(strncmp(stringa, "exit", N) != 0)
    {
        pid_t processPid;
        if((processPid = fork()) == 1)
        {
            perror("impossibile eseguire la fork");
            exit(EXIT_FAILURE);
        }

        if(processPid != 0)
        {
            closePipe(pipeFd[0]);
            closePipe(pRicezioneFd[1]);

            printf("operazione: %s\n", stringa);

            Write(pipeFd[1], stringa, N);
            closePipe(pipeFd[1]);

            read(pRicezioneFd[0], stringa, N);

            closePipe(pRicezioneFd[0]);

            if(wait(NULL) == -1)
            {
                perror("Impossibile eseguire la wait");
                exit(EXIT_FAILURE);
            }

            printf("%s\n", stringa);
        }
        else
        {
            closePipe(pipeFd[1]);
            closePipe(pRicezioneFd[0]);

            Dup2(pipeFd[0], 0);
            Dup2(pRicezioneFd[1], 1);
            Dup2(pRicezioneFd[1], 2);


            // closePipe(pipeFd[0]);
            // closePipe(pRicezioneFd[1]);

            if(execl("/usr/bin/bc", "bc", "-lq", NULL) == -1)
            {
                perror("programma non reperibile");
                exit(EXIT_FAILURE);
            }
        }
    }

    return 0;
}
void closePipe(int pipeFd)
{
    if(close(pipeFd) == -1)
    {
        perror("impossibile chiudere il fd della pipe");
        exit(EXIT_FAILURE);
    }
}

void Dup2(int pipeTempFd, int fd)
{
    if(dup2(pipeTempFd, fd) == -1)
    {
        perror("Impossibile eseguire la dup2");
         exit(EXIT_FAILURE);
    }
}

void Write(int pipeTempFd, char stringa[], int n)
{
    if(write(pipeTempFd, stringa, n) == -1)
    {
        perror("impossibile scrivere sulla pipa");
        exit(EXIT_FAILURE);
    }
}

void Read(int pipeTempFd, char stringa[], int n)
{
    if(read(pipeTempFd, stringa, n) == -1)
    {
        perror("impossibile leggere dalla pipe pipa");
        exit(EXIT_FAILURE);
    }
}

【问题讨论】:

标签: c pipe fork dup2


【解决方案1】:

您正在将大部分未初始化的 stringa 的所有 1024 字节写入bc,然后它会因非法字符而窒息。 bc 期望换行符终止,“纯文本”表达式。

#define N 1024
char stringa[N];      // stringa := "\236\0\0\241\177>\0\0\3704\241..."

scanf("%s", stringa); // stringa := "1+2\0\177>\0\0\3704\241..."
                      //                ^^^^^^^^^^^^^^^^^^^^
....
Write(pipeFd[1], stringa, N);  // ohimè!

你会想要这样的东西,而不是:

Write(pipeFd[1], stringa, strlen(stringa));
Write(pipeFd[1], "\n", 1);

【讨论】:

    【解决方案2】:

    我认为我的代码与您的代码相似,一个主要变化是 exec 调用的参数。另一个变化是我在子进程中只有 2 个 dup2 调用,一个用于更改标准输入,另一个用于标准输出。你只需要改变这些。

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<unistd.h>
    #include<sys/wait.h>
    
    #define N 1024
    #define READ_END 0
    #define WRITE_END 1
    
    int main()
    {
        char *input = NULL;
        size_t len = 0;
        ssize_t amt_read = 0;
    
        puts("Please enter a string: ");
        amt_read = getline(&input, &len, stdin);
    
        if(amt_read < 0)
            exit(EXIT_FAILURE);
    
        pid_t pid;
    
        int fd[2];
        int fd_return[2];
    
        // Create the pipes
        pipe(fd);
        pipe(fd_return);
    
        pid = fork();
    
        if(pid==0)
        {  // If child process
            dup2(fd[READ_END], STDIN_FILENO);
            dup2(fd_return[WRITE_END], STDOUT_FILENO);
    
            close(fd[WRITE_END]);
            close(fd[READ_END]);
    
            close(fd_return[WRITE_END]);
            close(fd_return[READ_END]);
    
            execl("/usr/bin/bc", "/usr/bin/bc", "-lq", (char *)NULL);
    
            fprintf(stderr, "Failed to execute\n");
            exit(EXIT_FAILURE);
        }
        else
        { // If parent process
            int status;
            close(fd[READ_END]);
            close(fd_return[WRITE_END]);
    
            // Write to the pipe
            write(fd[WRITE_END], input, strlen(input));
    
            close(fd[WRITE_END]);
    
            // Wait for the child to finish
            waitpid(pid, &status, 0);
    
            char buff[N];
            buff[N-1] = 0;
    
            read(fd_return[READ_END], buff, N-1);
            *(index(buff, '\n')) = '\0'; // Add null terminator on your own
    
            close(fd_return[READ_END]);
    
            printf("The Result is: %s\n", buff);
    
        }
    
        free(input);
    
        return 0;
    }
    

    编辑: 我编辑了代码并调试了它。我还更改了用户输入示意图,因此现在您不再为用户输入分配静态存储,而是动态分配,然后在最后使用 free() 释放该存储;

    请注意,我确实保留了静态存储以重新读取输入,您可以在以后更改此设置。

    非法字符:^@ 来自 write() 写的太多,注意现在我们只写 strlen(buff) 数量。

    注意:除非字符串以 "...\n\0" 结尾,否则 bc 的输入将不起作用,幸运的是 getline() 默认会这样做。

    另外,我让所有管道都关闭,这不是问题的原因。

    【讨论】:

    • 我通过在代码开头添加:#define READ_END 0 #define OUTPUT_END 1 来测试您的代码,但它给了我类似的错误。
    • 这是我的代码的输出(每次变化很小):
       2+2 操作:2+2 (standard_in) 1:非法字符:^@ (standard_in) 1:非法字符:\253 (standard_in) 1:非法字符:\177 += 
    • 我将尝试在我的机器上运行它。通过在 gdb 上运行,您知道哪一行引发了错误吗?
    • 我不知道;从您发送的链接来看,它可能很接近。
    • 我修复了代码,它在 linux mint 上为我工作。让我知道它是否适合您。
    猜你喜欢
    • 1970-01-01
    • 2021-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-17
    • 2021-04-06
    • 1970-01-01
    • 2020-06-19
    相关资源
    最近更新 更多