🔱

ゾンビプロセスを作成して自動で終了させる

に公開

サマリ

ゾンビプロセスを作成して削除するプログラムをClaudeCodeに作成していただきました。

マシンスペック

MacBook Air M2 arm64

ゾンビプロセス

ゾンビプロセスは、親プロセスが子プロセスをforkした際に、子プロセスを終了させずに親プロセスが終了してしまい、子プロセスが残り続けてしまう事象です。
こちらは簡単な説明になります、専門的な内容は省略します。

ゾンビプロセスを作成して手動で終了する

一度、ゾンビプロセスの発見から削除までを手動で行います。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>

void show_zombies() {
    printf("\n現在のゾンビプロセス一覧:\n");
    printf("PID\tPPID\tSTATE\tCOMMAND\n");
    printf("----\t----\t-----\t-------\n");
    system("ps -eo pid,ppid,state,comm | grep ' Z ' || echo 'ゾンビプロセスは見つかりませんでした'");
    printf("\n");
}

int get_zombie_parents(pid_t *parents, int max_parents) {
    FILE *fp;
    char line[256];
    int count = 0;
    pid_t current_ppid, last_ppid = 0;
    
    fp = popen("ps -eo ppid,state | grep ' Z' | awk '{print $1}' | sort -u", "r");
    if (fp == NULL) {
        perror("popen failed");
        return 0;
    }
    
    while (fgets(line, sizeof(line), fp) != NULL && count < max_parents) {
        current_ppid = atoi(line);
        if (current_ppid > 1 && current_ppid != last_ppid) {
            parents[count] = current_ppid;
            count++;
            last_ppid = current_ppid;
        }
    }
    
    pclose(fp);
    return count;
}

int kill_parent_process(pid_t parent_pid) {
    printf("親プロセス %d を終了させています...", parent_pid);
    fflush(stdout);
    
    if (kill(parent_pid, SIGTERM) == 0) {
        printf(" SIGTERM送信");
        fflush(stdout);
        usleep(500000);
        
        if (kill(parent_pid, 0) == 0) {
            printf(" -> SIGKILL送信");
            fflush(stdout);
            if (kill(parent_pid, SIGKILL) == 0) {
                printf(" -> 完了\n");
                return 0;
            } else {
                printf(" -> 失敗\n");
                return 1;
            }
        } else {
            printf(" -> 完了\n");
            return 0;
        }
    } else {
        printf(" -> 失敗 (プロセスが存在しないか権限不足)\n");
        return 1;
    }
}

int main() {
    pid_t zombie_parents[100];
    int parent_count;
    char input[10];
    
    printf("=== ゾンビプロセス一括削除ツール ===\n\n");
    
    show_zombies();
    
    parent_count = get_zombie_parents(zombie_parents, 100);
    
    if (parent_count == 0) {
        printf("削除対象のゾンビプロセスは見つかりませんでした。\n");
        return 0;
    }
    
    printf("発見されたゾンビの親プロセス (%d個):\n", parent_count);
    for (int i = 0; i < parent_count; i++) {
        printf("  PID: %d\n", zombie_parents[i]);
    }
    
    printf("\nこれらの親プロセスを全て終了させますか? (y/N): ");
    fflush(stdout);
    
    if (fgets(input, sizeof(input), stdin) != NULL) {
        if (input[0] == 'y' || input[0] == 'Y') {
            printf("\n一括削除を開始します...\n");
            
            int success_count = 0;
            for (int i = 0; i < parent_count; i++) {
                if (kill_parent_process(zombie_parents[i]) == 0) {
                    success_count++;
                }
            }
            
            printf("\n処理完了: %d/%d の親プロセスを終了させました\n", success_count, parent_count);
            
            printf("\n2秒後に結果を確認します...\n");
            sleep(2);
            show_zombies();
            
        } else {
            printf("キャンセルされました。\n");
        }
    }
    
    return 0;
}

こちらを実行します。

./zombie_creator 1
親プロセスPID: 22470
1個のゾンビプロセスを作成します...
子プロセス 1 (PID: 22471) を作成しました

ゾンビプロセスが作成されました。
確認するには別ターミナルで: ps aux | grep defunct
または: ps -eo pid,ppid,state,comm | grep Z

親プロセスを終了するには Ctrl+C を押してください
(親プロセスが終了するとゾンビは自動的に回収されます)
子プロセス 1 (PID: 22471) が終了します

丁寧に説明が出てきましたので、コマンドを実行してみます。

ps aux | grep defunct
hoge         22482   0.0  0.0 410734064   1584 s002  S+    6:16AM   0:00.01 grep defunct
hoge         22471   0.0  0.0        0      0 s000  Z+    6:15AM   0:00.00 <defunct>

22232がpidになっています。ここで、親のプロセスを調べます。

ps -o ppid= -p 22471
22470

