2009-02-13 51 views
18

Sau khi mở đường ống đến một quy trình với popen, có cách nào để giết quá trình đã được bắt đầu không? (Sử dụng pclose không phải là những gì tôi muốn bởi vì điều đó sẽ chờ cho quá trình kết thúc, nhưng tôi cần phải giết nó.)giết một quá trình bắt đầu bằng popen

Trả lời

24

Không sử dụng popen() và viết trình bao bọc của riêng bạn làm những gì bạn muốn.

Nó khá đơn giản để fork(), và sau đó thay thế stdin & stdout bằng cách sử dụng dup2(), và sau đó gọi exec() trên con bạn.

Bằng cách đó, cha mẹ của bạn sẽ có con PID chính xác và bạn có thể sử dụng kill() trên đó.

Tìm kiếm Google để thực hiện "popen2()" đối với một số mã mẫu trên cách triển khai popen() đang làm gì. Nó chỉ dài hơn một chục dòng hoặc dài . Lấy từ dzone.com chúng ta có thể thấy một ví dụ trông như thế này:

#define READ 0 
#define WRITE 1 

pid_t 
popen2(const char *command, int *infp, int *outfp) 
{ 
    int p_stdin[2], p_stdout[2]; 
    pid_t pid; 

    if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0) 
     return -1; 

    pid = fork(); 

    if (pid < 0) 
     return pid; 
    else if (pid == 0) 
    { 
     close(p_stdin[WRITE]); 
     dup2(p_stdin[READ], READ); 
     close(p_stdout[READ]); 
     dup2(p_stdout[WRITE], WRITE); 

     execl("/bin/sh", "sh", "-c", command, NULL); 
     perror("execl"); 
     exit(1); 
    } 

    if (infp == NULL) 
     close(p_stdin[WRITE]); 
    else 
     *infp = p_stdin[WRITE]; 

    if (outfp == NULL) 
     close(p_stdout[READ]); 
    else 
     *outfp = p_stdout[READ]; 

    return pid; 
} 

NB: Có vẻ như popen2() là những gì bạn muốn, nhưng phân bố của tôi dường như không đi kèm với phương pháp này.

+0

điều này không giải quyết được vấn đề trùng lặp bộ nhớ của ngã ba. có thể là chúng tôi không muốn gọi đến ngã ba vì lý do này – Will

+0

@phooji python phải làm gì với bất kỳ thứ gì? –

+0

@ MahmoudAl-Qudsi: Rất tiếc. Tôi chắc đã loooking quá nhiều câu hỏi python. [Nhận xét liên quan đến python trước đó đã được chỉnh sửa] – phooji

0

Cách rõ ràng là hệ thống ("pkill process_name");

Rõ ràng đây là vấn đề nếu bạn có nhiều trường hợp của quy trình đang chạy.

+0

Trường hợp này xảy ra khi nhiều phiên bản đang chạy và cần phải nhắm mục tiêu một phiên bản chính xác. – jldupont

4

popen không thực sự bắt đầu một chuỗi, mà là loại bỏ một quy trình. Khi tôi nhìn vào định nghĩa, nó không giống như có một cách dễ dàng để có được PID của quá trình đó và giết nó. Có thể có những cách khó khăn như kiểm tra quá trình cây, nhưng tôi đoán bạn nên được tốt hơn với việc sử dụng đường ống, ngã ba và chức năng exec để bắt chước hành vi của popen. Sau đó, bạn có thể sử dụng PID bạn nhận được từ ngã ba() để giết quá trình con.

3

Trên thực tế nếu quá trình này được thực hiện I/O (mà nó phải được, nếu không tại sao popen thay vì system (3)?), Sau đó pclose nên Whack nó với một SIGPIPE lần sau nó sẽ cố gắng để đọc hoặc viết, và nó sẽ rơi trên độc đáo :-)

0

Tôi đã làm một cái gì đó tương tự trong python, nơi bạn có thể giết chết một tiến trình con và bất kỳ quy trình con được chia nhỏ cho điều đó. Nó tương tự như giải pháp của slacy thực sự. http://www.pixelbeat.org/libs/subProcess.py

5

