Tôi nên gửi tín hiệu đến các quy trình tắt một cách duyên dáng theo thứ tự nào?


88

Trong một bình luận về câu trả lời này của một câu hỏi khác , người bình luận nói:

không sử dụng kill -9 trừ khi thực sự cần thiết! SIGKILL không thể bị mắc kẹt nên chương trình bị giết không thể chạy bất kỳ quy trình tắt máy nào, ví dụ như xóa các tệp tạm thời. Đầu tiên hãy thử HUP (1), sau đó INT (2), sau đó QUIT (3)

Tôi đồng ý về nguyên tắc SIGKILL, nhưng phần còn lại là tin tức đối với tôi. Do tín hiệu mặc định được gửi bởi killSIGTERM, tôi cho rằng đó là tín hiệu được mong đợi phổ biến nhất cho việc tắt một cách tùy ý một quy trình tùy ý. Ngoài ra, tôi đã thấy SIGHUPđược sử dụng cho các lý do không kết thúc, chẳng hạn như yêu cầu daemon "đọc lại tệp cấu hình của bạn." Và có vẻ như đối với tôi rằng SIGINT(cùng một ngắt mà bạn thường nhận được với Ctrl-C, phải không?) Không được hỗ trợ rộng rãi như nó phải được, hoặc kết thúc một cách vô duyên.

Cho rằng đó SIGKILLlà phương sách cuối cùng - Bạn nên gửi tín hiệu nào và theo thứ tự nào đến một quy trình tùy ý, để tắt nó một cách duyên dáng nhất có thể?

Vui lòng chứng minh câu trả lời của bạn bằng các dữ kiện hỗ trợ (ngoài sở thích hoặc quan điểm cá nhân) hoặc tài liệu tham khảo, nếu bạn có thể.

Lưu ý: Tôi đặc biệt quan tâm đến các phương pháp hay nhất bao gồm việc xem xét bash / Cygwin.

Chỉnh sửa: Cho đến nay, dường như không ai đề cập đến INT hoặc QUIT, và có giới hạn đề cập đến HUP. Có lý do gì để đưa những thứ này vào một quy trình giết người có trật tự không?


4
Nếu bạn phải dùng đến SIGKILL để thực sự giết một quy trình, tôi sẽ coi đó là một lỗi trong chương trình.
sigjuice

Câu trả lời:


114

SIGTERM yêu cầu một ứng dụng chấm dứt. Các tín hiệu khác cho ứng dụng biết những điều khác không liên quan đến việc tắt máy nhưng đôi khi có thể có cùng kết quả. Đừng sử dụng chúng. Nếu bạn muốn một ứng dụng tắt, hãy yêu cầu nó. Đừng đưa ra những tín hiệu sai lệch.

Một số người tin rằng cách tiêu chuẩn thông minh để kết thúc một quy trình là gửi cho nó một loạt các tín hiệu, chẳng hạn như HUP, INT, TERM và cuối cùng là KILL. Chuyện này thật vớ vẩn. Tín hiệu phù hợp để kết thúc là SIGTERM và nếu SIGTERM không kết thúc quá trình ngay lập tức, như bạn có thể muốn, thì đó là do ứng dụng đã chọn xử lý tín hiệu. Có nghĩa là nó có lý do chính đáng để không chấm dứt ngay lập tức: Đó là công việc dọn dẹp phải làm. Nếu bạn làm gián đoạn công việc dọn dẹp đó với các tín hiệu khác, sẽ không cho biết dữ liệu nào từ bộ nhớ mà nó chưa được lưu vào đĩa, ứng dụng khách nào bị treo hoặc liệu bạn có làm gián đoạn nó "giữa chừng", đó là cách dữ liệu bị hỏng.

Để biết thêm thông tin về ý nghĩa thực sự của các tín hiệu, hãy xem biểu tượng (2). Đừng nhầm lẫn giữa "Hành động mặc định" với "Mô tả", chúng không giống nhau.

SIGINT được sử dụng để báo hiệu "ngắt bàn phím" tương tác của quá trình. Một số chương trình có thể xử lý tình huống theo một cách đặc biệt cho mục đích của người dùng thiết bị đầu cuối.

