Linuxはプロセスをどのように「殺す」のでしょうか?


91

私は数十年の間コンピューターで、10年の間Linuxで専門的に仕事をしてきましたが、実際には魔法とは違い、ほとんどのOSの機能をブラックボックスとして扱っていることに困惑させられます。

今日、私はこのkillコマンドについて考えましたが、1日に複数回(「通常」と-9フレーバーの両方で)使用している間、それが舞台裏でどのように機能するかはまったくわかりません。

私の観点からは、実行中のプロセスが「ハング」している場合、killそのPID を呼び出すと、突然実行されなくなります。魔法!

そこで実際に何が起こるのでしょうか?マンページは「シグナル」について述べていますが、確かにそれは単なる抽象化です。kill -9プロセスへの送信は、プロセスの協力(シグナルの処理など)を必要とせず、単に終了させます。

  • LinuxはプロセスがCPU時間を占有し続けるのをどのように停止しますか?
  • スケジューリングから削除されますか?
  • 開いているファイルハンドルからプロセスを切断しますか?
  • プロセスの仮想メモリはどのように解放されますか?
  • Linuxがプロセスによって占有されるすべてのリソースへの参照を保持しているメモリ内のグローバルテーブルのようなものがあります。プロセスを「強制終了」すると、Linuxは単純にそのテーブルを通過してリソースを1つずつ解放しますか?

私は本当にすべてを知りたいです!



SIGKILLについての質問に対する私の私の答えは、ここでも関連する可能性があります。
telcoM

回答:


72

kill -9をプロセスに送信する場合、プロセスの協力(シグナルの処理など)は必要ありません。単にプロセスを強制終了します。

一部の信号はキャッチされ無視される可能性があるため、すべての信号に協力が必要であると思われます。しかしごとにman 2 signal、「シグナル SIGKILLとSIGSTOPはキャッチまたは無視することはできません」。SIGTERMをキャッチできるため、プレーンkillが常に有効であるとは限りません。一般的に、これはプロセスのハンドラーで何かがおかしくなったことを意味します。1

プロセスが特定のシグナルのハンドラーを定義しない(またはできない)場合、カーネルはデフォルトのアクションを実行します。 SIGTERMとSIGKILLの場合、これはプロセスを終了します(PIDが1でない限り、カーネルは終了しませんinit2 ファイルハンドルが閉じられ、メモリがシステムプールに返され、親がSIGCHILDを受信し、孤立していることを意味します子はinitなどによって呼び出されたように継承されますexit(を参照man 2 exit)。プロセスはもはや存在しません-ゾンビとして終わらない限り、その場合、それはまだいくつかの情報とともにカーネルのプロセステーブルにリストされています。それは、その親がwaitこの情報を適切に処理します。ただし、ゾンビプロセスにはメモリが割り当てられていないため、実行を継続できません。

Linuxがプロセスによって使用されるすべてのリソースへの参照を保持し、プロセスを「殺す」ときにLinuxがそのテーブルを通過してリソースを1つずつ解放する、メモリ内のグローバルテーブルのようなものはありますか?

これで十分だと思います。物理メモリはページ(通常は4 KBチャンクに等しい1ページ)ごとに追跡され、それらのページはグローバルプールから取得され、グローバルプールに返されます。ページに含まれるデータ(つまり、既存のファイルから読み取られたデータ)が再び必要になった場合に、一部の解放されたページがキャッシュされるという点で少し複雑です。

マンページは「シグナル」について述べていますが、確かにそれは単なる抽象化です。

もちろん、すべての信号は抽象化されています。それらは「プロセス」のような概念的なものです。私は少しセマンティクスをプレイしていますが、SIGKILLがSIGTERMと質的に異なるという意味であれば、yesとnoです。それは捕まえられないという意味ではありますが、両方ともシグナルであるという意味ではありません。類推により、リンゴはオレンジではありませんが、リンゴとオレンジは、先入観によると、両方の果物です。SIGKILL はキャッチできないため、より抽象的なように見えますが、それでもシグナルです。SIGTERM処理の例を次に示します。これらは以前に見たことがあると思います。

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

void sighandler (int signum, siginfo_t *info, void *context) {
    fprintf (
        stderr,
        "Received %d from pid %u, uid %u.\n",
        info->si_signo,
        info->si_pid,
        info->si_uid
    );
}

int main (void) {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_sigaction = sighandler;
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGTERM, &sa, NULL);
    while (1) sleep(10);
    return 0;
}

このプロセスは永久にスリープ状態になります。ターミナルで実行し、SIGTERMで送信できますkill。次のようなものを吐き出します:

Received 15 from pid 25331, uid 1066.

1066は私のUIDです。PID killは、実行元のシェルのPID、またはフォークした場合はkillのPIDになります(kill 25309 & echo $?)。

