Vraag popen gelijktijdig lezen en schrijven


Is het mogelijk om te lezen en schrijven naar een bestandsdescriptor geretourneerd door popen. Ik heb een interactief proces dat ik graag zou willen controleren via C. Als dit met popen niet mogelijk is, is er dan een manier om het te omzeilen?


26
2018-05-30 01:27


oorsprong


antwoorden:


Zoals al is aangegeven, werkt popen in één richting. Als u moet lezen en schrijven, kunt u een pipe met pipe () maken, een nieuw proces overspannen met fork () en exec-functies en vervolgens de invoer en uitvoer omleiden met dup2 (). In ieder geval geef ik de voorkeur aan exec over popen, omdat het je een betere controle geeft over het proces (je kent bijvoorbeeld de pid)

Bewerkt:

Zoals de opmerkingen suggereerden, kan een pijp slechts in één richting worden gebruikt. Daarom moet je aparte pijpen maken om te lezen en te schrijven. Omdat het eerder geposte voorbeeld niet klopte, heb ik het verwijderd en een nieuw, correct exemplaar gemaakt:

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

int main(int argc, char** argv)
{
  pid_t pid = 0;
  int inpipefd[2];
  int outpipefd[2];
  char buf[256];
  char msg[256];
  int status;

  pipe(inpipefd);
  pipe(outpipefd);
  pid = fork();
  if (pid == 0)
  {
    // Child
    dup2(outpipefd[0], STDIN_FILENO);
    dup2(inpipefd[1], STDOUT_FILENO);
    dup2(inpipefd[1], STDERR_FILENO);

    //ask kernel to deliver SIGTERM in case the parent dies
    prctl(PR_SET_PDEATHSIG, SIGTERM);

    //replace tee with your process
    execl("/usr/bin/tee", "tee", (char*) NULL);
    // Nothing below this line should be executed by child process. If so, 
    // it means that the execl function wasn't successfull, so lets exit:
    exit(1);
  }
  // The code below will be executed only by parent. You can write and read
  // from the child using pipefd descriptors, and you can send signals to 
  // the process using its pid by kill() function. If the child process will
  // exit unexpectedly, the parent process will obtain SIGCHLD signal that
  // can be handled (e.g. you can respawn the child process).

  //close unused pipe ends
  close(outpipefd[0]);
  close(inpipefd[1]);

  // Now, you can write to outpipefd[1] and read from inpipefd[0] :  
  while(1)
  {
    printf("Enter message to send\n");
    scanf("%s", msg);
    if(strcmp(msg, "exit") == 0) break;

    write(outpipefd[1], msg, strlen(msg));
    read(inpipefd[0], buf, 256);

    printf("Received answer: %s\n", buf);
  }

  kill(pid, SIGKILL); //send SIGKILL signal to the child process
  waitpid(pid, &status, 0);
}

20
2018-05-30 05:28



De reden popen() en vrienden bieden geen bidirectionele communicatie is dat het door deadbuffing gevoelig zou zijn vanwege buffering in het subproces. Alle geïmproviseerde leidingen en socketpair() oplossingen die in de antwoorden worden besproken, lijden aan hetzelfde probleem.

Onder UNIX kunnen de meeste opdrachten niet vertrouwd worden om één regel te lezen en onmiddellijk verwerken en afdrukken, behalve als hun standaarduitvoer een tty is. De reden hiervoor is dat stdio-buffers standaard worden uitgevoerd in gebruikersruimte en de write() systeem aanroepen totdat de buffer vol is of de stdio-stroom is gesloten (meestal omdat het programma of script op het punt staat af te sluiten nadat EOF bij invoer is gezien). Als je via een pijp naar de stdin van zo'n programma schrijft, en nu wacht op een antwoord van de stdout van dat programma (zonder de ingangspijp te sluiten), zit het antwoord vast in de stdio-buffers en zal nooit uitkomen - Dit is een impasse.

