こんにちは!今回はPHPのPOSIX拡張機能の中でも、プロセス管理に関わる重要なposix_getpgid関数について詳しく解説していきます。
posix_getpgid関数とは?
posix_getpgidは、指定したプロセスIDのプロセスグループID(PGID)を取得する関数です。プロセスグループは、複数のプロセスをまとめて管理するためのUNIX/Linuxの仕組みです。
基本的な構文
posix_getpgid(int $process_id): int|false
- 引数: プロセスID(PID)
- 戻り値: プロセスグループID(PGID)、または失敗時は
false
プロセスグループとは?
プロセスグループは、関連するプロセスをひとまとめにして管理するための仕組みです。
プロセスグループの特徴
- 🔹 シグナルの一括送信: グループ全体にシグナルを送信可能
- 🔹 ジョブ制御: シェルでのフォアグラウンド/バックグラウンド管理
- 🔹 パイプライン:
cmd1 | cmd2 | cmd3のような連続実行で同じグループに
例: シェルでのパイプライン
$ cat file.txt | grep "pattern" | sort
この場合、cat、grep、sortは同じプロセスグループに属します
基本的な使い方
シンプルな例
<?php
// 現在のプロセスのプロセスグループIDを取得
$pid = posix_getpid();
$pgid = posix_getpgid($pid);
if ($pgid !== false) {
echo "プロセスID (PID): {$pid}\n";
echo "プロセスグループID (PGID): {$pgid}\n";
if ($pid === $pgid) {
echo "このプロセスはプロセスグループリーダーです\n";
} else {
echo "このプロセスはグループメンバーです\n";
}
} else {
echo "プロセスグループIDの取得に失敗しました\n";
}
?>
出力例:
プロセスID (PID): 12345
プロセスグループID (PGID): 12340
このプロセスはグループメンバーです
親プロセスとの関係を調査
<?php
function analyzeProcessHierarchy() {
// 現在のプロセス情報
$pid = posix_getpid();
$ppid = posix_getppid();
$pgid = posix_getpgid($pid);
$sid = posix_getsid($pid);
echo "=== プロセス階層分析 ===\n\n";
// 現在のプロセス
echo "現在のプロセス:\n";
echo " PID (プロセスID): {$pid}\n";
echo " PPID (親プロセスID): {$ppid}\n";
echo " PGID (プロセスグループID): {$pgid}\n";
echo " SID (セッションID): {$sid}\n\n";
// 親プロセスの情報
$parent_pgid = posix_getpgid($ppid);
$parent_sid = posix_getsid($ppid);
if ($parent_pgid !== false) {
echo "親プロセス:\n";
echo " PID: {$ppid}\n";
echo " PGID: {$parent_pgid}\n";
echo " SID: {$parent_sid}\n\n";
}
// 関係性の分析
echo "関係性:\n";
if ($pid === $pgid) {
echo " ✓ このプロセスはプロセスグループリーダー\n";
} else {
echo " - プロセスグループリーダーのPIDは {$pgid}\n";
}
if ($pid === $sid) {
echo " ✓ このプロセスはセッションリーダー\n";
}
if ($pgid === $parent_pgid) {
echo " ✓ 親プロセスと同じプロセスグループ\n";
} else {
echo " - 親プロセスとは別のプロセスグループ\n";
}
}
analyzeProcessHierarchy();
?>
実践的な活用例
1. プロセスグループ監視ツール
<?php
class ProcessGroupMonitor {
public function getGroupInfo(int $pid): ?array {
$pgid = posix_getpgid($pid);
if ($pgid === false) {
return null;
}
$sid = posix_getsid($pid);
return [
'pid' => $pid,
'pgid' => $pgid,
'sid' => $sid,
'is_group_leader' => ($pid === $pgid),
'is_session_leader' => ($pid === $sid)
];
}
public function findGroupMembers(int $pgid): array {
$members = [];
// /procディレクトリからプロセス情報を取得
$procDirs = glob('/proc/[0-9]*', GLOB_ONLYDIR);
foreach ($procDirs as $procDir) {
$pid = (int)basename($procDir);
// プロセスのPGIDを確認
$processPgid = posix_getpgid($pid);
if ($processPgid === $pgid) {
$members[] = $pid;
}
}
return $members;
}
public function printGroupReport(int $targetPid): void {
$info = $this->getGroupInfo($targetPid);
if ($info === null) {
echo "プロセス {$targetPid} の情報を取得できませんでした\n";
return;
}
echo "=== プロセスグループレポート ===\n\n";
echo "対象プロセス (PID): {$info['pid']}\n";
echo "プロセスグループ (PGID): {$info['pgid']}\n";
echo "セッション (SID): {$info['sid']}\n";
if ($info['is_group_leader']) {
echo "\n✓ このプロセスはグループリーダーです\n";
}
if ($info['is_session_leader']) {
echo "✓ このプロセスはセッションリーダーです\n";
}
// 同じグループのメンバーを検索
echo "\n同じグループのプロセス:\n";
$members = $this->findGroupMembers($info['pgid']);
foreach ($members as $memberPid) {
$isLeader = ($memberPid === $info['pgid']) ? ' [リーダー]' : '';
$isCurrent = ($memberPid === $targetPid) ? ' [現在]' : '';
echo " PID {$memberPid}{$isLeader}{$isCurrent}\n";
}
}
}
// 使用例
$monitor = new ProcessGroupMonitor();
$monitor->printGroupReport(posix_getpid());
?>
2. 子プロセス管理システム
<?php
class ProcessGroupManager {
private array $children = [];
public function spawnChildProcess(callable $callback): ?int {
$pid = pcntl_fork();
if ($pid === -1) {
// fork失敗
error_log('プロセスのforkに失敗しました');
return null;
}
if ($pid === 0) {
// 子プロセス
// 新しいプロセスグループを作成
posix_setpgid(0, 0);
$childPid = posix_getpid();
$childPgid = posix_getpgid($childPid);
echo "[子プロセス] PID: {$childPid}, PGID: {$childPgid}\n";
// コールバック実行
$callback();
exit(0);
}
// 親プロセス
$this->children[] = $pid;
$childPgid = posix_getpgid($pid);
echo "[親プロセス] 子プロセス {$pid} を起動 (PGID: {$childPgid})\n";
return $pid;
}
public function waitForChildren(): void {
foreach ($this->children as $childPid) {
echo "子プロセス {$childPid} の終了を待機中...\n";
pcntl_waitpid($childPid, $status);
if (pcntl_wifexited($status)) {
$exitCode = pcntl_wexitstatus($status);
echo "子プロセス {$childPid} が終了しました (終了コード: {$exitCode})\n";
}
}
$this->children = [];
}
public function killProcessGroup(int $pgid): bool {
// プロセスグループ全体にSIGTERMを送信
// マイナスのPGIDを指定することでグループ全体にシグナル送信
return posix_kill(-$pgid, SIGTERM);
}
}
// 使用例
if (function_exists('pcntl_fork')) {
$manager = new ProcessGroupManager();
// 子プロセスを3つ起動
for ($i = 1; $i <= 3; $i++) {
$manager->spawnChildProcess(function() use ($i) {
echo "子プロセス #{$i} が実行中...\n";
sleep(2);
echo "子プロセス #{$i} が完了\n";
});
}
// 全ての子プロセスの終了を待つ
$manager->waitForChildren();
echo "全ての子プロセスが終了しました\n";
} else {
echo "pcntl拡張が利用できません\n";
}
?>
3. デーモンプロセスの作成
<?php
class DaemonProcess {
private string $pidFile;
private string $logFile;
public function __construct(string $pidFile, string $logFile) {
$this->pidFile = $pidFile;
$this->logFile = $logFile;
}
public function daemonize(): bool {
// 第1段階: 親プロセスから分離
$pid = pcntl_fork();
if ($pid === -1) {
return false;
}
if ($pid > 0) {
// 親プロセスは終了
exit(0);
}
// 子プロセスがセッションリーダーになる
if (posix_setsid() === -1) {
return false;
}
// 第2段階: 制御端末から完全に切り離す
$pid = pcntl_fork();
if ($pid === -1) {
return false;
}
if ($pid > 0) {
// 第1子プロセスも終了
exit(0);
}
// これでデーモンプロセスになった
$myPid = posix_getpid();
$myPgid = posix_getpgid($myPid);
$mySid = posix_getsid($myPid);
// PIDファイルに書き込み
file_put_contents($this->pidFile, $myPid);
// ログに記録
$this->log("デーモン起動: PID={$myPid}, PGID={$myPgid}, SID={$mySid}");
// 作業ディレクトリを変更
chdir('/');
// ファイルマスクをクリア
umask(0);
// 標準入出力を閉じる
fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);
return true;
}
private function log(string $message): void {
$timestamp = date('Y-m-d H:i:s');
$logEntry = "[{$timestamp}] {$message}\n";
file_put_contents($this->logFile, $logEntry, FILE_APPEND);
}
public function run(): void {
$this->log("デーモンプロセス実行中");
// メインループ
while (true) {
// 実際の処理をここに記述
$this->log("定期処理実行");
sleep(60); // 60秒ごと
}
}
}
// 使用例
if (php_sapi_name() === 'cli') {
$daemon = new DaemonProcess('/tmp/mydaemon.pid', '/tmp/mydaemon.log');
if ($daemon->daemonize()) {
$daemon->run();
} else {
echo "デーモン化に失敗しました\n";
exit(1);
}
}
?>
4. プロセスツリーの可視化
<?php
class ProcessTreeVisualizer {
public function getProcessInfo(int $pid): ?array {
$pgid = posix_getpgid($pid);
if ($pgid === false) {
return null;
}
$ppid = $this->getParentPid($pid);
$sid = posix_getsid($pid);
return [
'pid' => $pid,
'ppid' => $ppid,
'pgid' => $pgid,
'sid' => $sid,
'command' => $this->getCommand($pid)
];
}
private function getParentPid(int $pid): ?int {
$statusFile = "/proc/{$pid}/status";
if (!file_exists($statusFile)) {
return null;
}
$status = file_get_contents($statusFile);
if (preg_match('/PPid:\s+(\d+)/', $status, $matches)) {
return (int)$matches[1];
}
return null;
}
private function getCommand(int $pid): string {
$cmdlineFile = "/proc/{$pid}/cmdline";
if (!file_exists($cmdlineFile)) {
return '(unknown)';
}
$cmdline = file_get_contents($cmdlineFile);
$cmdline = str_replace("\0", ' ', $cmdline);
$cmdline = trim($cmdline);
return $cmdline ?: '(unknown)';
}
public function printProcessTree(int $rootPid, int $depth = 0): void {
$info = $this->getProcessInfo($rootPid);
if ($info === null) {
return;
}
$indent = str_repeat(' ', $depth);
$marker = $depth > 0 ? '└─ ' : '';
printf("%s%sPID: %d, PGID: %d, SID: %d\n",
$indent,
$marker,
$info['pid'],
$info['pgid'],
$info['sid']);
printf("%s Command: %s\n",
$indent,
substr($info['command'], 0, 60));
// 子プロセスを検索
$children = $this->findChildren($rootPid);
foreach ($children as $childPid) {
$this->printProcessTree($childPid, $depth + 1);
}
}
private function findChildren(int $parentPid): array {
$children = [];
$procDirs = glob('/proc/[0-9]*', GLOB_ONLYDIR);
foreach ($procDirs as $procDir) {
$pid = (int)basename($procDir);
$ppid = $this->getParentPid($pid);
if ($ppid === $parentPid) {
$children[] = $pid;
}
}
return $children;
}
}
// 使用例
$visualizer = new ProcessTreeVisualizer();
echo "=== プロセスツリー ===\n\n";
$visualizer->printProcessTree(posix_getpid());
?>
よくある使用パターン
パターン1: プロセスグループの状態確認
<?php
function checkProcessGroup(): void {
$pid = posix_getpid();
$pgid = posix_getpgid($pid);
$ppid = posix_getppid();
$parent_pgid = posix_getpgid($ppid);
echo "現在のプロセス: PID={$pid}, PGID={$pgid}\n";
echo "親プロセス: PID={$ppid}, PGID={$parent_pgid}\n";
if ($pgid === $parent_pgid) {
echo "状態: 親プロセスと同じグループに所属\n";
} else {
echo "状態: 独立したプロセスグループ\n";
}
}
checkProcessGroup();
?>
パターン2: グループリーダーの特定
<?php
function findGroupLeader(int $pid): ?int {
$pgid = posix_getpgid($pid);
if ($pgid === false) {
return null;
}
// PGIDはグループリーダーのPIDと同じ
return $pgid;
}
$pid = posix_getpid();
$leader = findGroupLeader($pid);
if ($leader !== null) {
echo "プロセスグループリーダーのPID: {$leader}\n";
if ($pid === $leader) {
echo "このプロセスがグループリーダーです\n";
}
}
?>
注意点とベストプラクティス
⚠️ 重要な注意事項
- POSIX環境限定: Windows環境では使用できません
- 権限の制限: 他のユーザーのプロセスの情報は取得できない場合があります
- プロセスの存在: 存在しないPIDを指定すると
falseを返します
エラーハンドリング
<?php
function safeGetPgid(int $pid): ?int {
if (!function_exists('posix_getpgid')) {
error_log('POSIX拡張が利用できません');
return null;
}
// プロセスが存在するか確認
if (!posix_kill($pid, 0)) {
error_log("プロセス {$pid} は存在しません");
return null;
}
$pgid = posix_getpgid($pid);
if ($pgid === false) {
error_log("プロセス {$pid} のPGIDを取得できませんでした");
return null;
}
return $pgid;
}
// 使用例
$pid = posix_getpid();
$pgid = safeGetPgid($pid);
if ($pgid !== null) {
echo "PGID: {$pgid}\n";
}
?>
関連関数との関係
| 関数 | 説明 | 関係性 |
|---|---|---|
posix_getpgid() | プロセスグループIDを取得 | – |
posix_setpgid() | プロセスグループIDを設定 | 新しいグループの作成 |
posix_getpid() | 現在のプロセスIDを取得 | PGIDと比較してリーダー判定 |
posix_getsid() | セッションIDを取得 | より上位のグループ化 |
posix_kill() | シグナルを送信 | グループ全体への送信に使用 |
関連関数の活用例
<?php
function demonstrateProcessFunctions(): void {
$pid = posix_getpid();
$ppid = posix_getppid();
$pgid = posix_getpgid($pid);
$sid = posix_getsid($pid);
echo "=== プロセス関数の活用 ===\n\n";
echo "識別子:\n";
echo " プロセスID (PID): {$pid}\n";
echo " 親プロセスID (PPID): {$ppid}\n";
echo " プロセスグループID (PGID): {$pgid}\n";
echo " セッションID (SID): {$sid}\n\n";
echo "階層構造:\n";
echo " セッション [{$sid}]\n";
echo " └─ プロセスグループ [{$pgid}]\n";
echo " └─ プロセス [{$pid}]\n\n";
echo "役割:\n";
if ($pid === $pgid) {
echo " ✓ プロセスグループリーダー\n";
}
if ($pid === $sid) {
echo " ✓ セッションリーダー\n";
}
if ($pid !== $pgid && $pid !== $sid) {
echo " - 通常のプロセス\n";
}
}
demonstrateProcessFunctions();
?>
まとめ
posix_getpgid関数は、プロセスのグループID(PGID)を取得し、プロセス間の関係性を理解するための重要な関数です。
主な用途:
- ✅ プロセス管理: 関連プロセスのグループ化
- ✅ シグナル制御: グループ単位でのシグナル送信
- ✅ デーモン化: バックグラウンドプロセスの作成
- ✅ ジョブ制御: フォアグラウンド/バックグラウンド管理
重要なポイント:
- プロセスグループリーダーは
PID === PGID posix_setpgid()と組み合わせて新しいグループを作成- マイナスのPGIDでグループ全体にシグナル送信可能
- セッション > プロセスグループ > プロセス の階層構造
Linux/Unix環境でのプロセス管理やデーモン開発に必須の知識です!
参考リンク:
この記事が役立ったら、ぜひシェアしてください!🚀
