Tại sao Ctrl-C không hoạt động?


9

Tôi vừa đánh Ctrlchai lần vào vỏ của mình trong nỗ lực ngăn chặn một quá trình đang mất nhiều thời gian để hoàn thành.

^C đã lặp lại hai lần, nhưng quá trình chỉ tiếp tục.

Tại sao không Ctrlcbỏ quá trình như bình thường?


4
Giải pháp của tôi cho các chương trình gây phiền nhiễu mà không muốn chết thường là đình chỉ chúng bằng CTRL + Z sau đó kill -9 %để giết nó. Tín hiệu 9 không thể bỏ qua, cũng không thể bỏ qua tín hiệu. Trình tự bàn phím CTRL + Z có thể bị bỏ qua trong lý thuyết - nhưng thực tế không phải vậy.
David Sainty

@DavidScellence Tín hiệu tạm dừng có thể bị bỏ qua (hoặc, ít nhất là không dừng lại). Ví dụ : perl -E '$SIG{TSTP} = sub { say "ha ha" }; sleep 1 while 1'. Có lẽ bạn đang nghĩ về SIGSTOP, đó là một tín hiệu khác.
derobert

À, đúng rồi đấy :)
David Sainty

Câu trả lời:


13

Các quy trình có thể chọn:

  • bỏ qua tín hiệu SIGINT thường được gửi khi nhấn Ctrl-C(như với trap '' INTvỏ) hoặc có trình xử lý riêng để quyết định không chấm dứt (hoặc không kết thúc kịp thời).
  • nói với thiết bị đầu cuối rằng ký tự khiến SIGINT được gửi đến công việc nền trước là một cái gì đó khác (như với stty int '^K'vỏ)
  • yêu cầu thiết bị đầu cuối không gửi bất kỳ tín hiệu nào (như với stty -isigvỏ).

Hoặc, chúng có thể không bị gián đoạn, như khi ở giữa một cuộc gọi hệ thống không thể bị gián đoạn.

Trên Linux (với một hạt nhân tương đối gần đây), bạn có thể biết liệu một quy trình có bỏ qua và / hoặc xử lý SIGINT hay không bằng cách xem kết quả đầu ra của

$ kill -l INT
2
$ grep Sig "/proc/$pid/status"
SigQ:   0/63858
SigPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000002
SigCgt: 0000000000000000

SIGINT là 2. Bit thứ hai của SigIgn ở trên là 1, có nghĩa là SIGINT bị bỏ qua.

Bạn có thể tự động hóa điều đó với:

$ SIG=$(kill -l INT) perl -lane 'print $1 if $F[0] =~ /^Sig(...):/ && 
    $F[1] & (1<<($ENV{SIG}-1))' < "/proc/$pid/status"
Ign

Để kiểm tra intrký tự hiện tại là gì hoặc nếu isigđược bật cho một thiết bị đầu cuối cụ thể:

$ stty -a < /dev/pts/0
[...] intr = ^C [...] isig

(phía trên intrký tự là ^C(ký tự thường được gửi bởi thiết bị đầu cuối (trình giả lập) của bạn khi nhấn CTRL-Cvà tín hiệu đầu vào không bị tắt.

$ stty -a < /dev/pts/1
[...] intr = ^K [...] -isig

( intrký tự được ^Kisigbị vô hiệu hóa /dev/pts/1).

Để hoàn thiện, có hai cách khác mà một quy trình có thể làm một cái gì đó để ngừng nhận SIGINT mặc dù đó không phải là thứ bạn thường thấy.

Khi đó Ctrl+C, tín hiệu SIGINT được gửi đến tất cả các quy trình trong nhóm quy trình nền trước của thiết bị đầu cuối . Nó thường vỏ rằng các quá trình diễn ra trong nhóm quá trình (ánh xạ tới vỏ công việc ) và nói với các thiết bị đầu cuối là foreground một.

Bây giờ một quá trình có thể:

  • Rời khỏi nhóm quá trình của nó. Nếu nó di chuyển sang nhóm quy trình khác (bất kỳ nhóm quy trình nào ngoại trừ nhóm quy trình trước ), thì nó sẽ không còn nhận được SIGINT theo Ctrl-C(cũng như các tín hiệu liên quan đến bàn phím khác như SIGTSTP, SIGQUIT). Tuy nhiên, nó có thể bị treo nếu nó cố đọc (có thể viết tùy thuộc vào cài đặt thiết bị đầu cuối) từ thiết bị đầu cuối (như các quá trình nền làm).

    Ví dụ:

    perl -MPOSIX -e 'setpgid(0,getppid) or die "$!"; sleep 10'

    không thể bị gián đoạn với Ctrl-C. Ở trên perlsẽ cố gắng tham gia nhóm quy trình có ID giống với ID quy trình mẹ của nó. Nói chung, không có gì đảm bảo rằng có một nhóm quy trình như vậy với id đó. Nhưng ở đây, trong trường hợp perllệnh đó tự chạy theo dấu nhắc của trình bao tương tác, ppid sẽ là quy trình của trình bao và trình bao thường sẽ được bắt đầu trong nhóm quy trình riêng của nó.

    Nếu lệnh chưa phải là một người lãnh đạo nhóm quy trình (người lãnh đạo của nhóm quy trình tiền cảnh đó), thì nó bắt đầu một nhóm quy trình mới sẽ có tác dụng tương tự.

    Ví dụ, tùy thuộc vào vỏ,

    $ ps -j >&2 | perl -MPOSIX -e 'setpgid(0,0) or die "$!"; sleep 10'
      PID  PGID   SID TTY          TIME CMD
    21435 21435 21435 pts/12   00:00:00 zsh
    21441 21441 21435 pts/12   00:00:00 ps
    21442 21441 21435 pts/12   00:00:00 perl
    

    sẽ có tác dụng tương tự. psperlđược bắt đầu trong nhóm quy trình tiền cảnh, nhưng trên hầu hết các shell, pssẽ là người lãnh đạo của nhóm đó (như được thấy trong psđầu ra ở trên nơi pgid của cả hai psperllà pid của ps), vì vậy perlcó thể bắt đầu nhóm quy trình riêng của mình.

  • Hoặc nó có thể thay đổi nhóm quá trình tiền cảnh. Về cơ bản hãy nói với thiết bị tty để gửi SIGINT cho một số nhóm quy trình khác khiCtrl+C

    perl -MPOSIX -e 'tcsetpgrp (0, getppid) hoặc chết $!; ngủ 5 '

    Ở đó, perlvẫn nằm trong cùng một nhóm quy trình nhưng thay vào đó là nói với thiết bị đầu cuối rằng nhóm quy trình tiền cảnh là nhóm có ID giống với ID tiến trình mẹ của nó (xem ghi chú ở trên về điều đó).


1
Một ví dụ điển hình về cuộc gọi liên tục là truy cập vào thiết bị phần cứng không phản hồi. Ví dụ: nếu bạn cố gắng sử dụng hdparmhoặc smartctltrên một đĩa cứng bị lỗi không phản hồi, chúng sẽ bị treo vĩnh viễn và bạn không thể giết chúng bằng CTRL + C. Bạn có thể cho biết nếu một quá trình là trong giấc ngủ liên tục bằng cách nhìn vào cột stat của ps auxhoặc tại cột S top/ htop- Dphương tiện ngủ liên tục. Đây không hẳn là một điều xấu, nó chỉ có thể có nghĩa là quá trình này đang thực hiện rất nhiều IO.
Martin von Wittich
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.