[PHP]posix_getpgrp関数とは?現在のプロセスグループIDを取得する方法【簡単解説】

PHP

こんにちは!今回はPHPのPOSIX拡張機能の中でも、シンプルで使いやすいposix_getpgrp関数について詳しく解説していきます。

posix_getpgrp関数とは?

posix_getpgrpは、現在のプロセスのプロセスグループID(PGID)を取得する関数です。posix_getpgidの簡易版で、自分自身のプロセスグループIDだけを取得します。

基本的な構文

posix_getpgrp(): int
  • 引数: なし
  • 戻り値: 現在のプロセスのプロセスグループID(整数)

posix_getpgidとの違い

この2つの関数は似ていますが、重要な違いがあります:

関数引数取得対象用途
posix_getpgrp()なし自分自身のPGID現在のプロセス情報のみ
posix_getpgid($pid)PID必須指定したプロセスのPGID他のプロセスも調査可能

実質的には同じ

<?php
// この2つは同じ結果を返します
$pgrp1 = posix_getpgrp();
$pgrp2 = posix_getpgid(posix_getpid());

echo "posix_getpgrp(): {$pgrp1}\n";
echo "posix_getpgid(getpid()): {$pgrp2}\n";

if ($pgrp1 === $pgrp2) {
    echo "✓ 両方とも同じ値を返しました\n";
}
?>

出力例:

posix_getpgrp(): 12340
posix_getpgid(getpid()): 12340
✓ 両方とも同じ値を返しました

基本的な使い方

シンプルな例

<?php
// 現在のプロセスグループIDを取得
$pgrp = posix_getpgrp();
$pid = posix_getpid();

echo "プロセスID: {$pid}\n";
echo "プロセスグループID: {$pgrp}\n";

if ($pid === $pgrp) {
    echo "✓ このプロセスはグループリーダーです\n";
} else {
    echo "- このプロセスはグループメンバーです\n";
    echo "  (グループリーダーのPIDは {$pgrp})\n";
}
?>

プロセス情報の表示

<?php
function displayProcessInfo(): void {
    echo "=== 現在のプロセス情報 ===\n\n";
    
    $pid = posix_getpid();
    $ppid = posix_getppid();
    $pgrp = posix_getpgrp();
    $sid = posix_getsid(0);
    $uid = posix_getuid();
    $gid = posix_getgid();
    
    echo "プロセス識別:\n";
    echo "  PID  (プロセスID):          {$pid}\n";
    echo "  PPID (親プロセスID):        {$ppid}\n";
    echo "  PGRP (プロセスグループID):  {$pgrp}\n";
    echo "  SID  (セッションID):        {$sid}\n\n";
    
    echo "ユーザー/グループ:\n";
    echo "  UID (ユーザーID):           {$uid}\n";
    echo "  GID (グループID):           {$gid}\n\n";
    
    echo "役割:\n";
    if ($pid === $pgrp) {
        echo "  ✓ プロセスグループリーダー\n";
    }
    if ($pid === $sid) {
        echo "  ✓ セッションリーダー\n";
    }
}

displayProcessInfo();
?>

実践的な活用例

1. プロセス監視ツール

<?php
class SimpleProcessMonitor {
    public function getCurrentStatus(): array {
        return [
            'timestamp' => time(),
            'pid' => posix_getpid(),
            'ppid' => posix_getppid(),
            'pgrp' => posix_getpgrp(),
            'sid' => posix_getsid(0),
            'is_group_leader' => (posix_getpid() === posix_getpgrp())
        ];
    }
    
    public function logStatus(string $logFile): bool {
        $status = $this->getCurrentStatus();
        
        $logEntry = sprintf(
            "[%s] PID=%d PPID=%d PGRP=%d SID=%d Leader=%s\n",
            date('Y-m-d H:i:s', $status['timestamp']),
            $status['pid'],
            $status['ppid'],
            $status['pgrp'],
            $status['sid'],
            $status['is_group_leader'] ? 'YES' : 'NO'
        );
        
        return file_put_contents($logFile, $logEntry, FILE_APPEND) !== false;
    }
    
