Tại sao SIGKILL không chấm dứt chương trình đã dừng (có)?


8

Tôi đang sử dụng Ubuntu 14.04 và tôi gặp phải hành vi này dường như tôi không thể hiểu:

  1. Chạy yeslệnh (trong shell mặc định: Bash )
  2. CtrlZđể dừngyes
  3. Chạy đi jobs. Đầu ra:
    [1]+ Stopped yes
  4. Chạy kill -9 %1đến dừng lại yes. Đầu ra:
    [1]+ Stopped yes
  5. Chạy đi jobs. Đầu ra:
    [1]+ Stopped yes

Đây là trên Ubuntu 3.16.0-30-genericchạy trong một máy ảo song song.

Tại sao kill -9lệnh của tôi không chấm dứt lệnh yes ? Tôi nghĩ SIGKILL không thể bị bắt hoặc bỏ qua? Và làm thế nào tôi có thể chấm dứt lệnh yes ?


1
Nó thật thú vị. SIGKILL sẽ hoạt động và nó hoạt động trên Linux Mint của tôi 17. Đối với bất kỳ tín hiệu nào khác, thông thường bạn cần gửi SIGCONT sau đó để đảm bảo tín hiệu được nhận bởi mục tiêu đã dừng.
PSkocik

Có bash thực sự in "Dừng lại" cho một quá trình bị đình chỉ ?
edmz

Phiên bản hạt nhân ( uname -a) vui lòng
roaima

Linux ubuntu 3.16.0-30-generic #40~14.04.1-Ubuntu SMP Thu Jan 15 17:43:14 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux. Tôi đang chạy Ubuntu trong Parallels Desktop.
s1m0n

1
@black hầu hết các shell nói "Dừng lại". tcsh nói "Đình chỉ" và zsh nói "bị đình chỉ". Một sự khác biệt mỹ phẩm. Điều quan trọng hơn là thực tế là bash in một thông điệp giống hệt nhau cho STOP và TSTP, trong đó tất cả các shell khác đánh dấu chú thích tin nhắn STOP với (signal)để bạn có thể nhận ra sự khác biệt.

Câu trả lời:


10

Tín hiệu bị chặn cho các quy trình bị đình chỉ. Trong một thiết bị đầu cuối:

$ yes
...
y
y
^Zy

[1]+  Stopped                 yes

Trong một thiết bị đầu cuối thứ hai:

$ killall yes

Trong thiết bị đầu cuối đầu tiên:

$ jobs
[1]+  Stopped                 yes

$ fg
yes
Terminated

Tuy nhiên SIGKILLkhông thể bị chặn. Làm điều tương tự với killall -9 yestừ thiết bị đầu cuối thứ hai ngay lập tức mang lại điều này trong yesthiết bị đầu cuối:

[1]+  Killed                  yes

Do đó, nếu kill -9 %1không chấm dứt quá trình ngay lập tức thì bashthực tế sẽ không gửi tín hiệu cho đến khi bạn fgxử lý hoặc bạn đã phát hiện ra một lỗi trong kernel.


4
Một số chi tiết nền: Khi phát hành Ctrl + Z trong bash terminal của bạn sẽ gửi một SIGTSTP(đây là phiên bản có thể chặn được SIGSTOP) cho quy trình hoạt động. Điều này đặt quá trình ở trạng thái đóng băng trong đó kernel sẽ không lên lịch. Điều đó cũng ức chế xử lý tín hiệu (ngoại trừ SIGCONTtín hiệu giải phóng quá trình) và do đó ngăn quá trình bị giết ngay lập tức.
mreithub

1
SIGKILL, không giống như các tín hiệu khác, không bị chặn đối với các quy trình bị treo. Gửi tín hiệu KILL đến một quy trình bị đình chỉ sẽ giết chết nó - không đồng bộ, nhưng về cơ bản ngay lập tức.
Gilles 'SO- đừng trở nên xấu xa'

1
@Gilles Đó là những gì tôi đã cố gắng minh họa ở trên: SIGTERMbị chặn, nhưng SIGKILLkhông phải. Dù sao, theo một nhận xét từ OP, vấn đề dường như là jobskhông phát hiện ra rằng quá trình đã chết, không phải là quá trình không bị giết kill -9 %1.
lcd047

1
Nhưng tôi có thể tái tạo hành vi của s1m0n trên hệ thống của mình (Debian, amd64, bash 4.3.30).
Gilles 'SO- ngừng trở nên xấu xa'

