Ngăn chặn việc truyền SIGINT sang quy trình phụ huynh


10

Xem xét một kịch bản trong đó chương trình Parent (có thể là chương trình C ++ hoặc Shell Script) thực thi tập lệnh shell con, khi chúng ta nhấn Control + C (hoặc bất kỳ ký tự nào được định cấu hình là ký tự INTR) trong khi Script con Shell đang thực thi, một SIGINT được gửi đến tất cả các quy trình trong nhóm quy trình nền trước. Điều này bao gồm quá trình cha mẹ.

Nguồn: POSIX.1-2008 XBD phần 11.1.9

Có cách nào để ghi đè hành vi mặc định này không? Rằng quá trình TRẺ một mình xử lý TÍN HIỆU mà không truyền bá cho cha mẹ?

Tham khảo: Bài viết về Stack Overflow - Quá trình phụ huynh không hoàn thành khi trẻ bị gián đoạn (TRAP INT)

Câu trả lời:


11

(Lấy cảm hứng từ câu trả lời của Gilles)

Với ISIGcờ được đặt, cách duy nhất để Childtập lệnh nhận được SIGINTmà không có cha mẹ nhận được SIGINTlà tập lệnh nằm trong nhóm quy trình riêng. Điều này có thể được thực hiện với các set -mtùy chọn.

Nếu bạn bật -mtùy chọn trong Childtập lệnh shell, nó sẽ thực hiện kiểm soát công việc mà không cần tương tác. Điều này sẽ khiến nó chạy các thứ trong một nhóm quy trình riêng biệt, ngăn phụ huynh nhận được SIGINTkhi INTRký tự được đọc.

Dưới đây là mô tả POSIX của -mtùy chọn :

-mTùy chọn này sẽ được hỗ trợ nếu việc triển khai hỗ trợ tùy chọn Tiện ích di động người dùng. Tất cả các công việc sẽ được chạy trong các nhóm quy trình riêng của họ. Ngay trước khi shell phát ra lời nhắc sau khi hoàn thành công việc nền, một thông báo báo cáo trạng thái thoát của công việc nền sẽ được ghi vào lỗi tiêu chuẩn. Nếu một công việc nền trước dừng lại, trình bao sẽ viết một thông báo lỗi tiêu chuẩn cho hiệu ứng đó, được định dạng như mô tả bởi tiện ích công việc. Ngoài ra, nếu một công việc thay đổi trạng thái khác với thoát (ví dụ: nếu nó dừng cho đầu vào hoặc đầu ra hoặc bị dừng bởi tín hiệu SIGSTOP), trình bao sẽ viết một thông báo tương tự ngay trước khi viết lời nhắc tiếp theo. Tùy chọn này được bật theo mặc định cho các vỏ tương tác.

Các -mtùy chọn tương tự như -i, nhưng nó không làm thay đổi hành vi của vỏ gần càng nhiều càng -ikhông.

Thí dụ:

  • các Parentkịch bản:

    #!/bin/sh
    
    trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT
    
    echo "PARENT: pid=$$"
    echo "PARENT: Spawning child..."
    ./Child
    echo "PARENT: child returned"
    echo "PARENT: exiting normally"
    
  • các Childkịch bản:

    #!/bin/sh -m
    #         ^^        
    # notice the -m option above!
    
    trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT
    
    echo "CHILD: pid=$$"
    echo "CHILD: hit enter to exit"
    read foo
    echo "CHILD: exiting normally"
    

Đây là những gì xảy ra khi bạn nhấn Control+ Ctrong khi Childchờ đầu vào:

$ ./Parent
PARENT: pid=12233
PARENT: Spawning child...
CHILD: pid=12234
CHILD: hit enter to exit
^CCHILD: caught SIGINT; exiting
PARENT: child returned
PARENT: exiting normally

Lưu ý cách SIGINTxử lý của cha mẹ không bao giờ được thực thi.

Ngoài ra, nếu bạn muốn sửa đổi Parentthay vì Child, bạn có thể làm điều này:

  • các Parentkịch bản:

    #!/bin/sh
    
    trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT
    
    echo "PARENT: pid=$$"
    echo "PARENT: Spawning child..."
    sh -m ./Child  # or 'sh -m -c ./Child' if Child isn't a shell script
    echo "PARENT: child returned"
    echo "PARENT: exiting normally"
    
  • các Childkịch bản (bình thường, không có nhu cầu -m):

    #!/bin/sh
    
    trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT
    
    echo "CHILD: pid=$$"
    echo "CHILD: hit enter to exit"
    read foo
    echo "CHILD: exiting normally"
    