    public function printStatus(): void {
        $status = $this->getCurrentStatus();
        
        echo "=== プロセス状態 ===\n";
        echo "時刻: " . date('Y-m-d H:i:s', $status['timestamp']) . "\n";
        echo "PID: {$status['pid']}\n";
        echo "PPID: {$status['ppid']}\n";
        echo "PGRP: {$status['pgrp']}\n";
        echo "SID: {$status['sid']}\n";
        
        if ($status['is_group_leader']) {
            echo "\n✓ このプロセスはグループリーダーです\n";
        }
    }
}

// 使用例
$monitor = new SimpleProcessMonitor();
$monitor->printStatus();
$monitor->logStatus('/tmp/process_monitor.log');
?>

2. グループ制御システム

<?php
class ProcessGroupController {
    private int $pgrp;
    
    public function __construct() {
        $this->pgrp = posix_getpgrp();
    }
    
    public function isGroupLeader(): bool {
        return posix_getpid() === $this->pgrp;
    }
    
    public function getGroupId(): int {
        return $this->pgrp;
    }
    
    public function becomeGroupLeader(): bool {
        // 新しいプロセスグループを作成し、自分がリーダーになる
        if (posix_setpgid(0, 0)) {
            $this->pgrp = posix_getpgrp();
            return true;
        }
        return false;
    }
    
    public function sendSignalToGroup(int $signal): bool {
        // プロセスグループ全体にシグナルを送信
        // マイナスのPGIDを指定することでグループ全体に送信
        return posix_kill(-$this->pgrp, $signal);
    }
    
    public function printGroupInfo(): void {
        echo "=== プロセスグループ情報 ===\n";
        echo "グループID: {$this->pgrp}\n";
        echo "現在のPID: " . posix_getpid() . "\n";
        echo "リーダー: " . ($this->isGroupLeader() ? 'YES' : 'NO') . "\n";
    }
}

// 使用例
$controller = new ProcessGroupController();
$controller->printGroupInfo();

if (!$controller->isGroupLeader()) {
    echo "\n新しいプロセスグループを作成します...\n";
    if ($controller->becomeGroupLeader()) {
        echo "✓ グループリーダーになりました\n";
        $controller->printGroupInfo();
    }
}
?>

3. ジョブ制御のシミュレーション

<?php
class JobController {
    private array $jobs = [];
    
    public function startJob(string $name, callable $task): ?int {
        if (!function_exists('pcntl_fork')) {
            echo "pcntl拡張が必要です\n";
            return null;
        }
        
        $pid = pcntl_fork();
        
        if ($pid === -1) {
            return null;
        }
        
        if ($pid === 0) {
            // 子プロセス
            $myPid = posix_getpid();
            $myPgrp = posix_getpgrp();
            
            echo "[ジョブ {$name}] 開始 - PID: {$myPid}, PGRP: {$myPgrp}\n";
            
            $task();
            
            echo "[ジョブ {$name}] 完了\n";
            exit(0);
        }
        
        // 親プロセス
        $this->jobs[$pid] = [
            'name' => $name,
            'started_at' => time(),
            'pgrp' => posix_getpgid($pid)
        ];
        
        return $pid;
    }
    
    public function listJobs(): void {
        echo "=== 実行中のジョブ ===\n";
        echo "親プロセス PGRP: " . posix_getpgrp() . "\n\n";
        
        foreach ($this->jobs as $pid => $job) {
            $status = pcntl_waitpid($pid, $statusCode, WNOHANG);
            $running = ($status === 0);
            
            printf("%-20s PID: %5d  PGRP: %5d  状態: %s\n",
                   $job['name'],
                   $pid,
                   $job['pgrp'],
                   $running ? '実行中' : '完了');
        }
    }
    
    public function waitAll(): void {
        foreach (array_keys($this->jobs) as $pid) {
            pcntl_waitpid($pid, $status);
        }
        $this->jobs = [];
    }
}

// 使用例
if (function_exists('pcntl_fork')) {
    $jobController = new JobController();
    
    // 複数のジョブを起動
    $jobController->startJob('データ処理', function() {
        sleep(2);
        echo "  データ処理実行中...\n";
    });
    
    $jobController->startJob('ファイル変換', function() {
        sleep(1);
        echo "  ファイル変換実行中...\n";
    });
    
    $jobController->startJob('レポート生成', function() {
        sleep(3);
        echo "  レポート生成実行中...\n";
    });
    
    sleep(1);
    $jobController->listJobs();
    
    echo "\n全ジョブの完了を待機中...\n";
    $jobController->waitAll();
    echo "✓ 全ジョブが完了しました\n";
}
?>