繰り返しますが、SIGKILLのハンドラーを設定しても意味がありません。3私の場合kill -9 25309、プロセスは終了します。しかし、それはまだシグナルです。カーネルは、送信者に関する情報を持っている信号の種類、信号それは、など


1.可能な信号のリストをまだ見ていない場合は、を参照してくださいkill -l

2.別の例外は、Tim Postが以下で言及するように、無停電スリープ中のプロセスに適用されます。これらは根本的な問題が解決されるまで起こせないので、すべてのシグナル(SIGKILLを含む)がその期間延期されます。ただし、プロセスはそのような状況を意図的に作成することはできません。

3.これはkill -9、実際に使用する方が良いことを意味するものではありません。私の例のハンドラーはそれに通じないという意味で悪いものexit()です。SIGTERMハンドラーの本当の目的は、プロセスに一時ファイルのクリーンアップなどの処理を実行し、自発的に終了する機会を与えることです。を使用する場合kill -9、このチャンスは得られないので、「自発的に終了する」部分が失敗したと思われる場合にのみ、それを行ってください。


-9わかりましたが、プロセスを殺すのは、それが本当の問題だからです。;)
Kiwy 14年

@Kiwy:カーネル。信号を含むIPCは通過します。カーネルはデフォルトのアクションを実装します。
goldilocks 14年

12
プロセスがその状態にある間、ディスクスリープ(D)はすべてのシグナルをプリエンプトすることに言及する価値があるかもしれません。したがって、kill -9特定のI / Oバウンドプロセスを試行しても、少なくともすぐには動作しません。
ティムポスト

7
kill -9キャッチできないため、それを受け取るプロセスは終了する前にクリーンアップ(一時ファイルの削除、共有メモリの解放など)を実行できないことを追加します。したがって、kill -9(別名kill -kill)は最後の手段としてのみ使用してください。kill -hupand / orで始まり、最後の打撃としてkill -term使用kill -killします。
JRFerguson 14年

「プロセスはもはや存在しません-ゾンビとして終わらない限り、その場合、カーネルのプロセステーブルに何らかの情報とともにリストされます」、実際、すべてのプロセスは死ぬとゾンビ状態になり、ゾンビは消えると消えます親は子に対してwaitpidを実行します。通常、それはあなたがそれを見るには速すぎます
賢い14

3

各プロセスはスケジュールされた時間実行された後、ハードウェアタイマーによって中断され、CPUコアを他のタスクに割り当てます。そのため、CPUコアよりもはるかに多くのプロセスを使用したり、1つのシングルコアCPUで多くのプロセスを使用してすべてのオペレーティングシステムを実行したりすることもできます。

プロセスが中断されると、制御はカーネルコードに戻ります。そのコードは、プロセス側からの協力なしに、中断されたプロセスの実行を再開しないことを決定できます。kill -9は、プログラムのどの行でも実行される可能性があります。


0

プロセスの強制終了がどのように機能するかの理想的な説明を次に示します。実際には、どのUnixバリアントにも多くの追加の複雑さと最適化があります。

カーネルには、プロセスごとにデータ構造があり、マッピングしているメモリ、スレッドの種類、スケジュールされているスレッド、開いているファイルなどに関する情報を保存します。カーネルがプロセスを強制終了する場合は、プロセスが強制終了されるプロセスのデータ構造(およびおそらく各スレッドのデータ構造内)。

プロセスのスレッドの1つが現在別のCPUでスケジュールされている場合、カーネルは他のCPUで割り込みをトリガーして、そのスレッドに実行を停止させます。

スケジューラーは、スレッドを強制終了する必要があるプロセスにあることに気付くと、スレッドをスケジュールしなくなります。

プロセスのスレッドがスケジュールされなくなると、カーネルはプロセスのリソース(メモリ、ファイル記述子など)の解放を開始します。カーネルはリソースを解放するたびに、所有者がまだライブリソースを持っているかどうかを確認します。プロセスにライブリソース(メモリマッピング、オープンファイル記述子など)がなくなると、プロセス自体のデータ構造を解放し、対応するエントリをプロセステーブルから削除できます。

一部のリソースはすぐに解放できます(たとえば、I / O操作で使用されていないメモリの割り当てを解除する)。一方、I / O操作が(進行中に他のリソースを解放することができないI / O操作を説明するサンプルデータのために、待たなければなりませんDMAが進行中で、それはアクセスだとメモリーが使用されている、およびDMAをキャンセルすることが必要です周辺機器に連絡する)。そのようなリソースのドライバーは通知を受け、キャンセルを急いで試みることができます。操作が進行しなくなったら、ドライバーはそのリソースの解放を完了します。

(プロセステーブルのエントリは、実際には親プロセスに属するリソースであり、プロセスが終了し、親がイベントを確認する解放されます。)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.