2015-04-21 22 views
5

che sto eseguendo un comando di lunga durata (e spesso bloccati) tramite popen(): "ls -R /"Perché c'è un ritardo incorporato durante la lettura con popen()?

Problema: popen() legge in un buffer che si fornisce, e tenta apparentemente per popolare l'INTERO buffer prima di tornare. Questo causa il blocco abbastanza spesso (se il buffer è grande).

La soluzione sembrerebbe essere quella di rendere il sotto fd non bloccante. Quando lo faccio, popen() blocca ancora, di solito circa 1 secondo ogni volta. Perché sta succedendo?

Ecco il mio codice. Assicurati di compilare con -std = C++ 11:

#include <cstdio> 
#include <iostream> 
#include <sys/time.h> 
#include <unistd.h> 
#include <fcntl.h> 

static constexpr size_t SIZE = 65536; 

struct Time 
{ 
    friend std::ostream &operator<<(std::ostream &os, Time const &t) 
    { 
     (void)t; 

     timeval tv; 
     gettimeofday(&tv, nullptr); 

     os << tv.tv_sec << "." << std::fixed << tv.tv_usec << " "; 
     return os; 
    } 
}; 

int main() 
{ 
    FILE *file; 
    file = popen("ls -R /", "r"); 
    if(!file) 
    { 
     std::cerr << "Could not open app: " << errno; 
     return -1; 
    } 

    // Make it non-blocking 
    int fd = fileno(file); 
    fcntl(fd, F_SETFL, O_NONBLOCK); 

    char buffer[SIZE]; 
    Time t; 
    while(true) 
    { 
     int rc = fread(buffer, 1, SIZE, file); 
     if(rc <= 0) 
     { 
     if(EAGAIN == errno) 
     { 
      usleep(10); 
      continue; 
     } 
     std::cerr << t << "Error reading: " << errno << std::endl; 
     break; 
     } 
     std::cerr << t << "Read " << rc << std::endl; 
    } 
    pclose(file); 
    return 0; 
} 

di uscita (si noti che sono circa 1 secondo di distanza, anche se il fd è non bloccante e ho solo una pausa 10mS nel loop):

1429625100.983786 Read 4096 
1429625101.745369 Read 4096 
1429625102.426967 Read 4096 
1429625103.185273 Read 4096 
1429625103.834241 Read 4096 
1429625104.512131 Read 4096 
1429625105.188010 Read 4096 
1429625105.942257 Read 4096 
1429625106.642877 Read 4096 
+0

Si sta eseguendo il piping da 'ls -R /', quindi ci sarà un po 'di ritardo a causa dell'attività del disco. FWIW sul mio sistema generalmente vedo un ritardo molto più basso tra le letture con lo stesso codice. – davmac

+0

Prova a eseguire il piping da "yes" e guarda cosa succede. – Xaqq

+0

Penso che la tua domanda potrebbe essere formulata in modo migliore. "popen() si legge in un buffer che tu fornisci" - no, non lo fa, apre solo la pipa. Leggi da questo separatamente. "popen() blocca ancora" - non è "popen" che blocca, è la lettura. – davmac

risposta

5

In primo luogo, è necessario utilizzare read anziché fread. Le funzioni stdio hanno il proprio livello di buffering al di là dei sistemi operativi, in modo che possano bloccare anche i descrittori di file non bloccanti. Utilizzare read per evitare questo.

In secondo luogo, è necessario interrompere ls dal buffer dell'output. default behavior for programs that link to glibc utilizza il buffering di linea quando stdout si connette a un TTY e il buffer completo quando è connesso a una pipe o reindirizzato a un file. Il buffering completo significa che l'output viene svuotato solo quando il buffer 4KB si riempie, invece di svuotarsi ogni volta che viene emesso un newline.

È possibile utilizzare stdbuf per sovrascrivere questo comportamento. Si noti che funzionerà solo per i programmi che utilizzano i flussi C e collegano a glibc. Questa è la maggior parte dei programmi, ma non tutti.

popen("stdbuf -oL ls -R /", "r"); 
+0

Questo non spiega perché una lettura non bloccante sembra bloccarsi, anche se ... – davmac

+0

Wow era esattamente così. Fratello genio, grazie! @ Davmac, non stava bloccando - ho solo stampato qualcosa quando è avvenuta una lettura di successo. – Badmanchild

+0

@Badmanchild ah sì, vedo, ho letto male il tuo codice e ho pensato di stampare il messaggio di errore se la lettura non è riuscita in tutti i casi. Ora vedo che controlla specificamente EAGAIN. – davmac