4. バックグラウンドタスクランナー

<?php
class BackgroundTaskRunner {
    private string $taskLog;
    
    public function __construct(string $taskLog = '/tmp/tasks.log') {
        $this->taskLog = $taskLog;
    }
    
    public function runInBackground(callable $task, array $params = []): bool {
        if (!function_exists('pcntl_fork')) {
            echo "pcntl拡張が必要です\n";
            return false;
        }
        
        $pid = pcntl_fork();
        
        if ($pid === -1) {
            return false;
        }
        
        if ($pid === 0) {
            // 子プロセス
            $this->logTask('start', $params);
            
            try {
                $task($params);
                $this->logTask('success', $params);
            } catch (Exception $e) {
                $this->logTask('error', $params, $e->getMessage());
            }
            
            exit(0);
        }
        
        // 親プロセスはすぐに戻る(バックグラウンド実行)
        return true;
    }
    
    private function logTask(string $status, array $params, string $message = ''): void {
        $logEntry = sprintf(
            "[%s] PID=%d PGRP=%d Status=%s Params=%s Message=%s\n",
            date('Y-m-d H:i:s'),
            posix_getpid(),
            posix_getpgrp(),
            $status,
            json_encode($params),
            $message
        );
        
        file_put_contents($this->taskLog, $logEntry, FILE_APPEND);
    }
    
    public function showTaskLog(int $lines = 10): void {
        if (!file_exists($this->taskLog)) {
            echo "タスクログが存在しません\n";
            return;
        }
        
        $content = file($this->taskLog);
        $content = array_slice($content, -$lines);
        
        echo "=== 最新のタスクログ ({$lines}件) ===\n";
        echo implode('', $content);
    }
}

// 使用例
$runner = new BackgroundTaskRunner();

// タスク1: データベースのバックアップ
$runner->runInBackground(function($params) {
    sleep(2);
    echo "データベースバックアップ完了: {$params['database']}\n";
}, ['database' => 'myapp']);

// タスク2: 画像のサムネイル生成
$runner->runInBackground(function($params) {
    sleep(1);
    echo "サムネイル生成完了: {$params['image']}\n";
}, ['image' => 'photo.jpg']);

echo "✓ バックグラウンドタスクを開始しました\n";
echo "現在のプロセスグループID: " . posix_getpgrp() . "\n\n";

// ログの表示
sleep(3);
$runner->showTaskLog();
?>

よくある使用パターン

パターン1: グループリーダーのチェック

<?php
function amIGroupLeader(): bool {
    return posix_getpid() === posix_getpgrp();
}

if (amIGroupLeader()) {
    echo "✓ このプロセスはグループリーダーです\n";
    echo "  - グループ全体の制御が可能\n";
    echo "  - シグナルを受信してメンバーに伝播可能\n";
} else {
    $leader = posix_getpgrp();
    echo "- このプロセスはグループメンバーです\n";
    echo "  グループリーダー: PID {$leader}\n";
}
?>

パターン2: プロセス階層の確認

<?php
function showProcessHierarchy(): void {
    $pid = posix_getpid();
    $ppid = posix_getppid();
    $pgrp = posix_getpgrp();
    $sid = posix_getsid(0);
    
    echo "プロセス階層:\n";
    echo "  セッション (SID: {$sid})\n";
    echo "    └─ プロセスグループ (PGRP: {$pgrp})\n";
    echo "         ├─ 親プロセス (PPID: {$ppid})\n";
    echo "         └─ 現在のプロセス (PID: {$pid}) ← YOU ARE HERE\n";
}

showProcessHierarchy();
?>

パターン3: シンプルなロギング

<?php
function logWithProcessInfo(string $message): void {
    $logEntry = sprintf(
        "[%s][PID:%d][PGRP:%d] %s\n",
        date('Y-m-d H:i:s'),
        posix_getpid(),
        posix_getpgrp(),
        $message
    );
    
    echo $logEntry;
    error_log($logEntry);
}

// 使用例
logWithProcessInfo('アプリケーション起動');
logWithProcessInfo('データベース接続成功');
logWithProcessInfo('処理完了');
?>

posix_getpgid との使い分け

posix_getpgrp を使うべき場合

<?php
// ✅ 自分自身の情報だけが必要な場合
$myPgrp = posix_getpgrp();

