C - странное поведение fork () + execl () - PullRequest
0 голосов
/ 24 мая 2018

Я постараюсь быть максимально конкретным с моей проблемой.Моя цель - реализовать два сокета (клиент / сервер);клиент должен отправить серверу команду awk + несколько строк ввода.Сервер должен выполнить команду awk с заданным вводом и отослать клиенту каждую строку стандартного вывода awk и каждую строку стандартного ввода awk, соединенную со строкой «ERRLINEi» (i = номер строки).

Я спросил, как это сделать, так как я полностью потерялся, и я получил хороший совет, чтобы использовать fork, execl, pipe и dup2.Поэтому я попытался записать это:

void run_awk(char *command, char *out, char *err){
    int in_pipe[2]; //send input string from parent to child --> input
    int out_pipe[2]; //receive awk's stdout from child --> send back to client
    int err_pipe[2]; //receive awk's stderr from child --> write on fd 4, send back to client with errline
    pid_t p;
    if((pipe(in_pipe)+pipe(out_pipe)+pipe(err_pipe)) < 0) syscallerror("pipe");
    if((p = fork()) < 0) syscallerror("fork");
    if(p > 0){
        close(in_pipe[0]);  // Close reading end of first pipe
        close(out_pipe[1]); // Close writing end of second pipe
        write(in_pipe[1], command, strlen(command)+1);
        printf("parent process %d: sent input %s\n", getpid(), command);
        close(in_pipe[1]);
        // Wait for child to send out and err
        wait(NULL);
        char s_out[301];
        char s_err[301];
        int n = read(out_pipe[0], s_out, 300);
        int k = read(err_pipe[0], s_err, 300);
        if(n > 0) printf("parent %d: received output %s\n", getpid(), s_out);
        if(k > 0) printf("parent %d: received error %s\n", getpid(), s_err);
        close(out_pipe[0]);
        close(err_pipe[0]);
        close(err_pipe[1]);
    }
    else if(p == 0){
        close(in_pipe[1]);  // Close writing end of first pipe
        // Read a string using first pipe
        char buf[300];
        read(in_pipe[0], buf, 300);
        printf("child %d: received input %s\n", getpid(), buf);
        // Close all reading ends
        close(in_pipe[0]);
        close(out_pipe[0]);
        close(err_pipe[0]);
        dup2(out_pipe[1], 1); //set out_pipe[1] as stdout
        dup2(err_pipe[1], 2); //set err_pipe[1] as stderr
        close(err_pipe[1]);
        close(out_pipe[1]);
        execl("usr/bin/awk", "awk", buf, (char *)0); //at this point awk's
        exit(0);               //out and err should be redirected to the pipes
    }
}

Но это не работает.Обратите внимание, что эта функция вызывается в цикле while, который повторяется до тех пор, пока новые входные строки не будут получены от клиента.Я на самом деле не понимал, почему, поэтому я поставил инструкцию printf перед вызовом функции (в основной функции, где вызывается run_awk ()), и в результате этого run_awk () вызывался несколько раз из одного и того же процесса с одним и тем же вводом,

Примечание: параметры my * out и * err должны использоваться для хранения результатов awk.Я оставил их неиспользованными, так как я просто хотел посмотреть, что делает моя программа.

ОБНОВЛЕНИЕ Я понял некоторые ошибки в моем коде.Я вернулся к основной функции, но у меня все еще есть проблемы.Практически мне нужно сделать следующее: отправить одну команду (с разумным синтаксисом awk) от родителя к потомку (и я достиг этого), а затем заставить ребенка читать строки ввода (произвольный текст) одну за другой, из stdin, что позволяет execlчтобы работать так:

execl("/usr/bin/awk", "awk", command, (char *)0)

Мой новый код:

if((t_fd = accept(l_fd, (struct sockaddr *)NULL, NULL)) < 0) sockerror(2, 0); //accept connection from client
    recv(t_fd, &com_len, sizeof(int), 0); //receive command length
    command = malloc(com_len+1);
    rbytes = recv(t_fd, command, com_len, 0); //receive command
    command[rbytes] = '\0';
    printf("Received command: %s\n", command);
    if((pipe(in_pipe)+pipe(out_pipe)+pipe(err_pipe)) < 0) syscallerror("pipe"); //init pipes
    bool check = true;
    while(recv(t_fd, &inp_len, sizeof(int), 0) > 0){ //receive single input line length - loop while there are lines to read
        memset(input, 0, inp_len+1);
        recv(t_fd, input, inp_len, 0);  //receive input line
        if((p = fork()) < 0) syscallerror("fork");
        if(p > 0){
            close(in_pipe[0]);  // Close reading end of first pipe
            if(check){ //this makes the server write the command in the pipe just once
                write(in_pipe[1], command, strlen(command));
                check = false;
            }
            write(in_pipe[1], input, strlen(input)); //write the input line

            //close(in_pipe[1]); //I can't close it because other lines will be sent in the loop
            close(out_pipe[1]);
            close(err_pipe[1]);
            int n = read(out_pipe[0], out, 300); //read should block until child has produced output (you can assume it will always produce it)
            int k = read(err_pipe[0], err, 300);
            if(n > 0) printf("parent %d: received output %s\n", getpid(), out);
            if(k > 0) printf("parent %d: received error %s\n", getpid(), err);
            close(out_pipe[0]);
            close(err_pipe[0]);
            memset(out, 0, 301);
            memset(err, 0, 301);
        }
        else{
            close(in_pipe[1]);  // Close writing end of first pipe
            char com[300]; // Read command using first pipe
            read(in_pipe[0], com, 300);
            // Close all reading ends
            close(out_pipe[0]);
            close(err_pipe[0]);
            dup2(in_pipe[0], 0); //set correct file descriptors
            dup2(out_pipe[1], 1);
            dup2(err_pipe[1], 2);
            close(in_pipe[0]);
            close(err_pipe[1]);
            close(out_pipe[1]);
            if(execl("/usr/bin/awk", "awk", com, (char *)0) < 0) syscallerror("execl");
            exit(0);
        }
    }
    close(in_pipe[1]);
    kill(p, SIGKILL);

Если вы видите какие-либо неинициализированные переменные, это потому, что они объявлены вне этого цикла.Как я могу решить это?

...