2014-12-05 17 views
7

Tôi có một chức năng mà Forks một quá trình, mô tả file trùng lặp cho đầu vào và đầu ra bộ đệm, và sau đó chạy execl trên một lệnh được thông qua vào thông qua một chuỗi gọi cmd:Chụp thoát mã trạng thái của quá trình con

static pid_t 
c2b_popen4(const char* cmd, int pin[2], int pout[2], int perr[2], int flags) 
{ 
    pid_t ret = fork(); 

    if (ret < 0) { 
     fprintf(stderr, "fork() failed!\n"); 
     return ret; 
    } 
    else if (ret == 0) { 
     /*                                                                             
      Assign file descriptors to child pipes (not shown)...                                                                        
     */ 
     execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); 
     fprintf(stderr, "execl() failed!\n"); 
     exit(EXIT_FAILURE); 
    } 
    else { 
     /*                                                                             
      Close parent read and write pipes (not shown)...                                                                        
     */ 
     return ret; 
    } 
    return ret; 
} 

Mỗi trường hợp cmd xử lý dữ liệu của tôi một cách chính xác, miễn là các đầu vào thử nghiệm của tôi là chính xác.

Khi dữ liệu xấu sẽ được chuyển cho một quá trình con, chương trình cha mẹ của tôi sẽ chạy đến khi kết thúc và thoát với mã trạng thái không lỗi của 0.

Nếu tôi cố tình đưa vào đầu vào xấu - để cố tình cố gắng để có được một trong các trường hợp cmd không thành công theo cách dự kiến ​​- tôi muốn biết cách chụp trạng thái thoát của số cmd để tôi có thể phát hành mã trạng thái lỗi chính xác từ chương trình chính, trước khi chấm dứt.

Điều này thường được thực hiện như thế nào?

Trả lời

11

Bạn có thể nhận được trạng thái thoát của đứa trẻ qua đối số đầu tiên của wait(), hoặc đối số thứ hai của waitpid(), và sau đó sử dụng các macro WIFEXITEDWEXITSTATUS với nó.

Ví dụ:

pid_t ret = c2b_popen4("myprog", pin, pout, perr, 0); 

if (ret > 0) { 
    int status; 

    if (waitpid(ret, &status, 0) == -1) { 
     perror("waitpid() failed"); 
     exit(EXIT_FAILURE); 
    } 

    if (WIFEXITED(status)) { 
     int es = WEXITSTATUS(status); 
     printf("Exit status was %d\n", es); 
    } 
} 

Một đơn giản hóa ví dụ làm việc:

failprog.c:

int main(void) { 
    return 53; 
} 

shellex.c:

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/wait.h> 

int main(void) 
{ 
    pid_t p = fork(); 
    if (p == -1) { 
     perror("fork failed"); 
     return EXIT_FAILURE; 
    } 
    else if (p == 0) { 
     execl("/bin/sh", "bin/sh", "-c", "./failprog", "NULL"); 
     return EXIT_FAILURE; 
    } 

    int status; 
    if (waitpid(p, &status, 0) == -1) { 
     perror("waitpid failed"); 
     return EXIT_FAILURE; 
    } 

    if (WIFEXITED(status)) { 
     const int es = WEXITSTATUS(status); 
     printf("exit status was %d\n", es); 
    } 

    return EXIT_SUCCESS; 
} 

Output :

[email protected]:~/src/sandbox$ ./shellex 
exit status was 53 
[email protected]:~/src/sandbox$ 

waitpid() sẽ chặn cho đến khi quá trình với ID tiến trình được cung cấp thoát. Vì bạn đang gọi chức năng của mình với tên popen() và chuyển các đường ống đến đó, có lẽ quá trình con của bạn không chấm dứt nhanh chóng, vì vậy có thể không phải là nơi thích hợp để kiểm tra, nếu cuộc gọi thành công. Bạn có thể vượt qua WNOHANG làm thông số thứ ba để waitpid() để kiểm tra xem quá trình đã chấm dứt chưa và trả lại 0 nếu đứa trẻ chưa thoát, nhưng bạn phải cẩn thận về khi bạn thực hiện việc này, vì bạn không nhận được bảo đảm nào quá trình sẽ chạy khi nào. Nếu bạn gọi waitpid() với WNOHANG ngay sau khi trở về từ c2b_popen4(), nó có thể trả về 0 trước khi quá trình con của bạn có cơ hội thực thi và chấm dứt bằng mã lỗi và làm cho nó trông như thực hiện thành công thành công.

Nếu quá trình chết ngay lập tức, bạn sẽ gặp sự cố khi đọc và ghi vào đường ống của mình, do đó, một tùy chọn là kiểm tra waitpid() nếu bạn gặp lỗi từ lần đầu tiên thực hiện điều đó, để kiểm tra xem read() hoặc write() không thành công vì quá trình con của bạn đã qua đời. Nếu điều đó trở thành sự thật, bạn có thể truy xuất trạng thái thoát và thoát khỏi chương trình tổng thể của bạn sau đó.

Có các chiến lược khác có thể, bao gồm bắt tín hiệu SIGCHLD, vì sẽ được tăng lên bất cứ khi nào một trong các tiến trình con của bạn chết. Nó sẽ là OK, ví dụ, để gọi _exit() ngay từ bộ xử lý tín hiệu của bạn, sau khi chờ đợi quá trình con (gọi waitpid() trong bộ xử lý tín hiệu cũng an toàn) và nhận trạng thái thoát.

+1

Khi tôi chạy khối mã đầu tiên của bạn (ví dụ "ví dụ"), quá trình treo trên cuộc gọi 'waitpid()'. –

+1

@AlexReynolds: Đó là cách nó hoạt động theo mặc định, xem cập nhật để trả lời. Bạn sẽ cần một số loại chiến lược để phát hiện và sau đó đối phó với khả năng của các quá trình con chấm dứt sớm. –

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