高度な検索
Developer Connection
Member Login ログイン | ご入会 ADC連絡先

Technical Q&A QA1123
Getting List of All Processes on Mac OS X


Q: Mac OS X 上のすべてのプロセスのリストを取得するにはどのようにすればよいのでしょうか?

A: それは、プロセスをどのように定義するかによります。実行中のアプリケーションのリストが必要ならば、Carbon Process Manager ルーチン、GetNextProcess を使います。このルーチンは、Carbon、Cocoa、および Classic の環境で実行されているものを含む、すべてのアプリケーションプロセスのリストを返します。しかし、これは、アプリケーションでない(デーモン)プロセスのリストは返しません。

注意:
GetNextProcess に関してもう 1 つ注意すべきことは、Carbon とリンクする必要がある点です。ただし、プログラムによっては(BSD のデーモンプロセスなど)、これは不可能です。

BSD の sysctl ルーチンを使えば、デーモンプロセスを含むすべての BSD プロセスのリストを取得できます。これを実行するコードをリスト 1 に示します。 このコードを使用するときには、次のことに注意しなければなりません。

  • 返される kinfo_proc 構造体には、プロセス ID (kp_proc.p_pid に)とプロセス名(kp_proc.p_comm に)を含め、プロセスに関する膨大な情報が含まれています。
  • BSD から見れば、Classic アプリケーションはすべて、1 つのプロセスの中で実行されます。
  • この sysctl を呼び出すために特別な権限は必要ありません。すべてのユーザが、システム上のすべてのプロセスのリストを取得できます。
  • UNIX Programming FAQ には、これを行うためのいくつかの方法が示されています。この中で Mac OS X 上で有効な唯一の方法は、ps コマンドラインツールを exec して実行する方法です。ps を exec する場合には、ツールの出力の解析が必要となり、リスト 1 ほど効率的にはシステムリソース使用しません。

"Processes.h" の中で宣言されている GetProcessPID ルーチンと GetProcessForPID ルーチンを使えば、BSD プロセス ID とプロセスのシリアル番号とを対応させることができます。

#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/sysctl.h>

typedef struct kinfo_proc kinfo_proc;

static int GetBSDProcessList(kinfo_proc **procList, size_t *procCount)
    // システム上のすべての BSD プロセスのリストを返す
    // このルーチンは、このリストの割り当てを行って *procList に入れ
    // エントリの数を *procCount に返す
    // このリストは自分で解放しなければならない(System フレームワークの free を使用)
    // 成功時、関数は 0 を返す
    // エラー発生時には、関数は BSD の errno の値を返す
{
    int                 err;
    kinfo_proc *        result;
    bool                done;
    static const int    name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
    // name を const として宣言するので、sysctl に渡すときにキャストする必要がある
    // プロトタイプに const が含まれていないからだ
    size_t              length;

    assert( procList != NULL);
    assert(*procList == NULL);
    assert(procCount != NULL);

    *procCount = 0;

    // result == NULL と length == 0 として sysctl を呼び出すことにより開始。
    // これは成功し、length が適切な長さに設定される。
    // その後そのサイズのバッファを割り当て、sysctl を再び呼び出す
    // これが成功すれば処理は終了である。ENOMEM エラーで失敗した場合は
    // バッファを破棄してループする必要がある。
    // このループでは、再び NULL を指定して、sysctl を呼び出す必要があることに注意。
    // これは必要な処理である。ENOMEM エラーの場合、length は、
    // 返されるべきデータ量ではなく
    // 返されたデータ量に設定されるからだ

    result = NULL;
    done = false;
    do {
        assert(result == NULL);

        // バッファを NULL にして sysctl を呼び出す

        length = 0;
        err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1,
                      NULL, &length,
                      NULL, 0);
        if (err == -1) {
            err = errno;
        }

        // 上記の呼び出しの結果に基づき適切なサイズの
        // バッファを割り当てる

        if (err == 0) {
            result = malloc(length);
            if (result == NULL) {
                err = ENOMEM;
            }
        }

        // この新しいバッファを使って sysctl を再び呼び出す
        // ENOMEM エラーを受け取った場合は、バッファを破棄しもう一度やり直す

        if (err == 0) {
            err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1,
                          result, &length,
                          NULL, 0);
            if (err == -1) {
                err = errno;
            }
            if (err == 0) {
                done = true;
            } else if (err == ENOMEM) {
                assert(result != NULL);
                free(result);
                result = NULL;
                err = 0;
            }
        }
    } while (err == 0 && ! done);

    // クリーンナップ処理を行い、事後状態を設定する

    if (err != 0 && result != NULL) {
        free(result);
        result = NULL;
    }
    *procList = result;
    if (err == 0) {
        *procCount = length / sizeof(kinfo_proc);
    }

    assert( (err == 0) == (*procList != NULL) );

    return err;
}

リスト 1 すべての BSD プロセスをリストするコード


[2002 年 3 月 5 日]