1
Mặc dù SIGKILLkhông thể bị chặn, không có gì đảm bảo rằng nó sẽ được gửi trong bất kỳ thời gian có ý nghĩa nào. Ví dụ, nếu một quá trình bị đình chỉ chặn I / O, SIGKILLsẽ không đến khi quá trình thức dậy. Điều này có khả năng có thể không bao giờ, nếu không có I / O xảy ra.
sapi

7

Đừng hoảng sợ.

Không có gì sôi nổi đang diễn ra. Không có lỗi kernel ở đây. Đây là hành vi hoàn toàn bình thường từ vỏ Bourne Again và hệ điều hành đa nhiệm.

Điều cần nhớ là một quá trình giết chết chính nó , ngay cả để đáp ứng SIGKILL. Điều đang xảy ra ở đây là lớp vỏ Bourne Again đang xoay quanh mọi thứ trước quá trình mà nó vừa nói là tự giết mình để tự giết mình.

Hãy xem xét những gì xảy ra từ điểm yesđã dừng lại SIGTSTPvà bạn vừa thực hiện killlệnh với trình bao Bourne Again:

  1. Vỏ gửi SIGKILLđến yesquá trình.
  2. Song song :
    1. Các yesquá trình được lên kế hoạch để chạy và ngay lập tức giết chết bản thân.
    2. Vỏ Bourne Again tiếp tục, đưa ra một dấu nhắc khác.

Lý do mà bạn đang nhìn thấy một thứ và những người khác đang nhìn thấy một thứ khác là một cuộc đua đơn giản giữa hai quy trình sẵn sàng để chạy, người chiến thắng hoàn toàn phụ thuộc vào những thứ khác nhau từ máy này sang máy khác và theo thời gian. Tải hệ thống tạo ra sự khác biệt, cũng như thực tế là CPU của bạn là ảo.

Trong trường hợp thú vị, chi tiết của bước # 2 là:

  1. Vỏ Bourne Again tiếp tục.
  2. Là một phần của nội bộ của killlệnh tích hợp, nó đánh dấu mục trong bảng công việc của nó là cần một thông báo thông báo được in tại điểm có sẵn tiếp theo.
  3. Nó hoàn thành killlệnh và ngay trước khi in nhắc nhở, hãy kiểm tra xem liệu nó có nên in thông báo thông báo về bất kỳ công việc nào không.
  4. Các yesquá trình đã không có cơ hội để giết bản thân, vì vậy như xa như vỏ là có liên quan công việc vẫn đang trong trạng thái dừng. Vì vậy, shell in dòng trạng thái công việc "Đã dừng" cho công việc đó và đặt lại cờ chờ thông báo của nó.
  5. Các yesquá trình được lên kế hoạch và tiêu diệt chính nó.
  6. Hạt nhân thông báo cho shell, đang bận chạy trình soạn thảo dòng lệnh của nó, rằng quá trình này đã tự chết. Shell lưu ý sự thay đổi trạng thái và đánh dấu công việc là thông báo đang chờ xử lý lại.
  7. Chỉ cần nhấn enterđể quay vòng qua in nhắc một lần nữa sẽ cho shell cơ hội in trạng thái công việc mới.

Những điểm quan trọng là:

  • Quá trình tự sát. SIGKILLkhông phải là phép thuật. Các quy trình kiểm tra các tín hiệu đang chờ xử lý khi trở về chế độ ứng dụng từ chế độ kernel, xảy ra ở phần cuối của lỗi trang, ngắt (không lồng nhau) và các cuộc gọi hệ thống. Điều đặc biệt duy nhất là hạt nhân không cho phép hành động phản ứng trở SIGKILLthành bất cứ điều gì khác ngoài tự sát ngay lập tức và vô điều kiện, không quay trở lại chế độ ứng dụng. Điều quan trọng, các quy trình cần phải thực hiện chuyển đổi chế độ kernel sang ứng dụng được lên lịch để chạy để đáp ứng tín hiệu.
  • CPU ảo chỉ là một luồng trên hệ điều hành máy chủ. Không có gì đảm bảo rằng máy chủ đã lên lịch cho CPU ảo chạy. Hệ điều hành máy chủ cũng không phải là phép thuật.
  • Thông báo thông báo không được in khi thay đổi trạng thái công việc (trừ khi bạn sử dụng set -o notify). Chúng được in khi vỏ tiếp theo đạt đến một điểm trong chu kỳ thực hiện mà nó kiểm tra xem có bất kỳ thông báo nào đang chờ xử lý hay không.
  • Cờ chờ thông báo đang được đặt hai lần, một lần killvà một lần bởi SIGCHLDtrình xử lý tín hiệu. Điều này có nghĩa là người ta có thể nhìn thấy hai thông báo nếu shell đang chạy trước yesquá trình được lên lịch lại để tự chết; một tin nhắn "Đã dừng" và một tin nhắn "Bị giết".
  • Rõ ràng, /bin/killchương trình không có bất kỳ quyền truy cập nào vào bảng công việc nội bộ của shell; vì vậy bạn sẽ không thấy hành vi như vậy với /bin/kill. Cờ chờ thông báo chỉ được đặt một lần, bởi SIGCHLDtrình xử lý.
  • Cũng vì lý do tương tự, bạn sẽ không nhìn thấy hành vi này nếu bạn killcác yesquá trình từ vỏ khác.

