Kiểm soát quá trình nào bị hủy bởi Ctrl + C


13

Tôi có một đĩa CD trực tiếp khởi động vào Linux và chạy một đoạn script Bash nhỏ. Kịch bản tìm kiếm và chạy chương trình thứ hai (thường là tệp nhị phân C ++ được biên dịch).

Bạn được cho là có thể hủy bỏ chương trình thứ hai bằng cách nhấn Ctrl+ C. Điều nên xảy ra là chương trình thứ hai tạm dừng và tập lệnh Bash tiếp tục chạy dọn dẹp. Điều thực sự xảy ra là cả ứng dụng chính và tập lệnh Bash đều chấm dứt. Đó là một vấn đề.

Vì vậy, tôi đã sử dụng trapnội dung để nói với Bash bỏ qua SIGINT. Và bây giờ Ctrl+ Cchấm dứt ứng dụng C ++, nhưng Bash vẫn tiếp tục chạy. Tuyệt quá.

Ồ vâng ... Đôi khi "ứng dụng thứ hai" là một tập lệnh Bash khác. Và trong trường hợp đó , Ctrl+ Cbây giờ không làm gì cả .

Rõ ràng sự hiểu biết của tôi về cách thức hoạt động của công cụ này là sai ... Làm cách nào để kiểm soát quá trình nào bị SIGINT khi người dùng nhấn Ctrl+ C? Tôi muốn hướng tín hiệu này đến một quy trình cụ thể .

Câu trả lời:


11

Sau nhiều, nhiều giờ tìm kiếm bộ mặt của Internet, tôi đã tìm được câu trả lời.

  1. Linux có khái niệm về một nhóm quy trình .

  2. Trình điều khiển TTY có một khái niệm về Nhóm quy trình tiền cảnh.

  3. Khi bạn nhấn Ctrl+ C, TTY sẽ gửi SIGINTđến mọi quy trình trong Nhóm quy trình tiền cảnh. (Xem thêm mục blog này .)

Đây là lý do tại sao cả nhị phân được biên dịch tập lệnh khởi chạy nó đều bị ghi đè. Thực tế tôi chỉ muốn ứng dụng chính nhận được tín hiệu này chứ không phải các tập lệnh khởi động.

Giải pháp bây giờ rất rõ ràng: Chúng ta cần đưa ứng dụng vào một nhóm quy trình mới và biến nó thành Nhóm quy trình tiền cảnh cho TTY này. Rõ ràng lệnh để làm điều đó là

setsid -c <applcation>

Và đó là tất cả. Bây giờ khi người dùng nhấn Ctrl+ C, SIGINT sẽ được gửi đến ứng dụng (và bất kỳ đứa trẻ nào nó có thể có) và không ai khác. Đó là những gì tôi muốn.

  • setsid bằng chính nó đặt ứng dụng vào một nhóm quy trình mới (thực sự là toàn bộ "phiên" mới, rõ ràng là một nhóm các nhóm quy trình).

  • Thêm -ccờ làm cho nhóm quy trình mới này trở thành nhóm quy trình "tiền cảnh" cho TTY hiện tại. (Tức là, nó nhận được SIGINTkhi bạn nhấn Ctrl+ C)

Tôi đã thấy rất nhiều thông tin mâu thuẫn về thời điểm Bash làm hoặc không chạy các quy trình trong một nhóm quy trình mới. (Đặc biệt, nó dường như là khác nhau cho vỏ "tương tác" và "không tương tác".) Tôi đã nhìn thấy gợi ý mà bạn có thể có thể được điều này để làm việc với ống lừa đảo thông minh ... Tôi không biết. Nhưng cách tiếp cận ở trên dường như làm việc cho tôi.


5
Bạn gần như đã có nó ... điều khiển công việc bị tắt theo mặc định khi chạy tập lệnh, nhưng bạn có thể kích hoạt nó với set -m. Nó sạch sẽ và đơn giản hơn một chút so với việc sử dụng setsidmỗi khi bạn chạy trẻ.
psusi