Je kunt sommige line-oriented programma's bedriegen (bijv grep) om niet te bufferen door een pseudo-tty te gebruiken om met ze te praten; kijk eens naar libexpect(3). Maar in het algemene geval zou u voor elk bericht een ander subproces opnieuw moeten uitvoeren, zodat u EOF kunt gebruiken om het einde van elk bericht aan te geven en ervoor te zorgen dat alle buffers in het commando (of de pijplijn van opdrachten) worden leeggemaakt. Vanzelfsprekend geen goede prestatie.

Zie meer informatie over dit probleem in de perlipc man-pagina (dit is voor bidirectionele leidingen in Perl, maar de bufferingoverwegingen zijn van toepassing ongeacht de taal die voor het hoofdprogramma wordt gebruikt).


5
2018-04-25 13:38



U wilt iets dat vaak popen2 wordt genoemd. Hier is een basisimplementatie zonder foutcontrole.


4
2018-05-30 02:21



popen() kan de pijp alleen openen in de lees- of schrijfmodus, niet beide. Kijk eens naar deze thread voor een oplossing.


3
2018-05-30 01:30



Gebruik forkpty (het is niet standaard, maar de API is erg mooi en je kunt altijd je eigen implementatie gebruiken als je die niet hebt) en exec het programma waarmee je wilt communiceren in het onderliggende proces.

Als alternatief, als tty-semantiek je niet bevalt, zou je zoiets kunnen schrijven forkpty maar met behulp van twee leidingen, een voor elke communicatierichting of voor gebruik socketpairom te communiceren met het externe programma via een unix-socket.


1
2018-05-30 01:32



Je kunt niet gebruiken popen om tweerichtingspijpen te gebruiken.

Sommige besturingssystemen bieden geen ondersteuning voor tweerichtingspijpen, in welk geval een sokpaar (socketpair) is de enige manier om het te doen.


0
2018-05-30 01:31



In een van netresolve backends Ik praat tegen een script en daarom moet ik het schrijven naar het script stdin en lees van zijn stdout. De volgende functie voert een commando uit met stdin en stdout doorgestuurd naar een pipe. U kunt het gebruiken en aanpassen aan uw wensen.

static bool
start_subprocess(char *const command[], int *pid, int *infd, int *outfd)
{
    int p1[2], p2[2];

    if (!pid || !infd || !outfd)
        return false;

    if (pipe(p1) == -1)
        goto err_pipe1;
    if (pipe(p2) == -1)
        goto err_pipe2;
    if ((*pid = fork()) == -1)
        goto err_fork;

    if (*pid) {
        /* Parent process. */
        *infd = p1[1];
        *outfd = p2[0];
        close(p1[0]);
        close(p2[1]);
        return true;
    } else {
        /* Child process. */
        dup2(p1[0], 0);
        dup2(p2[1], 1);
        close(p1[0]);
        close(p1[1]);
        close(p2[0]);
        close(p2[1]);
        execvp(*command, command);
        /* Error occured. */
        fprintf(stderr, "error running %s: %s", *command, strerror(errno));
        abort();
    }

err_fork:
    close(p2[1]);
    close(p2[0]);
err_pipe2:
    close(p1[1]);
    close(p1[0]);
err_pipe1:
    return false;
}

https://github.com/crossdistro/netresolve/blob/master/backends/exec.c#L46

(Ik heb dezelfde code gebruikt in Kan popen () bidirectionele leidingen maken zoals pipe () + fork ()?)


0
2018-02-23 17:18



popen werkt voor mij in beide richtingen (lees en schrijf) Ik heb een gebruikt popen() pijp in beide richtingen ..

Een kindproces lezen en schrijven stdin en stdout met de bestandsdescriptor geretourneerd door popen (opdracht, "w")

Het lijkt goed te werken ..

Ik ging ervan uit dat het zou werken voordat ik het beter wist, en dat doet het. Volgens berichten hierboven zou dit niet moeten werken .. wat me een beetje zorgen baart.

gcc op raspbian (raspbery pi debian)


-1
2017-10-08 08:43