3
Đó là một lý thuyết thú vị, nhưng OP phải gõ jobsvà vỏ vẫn thấy quá trình này còn sống. Đó sẽ là một điều kiện cuộc đua lập kế hoạch dài bất thường. :)
lcd047

3
Trước hết, cảm ơn câu trả lời công phu của bạn! Tôi chắc chắn có ý nghĩa và làm rõ khá nhiều thứ .. Nhưng như đã nói ở trên, tôi có thể chạy jobscác lệnh nhân sau khi killtất cả vẫn chỉ ra quá trình chỉ dừng lại. Tuy nhiên, bạn đã truyền cảm hứng cho tôi tiếp tục thử nghiệm và tôi phát hiện ra điều này: tin nhắn [1]+ Terminated yesđược in ngay khi tôi chạy một lệnh bên ngoài khác (không phải là shell dựng sẵn như echohoặc jobs). Vì vậy, tôi có thể chạy jobsbao nhiêu tùy thích và nó tiếp tục in [1]+ Stopped yes. Nhưng ngay khi tôi chạy lschẳng hạn, Bash đã in[1]+ Terminated yes
s1m0n

lcd047 đã không đọc bình luận của bạn cho câu hỏi; điều quan trọng và đáng lẽ phải được chỉnh sửa vào đầu câu hỏi. Thật dễ dàng để làm quá tải một hệ điều hành máy chủ sao cho khách có vẻ lên lịch rất kỳ lạ, từ bên trong. Chỉ như thế này, và nhiều hơn nữa bên cạnh. (Tôi đã từng quản lý để lập lịch trình khá kỳ quặc với Máy tính để bàn Bing chạy trốn, tiêu tốn phần lớn thời gian của CPU chủ.)
JdeBP

1
@Gilles Vấn đề dường như là jobskhông nhận thấy rằng quá trình đã thực sự chết ... Không biết phải làm gì về trạng thái đang được cập nhật bằng cách chạy một lệnh khác mặc dù.
lcd047

1
Ngay cả Gilles cũng không thấy bình luận. Đây là lý do tại sao bạn nên đặt loại nội dung quan trọng này trong câu hỏi , không chôn vùi nó trong một bình luận. Gilles, câu trả lời rõ ràng nói về sự chậm trễ trong việc phát tín hiệu, không chậm trễ trong việc gửi tín hiệu . Bạn đã trộn chúng lên. Ngoài ra, hãy đọc nhận xét của người hỏi (và thực sự là gạch đầu dòng mà nó được đưa ra ở đây) và xem giả định cơ bản sai rất quan trọng mà bạn đang đưa ra. Bộ xử lý ảo không nhất thiết phải chạy theo bước khóa và không có khả năng kỳ diệu là luôn chạy ở tốc độ tối đa.
JdeBP

2

Một cái gì đó thú vị có thể đang xảy ra trên hệ thống của bạn, với tôi công thức của bạn hoạt động độc đáo cả có và không có -9:

> yes
...
^Z
[1]+  Stopped                 yes
> jobs
[1]+  Stopped                 yes
> kill %1
[1]+  Killed                  yes
> jobs
> 

Lấy pid với jobs -pvà cố gắng giết nó như root.