Ý tưởng thay thế

  1. Sửa đổi các quy trình khác trong nhóm quy trình tiền cảnh để bỏ qua SIGINTtrong thời gian Child. Điều này không giải quyết câu hỏi của bạn, nhưng nó có thể giúp bạn có được những gì bạn muốn.
  2. Sửa đổi Childthành:
    1. Sử dụng stty -gđể sao lưu các thiết lập thiết bị đầu cuối hiện tại.
    2. Chạy stty -isigđể không tạo ra tín hiệu với INTR, QUITSUSPký tự.
    3. Trong nền, đọc đầu vào đầu cuối và tự gửi tín hiệu cho phù hợp (ví dụ: chạy kill -QUIT 0khi Control+ \được đọc, kill -INT $$khi Control+ Cđược đọc). Điều này không phải là nhỏ, và có thể không làm cho nó hoạt động trơn tru nếu Childkịch bản hoặc bất cứ thứ gì nó chạy có nghĩa là tương tác.
    4. Khôi phục cài đặt thiết bị đầu cuối trước khi thoát (lý tưởng nhất là từ bẫy EXIT).
  3. Tương tự như số 2 ngoại trừ thay vì chạy stty -isig, hãy đợi người dùng nhấn Enterhoặc một số phím không đặc biệt khác trước khi giết Child.
  4. Viết setpgidtiện ích của riêng bạn bằng C, Python, Perl, v.v. mà bạn có thể sử dụng để gọi setpgid(). Đây là một triển khai C thô:

    #define _XOPEN_SOURCE 700
    #include <unistd.h>
    #include <signal.h>
    
    int
    main(int argc, char *argv[])
    {
        // todo: add error checking
        void (*backup)(int);
        setpgid(0, 0);
        backup = signal(SIGTTOU, SIG_IGN);
        tcsetpgrp(0, getpid());
        signal(SIGTTOU, backup);
        execvp(argv[1], argv + 1);
        return 1;
    }
    

    Ví dụ sử dụng từ Child:

    #!/bin/sh
    
    [ "${DID_SETPGID}" = true ] || {
        # restart self after calling setpgid(0, 0)
        exec env DID_SETPGID=true setpgid "$0" "$@"
        # exec failed if control reached this point
        exit 1
    }
    unset DID_SETPGID
    
    # do stuff here
    

4

Như chương bạn trích dẫn từ POSIX giải thích, SIGINT được gửi đến toàn bộ nhóm quy trình tiền cảnh. Do đó, để tránh giết cha mẹ chương trình, hãy sắp xếp để nó chạy trong nhóm quy trình riêng của nó.

Shell không cấp quyền truy cập setpgrpthông qua cấu trúc dựng sẵn hoặc cú pháp, nhưng có một cách gián tiếp để đạt được nó, đó là chạy shell một cách tương tác. (Cám ơn Stéphane Gimenez vì mánh khóe.)

ksh -ic '
  … the part that needs to be interruptible without bothering the parent …
'

+1 cho ý tưởng thông minh, mặc dù hãy cẩn thận rằng hành vi của vỏ thay đổi khi nó là vỏ tương tác (ít nhất là vỏ POSIX; tôi không quen với các chi tiết về ksh). Ví dụ: ${ENV}có nguồn gốc, shell sẽ không thoát ngay lập tức khi gặp lỗi SIGQUITSIGTERMbị bỏ qua.
Richard Hansen

1

Chà, từ câu hỏi Stack Overflow mà bạn đã tham chiếu, nó nói rõ rằng cha mẹ cần được cấu hình để xử lý tín hiệu.

Thứ hai, tham chiếu POSIX nói rõ rằng "Nếu ISIG được đặt, ký tự INTR sẽ bị loại bỏ khi xử lý".

Vì vậy, đó là hai lựa chọn. Thứ ba sẽ là điều hành đứa trẻ trong nhóm quy trình riêng của mình.


Cám ơn phản hồi của bạn. Tôi cũng cần tìm ra một cách để người dùng thoát khỏi tập lệnh. Có cách nào để thiết lập ISIG và đồng thời cho phép một số tổ hợp Khóa CONTRL (CTRL-Q có thể) thoát ra khỏi tập lệnh shell mà không gửi tín hiệu cho phụ huynh và tương tự không? Ngoài ra, bạn có thể vui lòng cho tôi biết làm thế nào tôi có thể điều hành đứa trẻ trong một nhóm quy trình khác với cha mẹ của nó không?
Guddu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.