SIGHUP được sử dụng để báo hiệu rằng thiết bị đầu cuối đã biến mất và không còn xem xét quá trình. Đó là tất cả. Một số quy trình chọn tắt để phản hồi, nói chung là do hoạt động của chúng không có ý nghĩa gì nếu không có thiết bị đầu cuối, một số chọn thực hiện những việc khác như kiểm tra lại tệp cấu hình.

SIGKILL được sử dụng để xóa tiến trình khỏi hạt nhân. Nó đặc biệt ở chỗ, nó không thực sự là một tín hiệu cho tiến trình mà là do hạt nhân trực tiếp giải thích.

Đừng gửi SIGKILL. Chắc chắn SIGKILL sẽ không bao giờ được gửi bằng script. Nếu ứng dụng xử lý SIGTERM, có thể mất một giây để dọn dẹp, có thể mất một phút, có thể mất một giờ . Tùy thuộc vào những gì ứng dụng phải hoàn thành trước khi sẵn sàng kết thúc. Bất kỳ logic nào " giả định " trình tự dọn dẹp của một ứng dụng đã diễn ra đủ lâu và cần phải tắt hoặc SIGKILLed sau X giây là sai hoàn toàn .

Lý do duy nhất tại sao một ứng dụng sẽ cần một SIGKILL để kết thúc, là nếu một cái gì đó bị lỗi trong trình tự dọn dẹp của nó. Trong trường hợp đó, bạn có thể mở một thiết bị đầu cuối và SIGKILL nó theo cách thủ công. Bên cạnh đó, lý do duy nhất khác khiến bạn SIGKILL một thứ gì đó là vì bạn MUỐN ngăn nó tự làm sạch.

Mặc dù một nửa thế giới gửi SIGKILL một cách mù quáng sau 5 giây, đó vẫn là một việc làm sai lầm khủng khiếp.


13
Bạn nói đúng rằng có rất nhiều việc sử dụng sai SIGKILL ngoài kia. Nhưng có thời gian và địa điểm để sử dụng nó, ngay cả từ một tập lệnh. Nhiều, nhiều ứng dụng bẫy SIGTERM và thoát ra một cách duyên dáng trong vòng chưa đầy một giây hoặc chỉ trong vòng vài giây, và một trong số những ứng dụng đó vẫn chạy 30 giây sau đó là do nó bị chèn.
dwc

4
@dwc: Hãy thử để nó chạy một lần trong một giờ. Nếu nó không chết thì nó bị "nêm" và hoặc sửa nó, hoặc lười biếng và trong tương lai SIGKILL nó sau một thời gian. Hãy lưu ý rằng có thể bạn đang làm hỏng nội dung và nhớ rằng đây KHÔNG phải là điều bạn nên làm "theo mặc định".
lhunath

2
@lhunath: Mong bạn không phiền, tôi đã sắp xếp lại các đoạn văn của bạn để làm cho câu trả lời trực tiếp và rõ ràng hơn từ câu hỏi. Lời rant chống SIGKILL là công cụ tốt, nhưng là điểm phụ. Cảm ơn một lần nữa cho một câu trả lời tuyệt vời và giáo dục.
hệ thống TẠM DỪNG

8
Đừng gửi SIGKILL. Không bao giờ. Chỉ đơn giản là sai. Có thật không? Ngay cả khi hệ thống của bạn đã bị cháy nhờ vòng lặp vô hạn. Chúc may mắn. -1
konsolebox

//, Ủng hộ cho điều này là vô lý.
Nathan Basanese

17

