如何在c中使用pipe和dup2

问题描述

我必须使用 c 中的管道来模拟这个命令:echo "" | bc -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,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],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,int n)
{
    if(write(pipeTempFd,n) == -1)
    {
        perror("impossibile scrivere sulla pipa");
        exit(EXIT_FAILURE);
    }
}

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

解决方法

您正在将大部分未初始化的 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],strlen(stringa));
Write(pipeFd[1],"\n",1);
,

我认为我的代码与您的相似,一个主要变化是 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" 结尾,幸运的是 getline() 默认情况下会这样做,否则 bc 的输入将不起作用。

另外,我让所有的管道都关闭了,这并没有导致问题。