Đây là phiên bản cải tiến của popen2 (tín dụng là do Sergey L.). Phiên bản được đăng bởi slacy không trả về PID của quá trình được tạo trong popen2, nhưng PID được gán cho sh.

pid_t popen2(const char **command, int *infp, int *outfp) 
{ 
    int p_stdin[2], p_stdout[2]; 
    pid_t pid; 

    if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0) 
     return -1; 

    pid = fork(); 

    if (pid < 0) 
     return pid; 
    else if (pid == 0) 
    { 
     close(p_stdin[WRITE]); 
     dup2(p_stdin[READ], READ); 
     close(p_stdout[READ]); 
     dup2(p_stdout[WRITE], WRITE); 

     execvp(*command, command); 
     perror("execvp"); 
     exit(1); 
    } 

    if (infp == NULL) 
     close(p_stdin[WRITE]); 
    else 
     *infp = p_stdin[WRITE]; 

    if (outfp == NULL) 
     close(p_stdout[READ]); 
    else 
     *outfp = p_stdout[READ]; 

    return pid; 
} 

Phiên bản mới sẽ được gọi với

char *command[] = {"program", "arg1", "arg2", ..., NULL}; 
+0

Tôi không thấy bất kỳ sự khác biệt trong phần pid; bạn có thể cập nhật nhận xét về phần khác biệt không. – nayab

+0

Cảm ơn cờ vua cho phiên bản của bạn, thực sự tốt! –

0

Tôi có theo ý tưởng của @slacy tạo ra chức năng popen2.
Nhưng tìm thấy vấn đề khi quá trình con là chết, quá trình cha mẹ vẫn đọc tập tin descripter outfp không trở về từ chức năng.

Điều đó có thể khắc phục bằng cách thêm ống không sử dụng gần vào quy trình gốc.

close(p_stdin[READ]); 
close(p_stdout[WRITE]); 

Chúng tôi có thể nhận đúng pid của quy trình mới bằng cách sử dụng bash làm shell.

execl("/bin/bash", "bash", "-c", command, NULL); 

Khi quá trình đứa trẻ chết người gọi chức năng popen2 nên thu thập trạng thái của quá trình trẻ em bằng cách gọi pclose2. nếu chúng ta không thu thập quy trình con trạng thái đó có thể là quá trình zombie khi nó chấm dứt.

Đây là mã đầy đủ đã kiểm tra và hoạt động như mong đợi.

#define READ 0 
#define WRITE 1 

pid_t 
popen2(const char *command, int *infp, int *outfp) 
{ 
    int p_stdin[2], p_stdout[2]; 
    pid_t pid; 

    if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0) 
     return -1; 

    pid = fork(); 

    if (pid < 0) 
     return pid; 
    else if (pid == 0) 
    { 
     dup2(p_stdin[READ], STDIN_FILENO); 
     dup2(p_stdout[WRITE], STDOUT_FILENO); 

    //close unuse descriptors on child process. 
    close(p_stdin[READ]); 
    close(p_stdin[WRITE]); 
    close(p_stdout[READ]); 
    close(p_stdout[WRITE]); 

     //can change to any exec* function family. 
     execl("/bin/bash", "bash", "-c", command, NULL); 
     perror("execl"); 
     exit(1); 
    } 

    // close unused descriptors on parent process. 
    close(p_stdin[READ]); 
    close(p_stdout[WRITE]); 

    if (infp == NULL) 
     close(p_stdin[WRITE]); 
    else 
     *infp = p_stdin[WRITE]; 

    if (outfp == NULL) 
     close(p_stdout[READ]); 
    else 
     *outfp = p_stdout[READ]; 

    return pid; 
} 

int 
pclose2(pid_t pid) { 
    int internal_stat; 
    waitpid(pid, &internal_stat, 0); 
    return WEXITSTATUS(internal_stat); 
} 
0

tôi chỉ đơn giản đưa vào (bash) kịch bản, trong dòng đầu tiên:

echo PID $$ 

sau đó tôi đọc pid, và sử dụng nó để làm một: kill (pid, 9)

+0

popen là cheesy để bắt đầu, đây là một giải pháp cheesy tốt đẹp cho vấn đề cheesy. – stu

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