Câu trả lời ngắn : Gửi SIGTERM, 30 giây sau SIGKILL,. Tức là gửi đi SIGTERM, đợi một chút (tùy từng chương trình có thể khác nhau, bạn có thể hiểu rõ hệ thống của mình hơn nhưng 5 đến 30 giây là đủ. Khi tắt máy, bạn có thể thấy máy tự động chờ đến 1'30 giây). Rốt cuộc tại sao lại vội vàng?), Sau đó gửi đi SIGKILL.

Hợp lý trả lời : SIGTERM, SIGINT, SIGKILL Đây là quá đủ. Quá trình này rất có thể sẽ kết thúc trước đó SIGKILL.

Dài trả lời : SIGTERM, SIGINT, SIGQUIT, SIGABRT,SIGKILL

Điều này là không cần thiết, nhưng ít nhất bạn không làm sai quy trình liên quan đến thông điệp của bạn. Tất cả những tín hiệu này có nghĩa là bạn muốn quá trình dừng những gì nó đang làm và thoát ra.

Cho dù bạn chọn câu trả lời nào từ lời giải thích này, hãy ghi nhớ điều đó!

Nếu bạn gửi một tín hiệu có nghĩa là một cái gì đó khác, quy trình có thể xử lý nó theo những cách rất khác nhau (một mặt). Mặt khác, nếu tiến trình không xử lý tín hiệu, thì cuối cùng bạn gửi gì cũng không quan trọng, tiến trình sẽ thoát bằng cách nào (tất nhiên khi hành động mặc định là kết thúc).

Vì vậy, bạn phải suy nghĩ như chính mình với tư cách là một lập trình viên. Bạn sẽ viết mã một trình xử lý hàm, chẳng hạn, SIGHUPđể thoát khỏi một chương trình kết nối với thứ gì đó, hay bạn sẽ lặp lại nó để thử kết nối lại? Đó là câu hỏi chính ở đây! Đó là lý do tại sao điều quan trọng là chỉ gửi tín hiệu có nghĩa là những gì bạn dự định.

Câu trả lời dài gần như ngu ngốc :

Bảng dưới đây chứa các tín hiệu liên quan và các hành động mặc định trong trường hợp chương trình không xử lý chúng.

Tôi đã đặt hàng chúng theo thứ tự mà tôi đề nghị sử dụng (BTW, tôi đề nghị bạn sử dụng câu trả lời hợp lý , không phải câu trả lời này ở đây), nếu bạn thực sự cần thử tất cả chúng (sẽ rất vui khi nói rằng bảng được sắp xếp theo thứ tự sự phá hủy chúng có thể gây ra, nhưng điều đó không hoàn toàn đúng).

Các tín hiệu có dấu hoa thị (*) KHÔNG được khuyến khích. Điều quan trọng là bạn có thể không bao giờ biết nó được lập trình để làm gì. Đặc biệt SIGUSR! Nó có thể bắt đầu apocalipse (đó là một tín hiệu miễn phí cho một lập trình viên làm bất cứ điều gì anh ấy / cô ấy muốn!). Nhưng, nếu không được xử lý HOẶC trong trường hợp không chắc nó được xử lý để chấm dứt, chương trình sẽ chấm dứt.

Trong bảng, các tín hiệu với các tùy chọn mặc định để kết thúc và tạo ra một kết xuất lõi được để lại cuối cùng, ngay trước đó SIGKILL.

Signal     Value     Action   Comment
----------------------------------------------------------------------
SIGTERM      15       Term    Termination signal
SIGINT        2       Term    Famous CONTROL+C interrupt from keyboard
SIGHUP        1       Term    Disconnected terminal or parent died
SIGPIPE      13       Term    Broken pipe
SIGALRM(*)   14       Term    Timer signal from alarm
SIGUSR2(*)   12       Term    User-defined signal 2
SIGUSR1(*)   10       Term    User-defined signal 1
SIGQUIT       3       Core    CONTRL+\ or quit from keyboard
SIGABRT       6       Core    Abort signal from abort(3)
SIGSEGV      11       Core    Invalid memory reference
SIGILL        4       Core    Illegal Instruction
SIGFPE        8       Core    Floating point exception
SIGKILL       9       Term    Kill signal

Sau đó, tôi sẽ đề nghị cho điều này câu trả lời dài gần ngu ngốc : SIGTERM, SIGINT, SIGHUP, SIGPIPE, SIGQUIT, SIGABRT,SIGKILL

Và cuối cùng,

Câu trả lời chắc chắn là ngu ngốc .

Đừng thử điều này ở nhà.

SIGTERM, SIGINT, SIGHUP, SIGPIPE, SIGALRM, SIGUSR2, SIGUSR1, SIGQUIT, SIGABRT, SIGSEGV, SIGILL, SIGFPEVà nếu không có gì làm việc, SIGKILL.

SIGUSR2nên được thử trước SIGUSR1vì chúng tôi tốt hơn nếu chương trình không xử lý tín hiệu. Và nhiều khả năng nó sẽ xử lý được SIGUSR1nếu nó chỉ xử lý một trong số chúng.

BTW, the KILL : không sai khi gửi SIGKILLđến một quy trình, như câu trả lời khác đã nêu. Hãy nghĩ xem điều gì sẽ xảy ra khi bạn gửi một shutdownlệnh? Nó sẽ cố gắng SIGTERMSIGKILLduy nhất. Bạn nghĩ tại sao lại như vậy? Và tại sao bạn cần bất kỳ tín hiệu nào khác, nếu shutdownlệnh chỉ sử dụng hai tín hiệu này?


Bây giờ, trở lại câu trả lời dài , đây là một câu trả lời hay:

for SIG in 15 2 3 6 9 ; do echo $SIG ; echo kill -$SIG $PID || break ; sleep 30 ; done

Nó ngủ trong 30 giây giữa các tín hiệu. Tại sao bạn lại cần một tấm lót ? ;)