Tôi có thể hỏi phiên bản phân phối / kernel / bash nào bạn đang sử dụng không? Có thể killlệnh nội bộ của bash của bạn đi xa hơn và kiểm tra xem công việc có bị đóng băng không (bạn có thể muốn thử tìm hiểu về công việc và giết nó bằng cách env kill <pid>đó. Bằng cách đó, bạn sẽ sử dụng killlệnh thực tế chứ không phải là bash dựng sẵn.
mreithub

bash-4.2-75.3.1.x86_64 trên openuse 13.2. Lệnh giết cmd không phải là nội bộ:which kill /usr/bin/kill
Dan Cornilescu

1
whichkhông phải là bash-dựng sẵn, vì vậy which <anything>sẽ luôn cung cấp cho bạn đường dẫn đến lệnh thực tế. Nhưng hãy thử so sánh kill --helpvới /usr/bin/kill --help.
mreithub

À, đúng rồi. Thật vậy, đó là nội dung kill.
Dan Cornilescu

2

Những gì bạn đang quan sát là một lỗi trong phiên bản bash này.

kill -9 %1không giết công việc ngay lập tức Bạn có thể quan sát điều đó với ps. Bạn có thể theo dõi quá trình bash để xem khi nào killcuộc gọi hệ thống được gọi và theo dõi tiến trình con để xem khi nào nó nhận và xử lý tín hiệu. Xen kẽ hơn, bạn có thể đi và xem những gì đang xảy ra với quá trình.

bash-4.3$ sleep 9999
^Z
[1]+  Stopped                 sleep 9999
bash-4.3$ kill -9 %1

[1]+  Stopped                 sleep 9999
bash-4.3$ jobs
[1]+  Stopped                 sleep 9999
bash-4.3$ jobs -l
[1]+  3083 Stopped                 sleep 9999
bash-4.3$ 

Trong một thiết bị đầu cuối khác:

% ps 3083
  PID TTY      STAT   TIME COMMAND
 3083 pts/4    Z      0:00 [sleep] <defunct>

Quá trình con là một zombie . Nó đã chết: tất cả những gì còn lại của nó là một mục trong bảng quy trình (nhưng không có bộ nhớ, mã, tệp đang mở, v.v.). Mục nhập được để lại xung quanh cho đến khi cha mẹ của nó chú ý và lấy trạng thái thoát của nó bằng cách gọi cuộc gọi waithệ thống hoặc một trong những anh chị em của nó .

Một vỏ tương tác có nhiệm vụ kiểm tra những đứa trẻ đã chết và gặt chúng trước khi in một dấu nhắc (trừ khi được cấu hình khác). Phiên bản bash này không thực hiện được trong một số trường hợp:

bash-4.3$ jobs -l
[1]+  3083 Stopped                 sleep 9999
bash-4.3$ true
bash-4.3$ /bin/true
[1]+  Killed                  sleep 9999

Bạn có thể mong đợi bash báo cáo về Kiết chết ngay khi nó in lời nhắc sau killlệnh, nhưng điều đó không được đảm bảo, vì có một điều kiện cuộc đua. Tín hiệu được phân phối không đồng bộ: killcuộc gọi hệ thống trả về ngay khi hạt nhân đã tìm ra (các) quá trình nào để truyền tín hiệu đến mà không cần chờ nó thực sự được gửi. Điều đó là có thể, và nó đã xảy ra trong thực tế, bash có thời gian để kiểm tra trạng thái của quy trình con của nó, thấy rằng nó vẫn chưa chết ( wait4không báo cáo bất kỳ cái chết nào của trẻ em) và in rằng quá trình vẫn đang dừng lại. Điều sai là trước lời nhắc tiếp theo, tín hiệu đã được gửi ( psbáo cáo rằng quá trình đã chết), nhưng bash vẫn chưa được gọiwait4(chúng ta có thể thấy rằng không chỉ bởi vì nó vẫn báo cáo công việc với tên là Stop Stopped, mà bởi vì zombie vẫn còn hiện diện trong bảng quy trình). Trên thực tế, bash chỉ gặt hái zombie vào lần tiếp theo mà nó cần gọi wait4, khi nó chạy một số lệnh bên ngoài khác.

Lỗi không liên tục và tôi không thể tái tạo nó trong khi bash được truy tìm (có lẽ vì đó là điều kiện cuộc đua mà bash cần phải phản ứng nhanh). Nếu tín hiệu được phát trước khi kiểm tra bash, mọi thứ sẽ diễn ra như mong đợi.

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.