Linux カーネルにおける kill_orphaned_pgrp 関数の解説

Linux 3.2 の kill_orphaned_pgrp 関数について詳しく見ていきます。

1. 使用例を考える

Shell を使ったことがある方であれば、次のシナリオが理解できるでしょう:


sudo-su-bash@Sudo-su-BashdeMacBook-Air ~ % sleep 100
^Z
zsh: suspended  sleep 100
sudo-su-bash@Sudo-su-BashdeMacBook-Air ~ % nc -l 8080
^Z
zsh: suspended  nc -l 8080
sudo-su-bash@Sudo-su-BashdeMacBook-Air ~ % jobs
[1]  - suspended  sleep 100
[2]  + suspended  nc -l 8080
sudo-su-bash@Sudo-su-BashdeMacBook-Air ~ % fg %1
[1]  - continued  sleep 100
^Z
zsh: suspended  sleep 100
sudo-su-bash@Sudo-su-BashdeMacBook-Air ~ % bg %1
[1]  - continued  sleep 100
sudo-su-bash@Sudo-su-BashdeMacBook-Air ~ % exit

ここで、`Ctrl+Z` を押すことでフロントエンドプロセスを一時停止し、`bg` コマンドでバックグラウンドに移行できます。しかし、`exit` を実行するとシェルが終了し、バックグラウンドで実行されていたプロセス(`sleep` と `nc`)が取り残されます。この問題に対処するために使用されるのがこの関数です。

2. 関数の概要

この関数は、**孤立したプロセスグループを検出し、そのグループ内のすべての Stopped 状態のプロセスに対して SIGHUP や SIGCONT シグナルを送信する**ことを目的としています。


static void orphaned_group_handler(struct task_struct *current_task, struct task_struct *parent_task) {
    struct pid *group_id = get_process_group(current_task);
    struct task_struct *ignored_task = current_task;

    if (!parent_task) {
        parent_task = current_task->real_parent;
    } else {
        ignored_task = NULL;
    }

    if (get_process_group(parent_task) != group_id &&
        get_session(parent_task) == get_session(current_task) && 
        check_orphaned_group(group_id, ignored_task) && 
        has_stopped_tasks(group_id)) {
        send_signal_to_group(SIGTERM, group_id);
        send_signal_to_group(SIGCONT, group_id);
    }
}

3. 孤立プロセスグループの判定

以下は孤立プロセスグループであるかどうかを判定する関数です。


// 指定されたプロセスグループが孤立しているかチェック
static int check_orphaned_group(struct pid *group_id, struct task_struct *ignored_task) {
    struct task_struct *task;

    iterate_tasks_in_group(group_id, task) {
        if ((task == ignored_task) || // 忽略対象のプロセス
            (task->state == TASK_ZOMBIE && is_single_thread(task)) || // シングルスレッドで状態が ZOMBIE
            is_system_init(task->parent)) { // 親プロセスがシステムの init
            continue;
        }

        if (get_process_group(task->parent) != group_id &&
            get_session(task->parent) == get_session(task)) {
            return 0; // 他のセッション内に親が存在しない場合
        }
    }
    return 1; // 孤立している
}

この関数は、以下の条件を満たす場合にプロセスグループを孤立とみなします:

  • 親プロセスが init ではない。
  • 親子プロセスが異なるグループに属しており、同じセッションに存在する。
  • プロセスグループ内の少なくとも1つのプロセスが動作中またはマルチスレッドである。

4. 条件分岐の詳細

次に、実際にシグナルを送信する部分を見てみましょう。


if (get_process_group(parent_task) != group_id &&
    get_session(parent_task) == get_session(current_task) && 
    check_orphaned_group(group_id, ignored_task) && 
    has_stopped_tasks(group_id)) {
    send_signal_to_group(SIGTERM, group_id);
    send_signal_to_group(SIGCONT, group_id);
}

ここでの条件は、パフォーマンスを向上させるためのものです。もし最初の条件を省略すると、`check_orphaned_group` 関数が全プロセスを走査することになり、大きな負荷がかかります。

5. 特殊ケースの考慮

最後に、いくつかの特殊ケースについて考えてみます。


struct pid *group_id = get_process_group(target_task);
struct task_struct *ignored_task = target_task;

if (!parent_task) {
    parent_task = target_task->real_parent;
} else {
    ignored_task = NULL;
}

`parent_task` が NULL の場合は、現在のプロセスの実際の親プロセスが代入されます。また、`ignored_task` は特定のプロセスを無視する役割を持ちます。

タグ: linux-kernel process-management sigterm-signal

7月15日 22:12 投稿