Ngoài ra, khuyến nghị: hãy thử nó chỉ với các tín hiệu 15 2 9từ câu trả lời hợp lý .

an toàn : loại bỏ thứ hai echokhi bạn đã sẵn sàng để đi. Tôi gọi nó là của tôi dry-runcho những người trực tuyến . Luôn sử dụng nó để kiểm tra.


Viết kịch bản một cách đáng tiếc

Trên thực tế, tôi bị hấp dẫn bởi câu hỏi này nên tôi quyết định tạo một kịch bản nhỏ để làm điều đó. Vui lòng tải xuống (sao chép) nó tại đây:

Liên kết GitHub đến kho lưu trữ Killgraceently


8

Thông thường bạn sẽ gửi SIGTERM, mặc định là giết. Đó là mặc định vì một lý do. Chỉ khi một chương trình không tắt trong một khoảng thời gian hợp lý, bạn mới nên dùng đến SIGKILL. Nhưng lưu ý rằng với SIGKILLchương trình không có khả năng dọn dẹp mọi thứ vì dữ liệu có thể bị hỏng.

Đối với SIGHUP, HUPviết tắt của "treo lên" và lịch sử có nghĩa là modem đã ngắt kết nối. Về cơ bản nó tương đương với SIGTERM. Lý do mà daemon đôi khi sử dụng SIGHUPđể khởi động lại hoặc tải lại cấu hình là daemon tách khỏi bất kỳ thiết bị đầu cuối điều khiển nào vì daemon không cần những thứ đó và do đó sẽ không bao giờ nhận được SIGHUP, vì vậy tín hiệu đó được coi là "giải phóng" để sử dụng chung. Không phải tất cả các daemon đều sử dụng cái này để tải lại! Hành động mặc định cho SIGHUP là chấm dứt và nhiều daemon hoạt động theo cách đó! Vì vậy, bạn không thể mù quáng gửi SIGHUPcác daemon và mong đợi chúng sống sót.

Chỉnh sửa: SIGINT có thể không thích hợp để kết thúc một quá trình, vì nó thường được gắn với ^Choặc bất kỳ cài đặt đầu cuối nào là để ngắt một chương trình. Nhiều chương trình nắm bắt điều này cho các mục đích riêng của họ, vì vậy nó đủ phổ biến để nó không hoạt động. SIGQUITthường có mặc định là tạo một kết xuất lõi và trừ khi bạn muốn các tệp lõi nằm xung quanh thì nó cũng không phải là một ứng cử viên tốt.

Tóm tắt: nếu bạn gửi SIGTERMvà chương trình không chết trong khung thời gian của bạn thì hãy gửi nó SIGKILL.


4
Lưu ý rằng việc theo dõi nó với SIGKILL chỉ nên được thực hiện trong các tình huống mà việc tắt ngay lập tức là ưu tiên cao hơn việc ngăn ngừa mất dữ liệu / hỏng dữ liệu.
thomasrutter

@dwc Tôi không hiểu điểm sau trong câu trả lời của bạn. bạn có thể vui lòng giúp đỡ "Lý do mà đôi khi daemon sử dụng SIGHUP để khởi động lại hoặc tải lại cấu hình là các daemon tách khỏi bất kỳ thiết bị đầu cuối điều khiển nào và do đó sẽ không bao giờ nhận được SIGTERM, vì vậy tín hiệu đó được coi là" giải phóng "để sử dụng chung."
Jack