// シンプルで分かりやすい
if (posix_getpid() === posix_getpgrp()) {
    echo "グループリーダー\n";
}
?>

posix_getpgid を使うべき場合

<?php
// ✅ 他のプロセスの情報も必要な場合
$parentPgrp = posix_getpgid(posix_getppid());
$myPgrp = posix_getpgid(posix_getpid());

// 親プロセスとの比較
if ($myPgrp === $parentPgrp) {
    echo "親と同じプロセスグループ\n";
} else {
    echo "独立したプロセスグループ\n";
}

// ✅ 任意のプロセスを調査する場合
$targetPid = 1234;
$targetPgrp = posix_getpgid($targetPid);
?>

注意点とベストプラクティス

⚠️ 重要な注意事項

  1. POSIX環境限定: Windows環境では使用できません
  2. 常に成功: 自分自身の情報なのでfalseを返すことはありません(環境が正しい限り)
  3. シンプル: 引数がないので使いやすいが、他のプロセスは調べられない

環境チェック

<?php
function safeGetPgrp(): ?int {
    if (!function_exists('posix_getpgrp')) {
        error_log('POSIX拡張が利用できません');
        return null;
    }
    
    return posix_getpgrp();
}

// 使用例
$pgrp = safeGetPgrp();
if ($pgrp !== null) {
    echo "プロセスグループID: {$pgrp}\n";
} else {
    echo "この環境ではPOSIX関数が利用できません\n";
}
?>

関連関数との比較

関数引数戻り値用途
posix_getpgrp()なし自分のPGIDシンプルに自分の情報を取得
posix_getpgid($pid)PID指定プロセスのPGID他のプロセスも調査可能
posix_setpgid($pid, $pgid)PID, PGID成功/失敗プロセスグループの設定
posix_getpid()なし自分のPIDプロセスIDの取得
posix_getsid($pid)PIDセッションIDより上位の階層

全関数の活用例

<?php
function comprehensiveProcessInfo(): void {
    echo "=== 包括的なプロセス情報 ===\n\n";
    
    // 基本情報
    $pid = posix_getpid();
    $ppid = posix_getppid();
    
    // グループ情報(2つの方法)
    $pgrp1 = posix_getpgrp();
    $pgrp2 = posix_getpgid($pid);
    $parent_pgrp = posix_getpgid($ppid);
    
    // セッション情報
    $sid = posix_getsid(0);
    
    echo "プロセス識別子:\n";
    echo "  PID:  {$pid}\n";
    echo "  PPID: {$ppid}\n\n";
    
    echo "プロセスグループ:\n";
    echo "  posix_getpgrp():          {$pgrp1}\n";
    echo "  posix_getpgid(getpid()):  {$pgrp2}\n";
    echo "  親のPGRP:                  {$parent_pgrp}\n\n";
    
    echo "セッション:\n";
    echo "  SID: {$sid}\n\n";
    
    echo "検証:\n";
    echo "  2つの方法で同じ結果: " . ($pgrp1 === $pgrp2 ? '✓ YES' : '✗ NO') . "\n";
    echo "  親と同じグループ: " . ($pgrp1 === $parent_pgrp ? '✓ YES' : '✗ NO') . "\n";
    echo "  グループリーダー: " . ($pid === $pgrp1 ? '✓ YES' : '✗ NO') . "\n";
}

comprehensiveProcessInfo();
?>

まとめ

posix_getpgrp関数は、現在のプロセスのプロセスグループIDを取得するシンプルで使いやすい関数です。

特徴:

  • 引数不要: 自分自身の情報だけをサッと取得
  • シンプル: posix_getpgid(posix_getpid())の省略形
  • 高速: 最小限の処理で情報取得
  • 信頼性: 自分自身の情報なので失敗しない

主な用途:

  • グループリーダーかどうかの判定
  • ログへのプロセス情報の記録
  • プロセス階層の理解
  • シンプルなプロセス監視

使い分けの基準:

  • 自分の情報だけ → posix_getpgrp() を使う
  • 他のプロセスも調査 → posix_getpgid($pid) を使う

コンパクトで分かりやすいので、プロセス情報が必要な場面でまず最初に使いたい関数です!


参考リンク:

この記事が役立ったら、ぜひシェアしてください!⭐

タイトルとURLをコピーしました