2010-10-30 27 views
5

Tôi đã viết một mã trong C/C++ mà dĩa một tiến trình con, nhân đôi stdin/stdout thành một đầu ống và gọi execvp.Vô hiệu hóa bộ đệm đệm của một quá trình chia rẽ

Tất cả mọi thứ đang làm việc tốt (ví dụ: sản lượng từ stdin/err ra/được chụp bởi quá trình cha mẹ)

Vấn đề là các stdout đứa trẻ được đệm.

vì vậy nếu mã con trông như thế này:

printf("Enter any key and hit ENTER:\n"); 
fgets(line); 
printf("read: %s\n", line); 
exit(0); 

Trong quá trình cha mẹ tôi không thấy dòng chữ 'Nhập một phím bất kỳ:' - đó sẽ là "đỏ mặt" chỉ sau khi chương trình gọi exit (trong đó ô tô sẽ xóa đệm stdout) hoặc cuộc gọi rõ ràng để 'tuôn ra (stdout)' được thêm

tôi đã làm một số nghiên cứu và cố gắng thêm một cuộc gọi để vô hiệu hóa đệm stdout bằng cách thêm một cuộc gọi đến:

setvbuf (stdout, NULL, _IONBF, 0); ngay trước khi gọi execvp (...) trong quá trình mẹ

để mã có liên quan hiện nay trông như thế này:

int rc = fork(); 
if (rc == 0) { 
    // Child process 
    if(workingDirectory.IsEmpty() == false) { 
     wxSetWorkingDirectory(workingDirectory); 
    } 
    int stdin_file = fileno(stdin ); 
    int stdout_file = fileno(stdout); 
    int stderr_file = fileno(stderr); 

    // Replace stdin/out with our pipe ends 
    dup2 (stdin_pipe_read, stdin_file); 
    close(stdin_pipe_write); 

    dup2 (stdout_pipe_write, stdout_file); 
    dup2 (stdout_pipe_write, stderr_file); 
    close(stdout_pipe_read); 

    setvbuf(stdout, NULL, _IONBF, 0); 

    // execute the process 
    execvp(argv[0], argv); 
    exit(0); 

} 

Với không may mắn.

Bất kỳ ý tưởng nào?

EDIT:

đây là một mẫu mã cha mẹ, điều duy nhất cần thay đổi là đường dẫn đến đứa trẻ thực thi:

#include <unistd.h> 
#include <signal.h> 
#include <sys/types.h> 
#include <sys/select.h> 
#include <errno.h> 
#include <sys/wait.h> 
#include <string> 
#include <string.h> 
#include <cstdio> 

static int read_handle(-1); 
static pid_t pid; 

bool read_from_child(std::string& buff) { 
    fd_set rs; 
    timeval timeout; 

    memset(&rs, 0, sizeof(rs)); 
    FD_SET(read_handle, &rs); 
    timeout.tv_sec = 1; // 1 second 
    timeout.tv_usec = 0; 

    int rc = select(read_handle+1, &rs, NULL, NULL, &timeout); 
    if (rc == 0) { 
     // timeout 
     return true; 

    } else if (rc > 0) { 
     // there is something to read 
     char buffer[1024*64]; // our read buffer 
     memset(buffer, 0, sizeof(buffer)); 
     if(read(read_handle, buffer, sizeof(buffer)) > 0) { 
      buff.clear(); 
      buff.append(buffer); 
      return true; 
     } 

     return false; 
    } else { /* == 0 */ 
     if (rc == EINTR || rc == EAGAIN) { 
      return true; 
     } 

     // Process terminated 
     int status(0); 
     waitpid(pid, &status, 0); 
     return false; 
    } 
} 

void execute() { 
    char *argv[] = {"/home/eran/devl/TestMain/Debug/TestMain", NULL}; 
    int argc = 1; 

    int filedes[2]; 
    int filedes2[2]; 

    // create a pipe 
    int d; 
    d = pipe(filedes); 
    d = pipe(filedes2); 

    int stdin_pipe_write = filedes[1]; 
    int stdin_pipe_read = filedes[0]; 

    int stdout_pipe_write = filedes2[1]; 
    int stdout_pipe_read = filedes2[0]; 

    int rc = fork(); 
    if (rc == 0) { 

     // Child process 
     int stdin_file = fileno(stdin ); 
     int stdout_file = fileno(stdout); 
     int stderr_file = fileno(stderr); 

     // Replace stdin/out with our pipe ends 
     dup2 (stdin_pipe_read, stdin_file); 
     close(stdin_pipe_write); 

     dup2 (stdout_pipe_write, stdout_file); 
     dup2 (stdout_pipe_write, stderr_file); 
     close(stdout_pipe_read); 

     setvbuf(stdout, NULL, _IONBF, 0); 

     // execute the process 
     execvp(argv[0], argv); 

    } else if (rc < 0) { 
     perror("fork"); 
     return; 

    } else { 
     // Parent 
     std::string buf; 
     read_handle = stdout_pipe_read; 
     while(read_from_child(buf)) { 
      if(buf.empty() == false) { 
       printf("Received: %s\n", buf.c_str()); 
      } 
      buf.clear(); 
     } 
    } 
} 