3
@Jack Hãy để tôi thử: SIGHUP là tín hiệu "gác máy" cho biết một quá trình rằng thiết bị đầu cuối đã bị ngắt kết nối. Vì daemon chạy trong nền nên chúng không cần thiết bị đầu cuối. Điều đó có nghĩa là tín hiệu "gác máy" không liên quan đến daemon. Họ sẽ không bao giờ nhận được nó từ ngắt kết nối đầu cuối, vì họ không có thiết bị đầu cuối được kết nối ngay từ đầu. Và vì dù sao thì tín hiệu cũng được định nghĩa, mặc dù chúng không cần nó cho mục đích ban đầu, nhiều trình daemon sử dụng nó thay thế cho một mục đích khác, chẳng hạn như đọc lại các tệp cấu hình của họ.
hệ thống TẠM DỪNG

Cảm ơn hệ thống TẠM DỪNG. điều này là hữu ích.
Jack

6

SIGTERMthực sự có nghĩa là gửi cho ứng dụng một thông điệp: " bạn có thể tử tế như vậy không và tự tử ". Nó có thể bị bẫy và xử lý bởi ứng dụng để chạy mã dọn dẹp và tắt máy.

SIGKILLkhông thể bị mắc kẹt bởi ứng dụng. Ứng dụng bị giết bởi hệ điều hành mà không có bất kỳ cơ hội để dọn dẹp.

Thông thường, gửi SIGTERMtrước, ngủ một lúc, sau đó gửi SIGKILL.


Tôi bỏ phiếu giả sử sẽ hiệu quả hơn một chút so với ngủ (trước SIGKILL)
Ohad Schneider

@OhadSchneider thì sẽ có, nhưng điều đó sẽ yêu cầu một thứ gì đó hơn là lệnh bash đơn giản.
vartec

Vâng, tôi đoán bạn sẽ cần lặp lại trong khi quá trình vẫn còn hoạt động bằng cách sử dụng một cái gì đó như sau: stackoverflow.com/a/15774758/67824 .
Ohad Schneider

5
  • SIGTERM tương đương với "nhấp vào 'X'" trong cửa sổ.
  • SIGTERM là thứ mà Linux sử dụng đầu tiên, khi nó ngừng hoạt động.

Đó là những gì tôi muốn biết. +1. Cảm ơn.
Luc

6
"SIGTERM tương đương với việc" nhấp vào 'X' "trong cửa sổ" Không, không phải vậy, bởi vì bất kỳ ứng dụng nào cũng có thể dễ dàng mở bất kỳ số lượng cửa sổ (tài liệu và công cụ, ví dụ) nào, chưa nói đến hộp thoại và có thể không thậm chí phản hồi lệnh đóng cửa sổ cuối cùng giống như đối với lệnh thoát (Tôi không thể nghĩ ra bất kỳ ví dụ rõ ràng nào, nhưng mặc dù không rõ ràng, không có lý do gì khiến nó không thể thực hiện theo cách đó). SIGTERM là (hoặc nên) tương đương với việc yêu cầu ứng dụng chấm dứt một cách duyên dáng, tuy nhiên , điều đó có thể được thực hiện trong ứng dụng cụ thể đó .
người dùng

3

Với tất cả các cuộc thảo luận đang diễn ra ở đây, không có mã nào được cung cấp. Đây là cách của tôi:

#!/bin/bash

$pid = 1234

echo "Killing process $pid..."
kill $pid

waitAttempts=30 
for i in $(seq 1 $waitAttempts)
do
    echo "Checking if process is alive (attempt #$i / $waitAttempts)..."
    sleep 1

    if ps -p $pid > /dev/null
    then
        echo "Process $pid is still running"
    else
        echo "Process $pid has shut down successfully"
        break
    fi
done

if ps -p $pid > /dev/null
then
    echo "Could not shut down process $pid gracefully - killing it forcibly..."
    kill -SIGKILL $pid
fi

0

HUP nghe có vẻ như rác rưởi đối với tôi. Tôi sẽ gửi nó để yêu cầu một daemon đọc lại cấu hình của nó.

SIGTERM có thể bị chặn; daemon của bạn chỉ có thể có mã dọn dẹp để chạy khi nó nhận được tín hiệu đó. Bạn không thể làm điều đó cho SIGKILL. Vì vậy, với SIGKILL, bạn không cung cấp cho tác giả của daemon bất kỳ tùy chọn nào.

Thêm về điều đó trên Wikipedia

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.