こちらを終了させると親プロセスと共にゾンビプロセスが終了します。
pidを指定してコマンドを打つのですが、意図しないプロセスを終了させるとクラッシュする恐れがあるので、pidはコピペで行いましょう。

sudo kill -9 22470

ゾンビプロセスの有無を確認します。

ps aux | grep defunct
hoge         22500   0.0  0.0 410724848   1440 s002  S+    6:18AM   0:00.00 grep defunct

無事、ゾンビを倒しました。

ゾンビプロセスを作成して自動で終了する

ゾンビプロセス掃除用のプログラムです。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>

void show_zombies() {
    printf("\n現在のゾンビプロセス一覧:\n");
    printf("PID\tPPID\tSTATE\tCOMMAND\n");
    printf("----\t----\t-----\t-------\n");
    system("ps -eo pid,ppid,state,comm | grep ' Z ' || echo 'ゾンビプロセスは見つかりませんでした'");
    printf("\n");
}

int get_zombie_parents(pid_t *parents, int max_parents) {
    FILE *fp;
    char line[256];
    int count = 0;
    pid_t current_ppid, last_ppid = 0;
    
    fp = popen("ps -eo ppid,state | grep ' Z' | awk '{print $1}' | sort -u", "r");
    if (fp == NULL) {
        perror("popen failed");
        return 0;
    }
    
    while (fgets(line, sizeof(line), fp) != NULL && count < max_parents) {
        current_ppid = atoi(line);
        if (current_ppid > 1 && current_ppid != last_ppid) {
            parents[count] = current_ppid;
            count++;
            last_ppid = current_ppid;
        }
    }
    
    pclose(fp);
    return count;
}

int kill_parent_process(pid_t parent_pid) {
    printf("親プロセス %d を終了させています...", parent_pid);
    fflush(stdout);
    
    if (kill(parent_pid, SIGTERM) == 0) {
        printf(" SIGTERM送信");
        fflush(stdout);
        usleep(500000);
        
        if (kill(parent_pid, 0) == 0) {
            printf(" -> SIGKILL送信");
            fflush(stdout);
            if (kill(parent_pid, SIGKILL) == 0) {
                printf(" -> 完了\n");
                return 0;
            } else {
                printf(" -> 失敗\n");
                return 1;
            }
        } else {
            printf(" -> 完了\n");
            return 0;
        }
    } else {
        printf(" -> 失敗 (プロセスが存在しないか権限不足)\n");
        return 1;
    }
}

int main() {
    pid_t zombie_parents[100];
    int parent_count;
    char input[10];
    
    printf("=== ゾンビプロセス一括削除ツール ===\n\n");
    
    show_zombies();
    
    parent_count = get_zombie_parents(zombie_parents, 100);
    
    if (parent_count == 0) {
        printf("削除対象のゾンビプロセスは見つかりませんでした。\n");
        return 0;
    }
    
    printf("発見されたゾンビの親プロセス (%d個):\n", parent_count);
    for (int i = 0; i < parent_count; i++) {
        printf("  PID: %d\n", zombie_parents[i]);
    }
    
    printf("\nこれらの親プロセスを全て終了させますか? (y/N): ");
    fflush(stdout);
    
    if (fgets(input, sizeof(input), stdin) != NULL) {
        if (input[0] == 'y' || input[0] == 'Y') {
            printf("\n一括削除を開始します...\n");
            
            int success_count = 0;
            for (int i = 0; i < parent_count; i++) {
                if (kill_parent_process(zombie_parents[i]) == 0) {
                    success_count++;
                }
            }
            
            printf("\n処理完了: %d/%d の親プロセスを終了させました\n", success_count, parent_count);
            
            printf("\n2秒後に結果を確認します...\n");
            sleep(2);
            show_zombies();
            
        } else {
            printf("キャンセルされました。\n");
        }
    }
    
    return 0;
}

まずは、ゾンビプロセスを100個作ってみます。バイオハザード状態にします。

./zombie_creator 100

ゾンビの数を確認します。

ps aux | grep defunct | wc -l
101

それでは倒していきます。

./zombie_cleaner 
=== ゾンビプロセス一括削除ツール ===


現在のゾンビプロセス一覧:
PID	PPID	STATE	COMMAND
----	----	-----	-------
ゾンビプロセスは見つかりませんでした

発見されたゾンビの親プロセス (1):
  PID: 22513

これらの親プロセスを全て終了させますか? (y/N): y

一括削除を開始します...
親プロセス 22513 を終了させています... SIGTERM送信 -> 完了

処理完了: 1/1 の親プロセスを終了させました

2秒後に結果を確認します...

現在のゾンビプロセス一覧:
PID	PPID	STATE	COMMAND
----	----	-----	-------
ゾンビプロセスは見つかりませんでした

Discussion