int main(int argc, char **argv) { 
    execute(); 
    return 0; 
} 
+0

Tôi nghĩ rằng bạn cần phải đóng (stdout_pipe_write) và stdin_piperead sau khi các cuộc gọi tương ứng để dup2 – Flexo

Trả lời

7

Trên thực tế, sau khi vật lộn với nó một chút, nó có vẻ như giải pháp duy nhất cho vấn đề này là bằng cách làm cho quá trình 'mẹ' giả vờ là một thiết bị đầu cuối sử dụng các cuộc gọi API giả thiết bị đầu cuối của hệ điều hành.

Người ta nên gọi 'openpty()' trước ngã ba(), và bên trong mã con, anh ta nên gọi 'login_tty (slave)' nô lệ sau đó trở thành stdin/out và stderr.

Bằng cách giả vờ thiết bị đầu cuối, bộ đệm của thiết bị xuất chuẩn sẽ tự động được đặt thành 'chế độ dòng' (tức là xảy ra xả khi \ n gặp phải). Phụ huynh nên sử dụng mô tả 'chủ' để đọc/ghi với quy trình con.

Mã cha mẹ sửa đổi (trong trường hợp bất cứ ai sẽ bao giờ cần cái này):

#include <unistd.h> 
#include <signal.h> 
#include <sys/types.h> 
#include <sys/select.h> 
#include <errno.h> 
#include <sys/wait.h> 
#include <string> 
#include <string.h> 
#include <cstdio> 
#include <pty.h> 
#include <utmp.h> 
static int read_handle(-1); 
static pid_t pid; 

bool read_from_child(std::string& buff) { 
    fd_set rs; 
    timeval timeout; 

    memset(&rs, 0, sizeof(rs)); 
    FD_SET(read_handle, &rs); 
    timeout.tv_sec = 1; // 1 second 
    timeout.tv_usec = 0; 

    int rc = select(read_handle+1, &rs, NULL, NULL, &timeout); 
    if (rc == 0) { 
     // timeout 
     return true; 

    } else if (rc > 0) { 
     // there is something to read 
     char buffer[1024*64]; // our read buffer 
     memset(buffer, 0, sizeof(buffer)); 
     if(read(read_handle, buffer, sizeof(buffer)) > 0) { 
      buff.clear(); 
      buff.append(buffer); 
      return true; 
     } 

     return false; 
    } else { /* == 0 */ 
     if (rc == EINTR || rc == EAGAIN) { 
      return true; 
     } 

     // Process terminated 
     int status(0); 
     waitpid(pid, &status, 0); 
     return false; 
    } 
} 

void execute() { 
    char *argv[] = {"/home/eran/devl/TestMain/Debug/TestMain", NULL}; 
    int argc = 1; 

    int master, slave; 
    openpty(&master, &slave, NULL, NULL, NULL); 

    int rc = fork(); 
    if (rc == 0) { 
     login_tty(slave); 
     close(master); 

     // execute the process 
     if(execvp(argv[0], argv) != 0) 
      perror("execvp"); 

    } else if (rc < 0) { 
     perror("fork"); 
     return; 

    } else { 
     // Parent 
     std::string buf; 
     close(slave); 

     read_handle = master; 
     while(read_from_child(buf)) { 
      if(buf.empty() == false) { 
       printf("Received: %s", buf.c_str()); 
      } 
      buf.clear(); 
     } 
    } 
} 

int main(int argc, char **argv) { 
    execute(); 
    return 0; 
} 
+0

+1 Giải pháp tuyệt vời cho vấn đề của riêng bạn. Ngoài ra, tôi tin rằng 'posix_ptopen' được ưa thích hơn' openpty' ngay bây giờ. Xem thêm: http://stackoverflow.com/questions/3382485/reading-child-process-output-as-soon-as-some-is-available –

2

Would chèn một cuộc gọi đến fflush(stdout) sau printf không đủ ?

Nếu không setvbuf nên làm các trick:

setvbuf(stdout,NULL,_IOLBF,0); 
+2

'fflush (NULL) 'sẽ tuôn tất cả mở 'FILE' và có lẽ là một ý tưởng tốt trước khi 'fork'ing. – nategoose

+0

Tôi không nhận thấy điều đó trước đây! – Flexo

+1

Tôi có thể thêm fflush (stdout) vào mã mẫu, tuy nhiên, tôi thường "ngã ba" mã bên thứ ba mà tôi không có quyền kiểm soát. Tôi đã thêm một cuộc gọi đến setvbuf (..) với tất cả các tùy chọn có thể tôi có thể suy nghĩ, không có may mắn ... – Eran

Các vấn đề liên quan