@psusi Cảm ơn vì tiền boa! Tôi chỉ cần chạy một đứa trẻ, vì vậy nó không phải là vấn đề lớn. Bây giờ tôi biết nơi để tìm trong hướng dẫn Bash mặc dù ...
Toán học,

Trớ trêu thay, tôi có một vấn đề ngược lại là tôi muốn phụ huynh bắt được sigint, nhưng không phải vì "mánh khóe thông minh". Ngoài ra nhóm tiến bộ -> nhóm quy trình
Andrew Domaszek 11/03/2015

2

Như tôi đã đề cập trong phần bình luận cho f01, bạn nên gửi SIGTERM cho tiến trình con. Dưới đây là một vài tập lệnh cho biết cách bẫy ^ C và gửi tín hiệu đến một tiến trình con.

Đầu tiên, phụ huynh.

tuyệt vời nhất

#!/bin/bash

# trap test
# Written by PM 2Ring 2014.10.23

myname=$(basename "$0")
child=sleeploop

set_trap()
{
    sig=$1
    msg="echo -e \"\n$myname received ^C, sending $sig to $child, $pid\""
    trap "$msg; kill -s $sig $pid" SIGINT
}
trap "echo \"bye from $myname\"" EXIT

echo "running $child..."
./$child 5  &
pid=$!

# set_trap SIGINT
set_trap SIGTERM
echo "$child pid = $pid"

wait $pid
echo "$myname finished waiting"

Và bây giờ, đứa trẻ.

áo ngủ

#!/bin/bash

# child script for traptest
# Written by PM 2Ring 2014.10.23

myname=$(basename "$0")
delay="$1"

set_trap()
{
    sig=$1
    trap "echo -e '\n$myname received $sig signal';exit 0" $sig
}

trap "echo \"bye from $myname\"" EXIT
set_trap SIGTERM
set_trap SIGINT

#Select sleep mode
if false
then
    echo "Using foreground sleep"
    Sleep()
    {
        sleep $delay
    }
else
    echo "Using background sleep"
    Sleep()
    {
        sleep "$delay" &
        wait $!
    }
fi

#Time to snooze :)
for ((i=0; i<5; i++));
do
    echo "$i: sleeping for $delay"
    Sleep
done

echo "$myname terminated normally"

Nếu traptest gửi SIGTERM, mọi thứ sẽ hoạt động tốt, nhưng nếu traptest gửi SIGINT thì bedloop không bao giờ nhìn thấy nó.

Nếu bedloop bẫy SIGTERM và chế độ ngủ là tiền cảnh, thì nó không thể phản hồi tín hiệu cho đến khi nó thức dậy từ giấc ngủ hiện tại. Nhưng nếu chế độ ngủ là nền, nó sẽ phản hồi ngay lập tức.


Cảm ơn bạn vì ví dụ tuyệt vời này, nó đã giúp tôi rất nhiều trong việc hiểu làm thế nào tôi có thể cải thiện kịch bản của mình :)
TabeaKischka

2

Trong kịch bản bash khởi đầu của bạn.

  • theo dõi PID của chương trình thứ hai

  • bắt SIGINT

  • khi bạn đã bắt được SIGINT, hãy gửi SIGINT đến chương trình thứ hai PID


1
Điều đó có thể không giúp đỡ. Nếu bạn bẫy SIGINT trong tập lệnh gốc thì tập lệnh con sẽ không thể nhận SIGINT, cho dù bạn gửi nó từ cha mẹ hoặc từ trình bao khác. Nhưng điều đó không tệ như âm thanh của nó, bởi vì bạn có thể gửi SIGTERM cho đứa trẻ, đây rõ ràng là tín hiệu ưa thích để sử dụng.
PM 2Ring

1
Tại sao SIGTERM "được ưa thích"?
Toán học,

Chúng gần như tương tự nhau. SIGINT là tín hiệu được gửi bởi thiết bị đầu cuối / người dùng kiểm soát, ví dụ Ctrl + C. SIGTERM là những gì bạn cũng có thể gửi nếu bạn muốn quá trình chấm dứt. Thêm suy nghĩ ở đây en.wikipedia.org/wiki/Unix_signal
f01
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.