はじめに
PHPでシステムプログラミングを行う際、「このプロセスはどれだけのリソースを使用できるのか?」「なぜファイルが開けないのか?」といった疑問に直面することがあります。
Unixライクなシステムでは、プロセスが使用できるリソース(メモリ、ファイル記述子、プロセス数など)に制限が設けられています。これらの制限を超えようとすると、エラーが発生します。
その現在のリソース制限を取得するのが**posix_getrlimit関数**です。この関数を使えば、プロセスが利用可能なリソースの上限を確認し、適切なエラーハンドリングやリソース管理を実装できます。
この記事では、posix_getrlimitの基本から実践的な活用方法まで、詳しく解説します。
posix_getrlimitとは?
posix_getrlimitは、現在のプロセスのリソース制限(rlimit)を取得する関数です。
基本構文
posix_getrlimit(): array|false
パラメータ
この関数はパラメータを取りません。
戻り値
成功時は以下のようなリソース制限情報を含む連想配列:
| キー | 説明 |
|---|---|
soft core | コアダンプファイルの最大サイズ(ソフトリミット) |
hard core | コアダンプファイルの最大サイズ(ハードリミット) |
soft data | データセグメントの最大サイズ |
hard data | データセグメントの最大サイズ |
soft stack | スタックサイズの最大値 |
hard stack | スタックサイズの最大値 |
soft rss | 常駐セットサイズの最大値 |
hard rss | 常駐セットサイズの最大値 |
soft nproc | ユーザーが作成できるプロセス数 |
hard nproc | ユーザーが作成できるプロセス数 |
soft nofile | オープンできるファイル記述子の最大数 |
hard nofile | オープンできるファイル記述子の最大数 |
soft memlock | ロックできるメモリの最大サイズ |
hard memlock | ロックできるメモリの最大サイズ |
soft cpu | CPU時間の最大値(秒) |
hard cpu | CPU時間の最大値(秒) |
soft filesize | 作成できるファイルの最大サイズ |
hard filesize | 作成できるファイルの最大サイズ |
soft openfiles | オープンできるファイル数(macOS) |
hard openfiles | オープンできるファイル数(macOS) |
失敗時: false
ソフトリミットとハードリミット
- ソフトリミット: 通常の制限値。プロセスが自分で変更可能
- ハードリミット: 最大の制限値。特権ユーザーのみ変更可能
ソフトリミットはハードリミットを超えることはできません。
対応環境
- POSIX準拠システム(Linux、Unix、macOS)
- Windows では利用不可
- POSIX拡張モジュールが必要
対応バージョン
- PHP 5.1.0 以降で使用可能
基本的な使い方
リソース制限の取得
<?php
$limits = posix_getrlimit();
if ($limits) {
echo "=== リソース制限 ===\n\n";
echo "【ファイル記述子】\n";
echo " ソフトリミット: {$limits['soft openfiles']}\n";
echo " ハードリミット: {$limits['hard openfiles']}\n\n";
echo "【プロセス数】\n";
echo " ソフトリミット: {$limits['soft nproc']}\n";
echo " ハードリミット: {$limits['hard nproc']}\n\n";
echo "【スタックサイズ】\n";
echo " ソフトリミット: {$limits['soft stack']} bytes\n";
echo " ハードリミット: {$limits['hard stack']} bytes\n";
} else {
echo "リソース制限の取得に失敗しました\n";
}
?>
特定のリソース制限を確認
<?php
function checkFileDescriptorLimit() {
$limits = posix_getrlimit();
if (!$limits) {
return null;
}
return [
'soft' => $limits['soft openfiles'],
'hard' => $limits['hard openfiles'],
'unlimited' => $limits['soft openfiles'] === 'unlimited'
];
}
$fd_limit = checkFileDescriptorLimit();
if ($fd_limit) {
echo "ファイル記述子の制限:\n";
echo " ソフトリミット: {$fd_limit['soft']}\n";
echo " ハードリミット: {$fd_limit['hard']}\n";
if ($fd_limit['unlimited']) {
echo " 状態: 無制限\n";
}
}
?>
現在の使用状況と比較
<?php
function checkResourceUsage() {
$limits = posix_getrlimit();
if (!$limits) {
echo "リソース制限の取得に失敗\n";
return;
}
echo "=== リソース使用状況 ===\n\n";
// ファイル記述子
$open_files = count(get_resources());
$file_limit = $limits['soft openfiles'];
echo "【ファイル記述子】\n";
echo " 現在開いているリソース: {$open_files}\n";
echo " ソフトリミット: {$file_limit}\n";
if (is_numeric($file_limit)) {
$usage_percent = ($open_files / $file_limit) * 100;
echo " 使用率: " . number_format($usage_percent, 2) . "%\n";
if ($usage_percent > 80) {
echo " ⚠ 警告: 使用率が高くなっています\n";
}
}
// メモリ使用量
$memory_usage = memory_get_usage(true);
$memory_limit_str = ini_get('memory_limit');
echo "\n【メモリ使用量】\n";
echo " 現在の使用量: " . formatBytes($memory_usage) . "\n";
echo " PHPメモリ制限: {$memory_limit_str}\n";
}
function formatBytes($bytes) {
$units = ['B', 'KB', 'MB', 'GB'];
$i = 0;
while ($bytes >= 1024 && $i < count($units) - 1) {
$bytes /= 1024;
$i++;
}
return number_format($bytes, 2) . ' ' . $units[$i];
}
checkResourceUsage();
?>
実践的な使用例
例1: リソース制限の詳細表示
<?php
class ResourceLimitDisplay {
/**
* すべてのリソース制限を見やすく表示
*/
public static function displayAll() {
$limits = posix_getrlimit();
if (!$limits) {
echo "リソース制限の取得に失敗しました\n";
return;
}
echo "=== システムリソース制限 ===\n";
echo "日時: " . date('Y-m-d H:i:s') . "\n";
echo "PID: " . posix_getpid() . "\n\n";
$resources = [
'openfiles' => ['名前' => 'ファイル記述子数', '単位' => '個'],
'nproc' => ['名前' => 'プロセス数', '単位' => '個'],
'stack' => ['名前' => 'スタックサイズ', '単位' => 'bytes'],
'core' => ['名前' => 'コアダンプサイズ', '単位' => 'bytes'],
'cpu' => ['名前' => 'CPU時間', '単位' => '秒'],
'data' => ['名前' => 'データセグメント', '単位' => 'bytes'],
'filesize' => ['名前' => 'ファイルサイズ', '単位' => 'bytes'],
'memlock' => ['名前' => 'ロックメモリ', '単位' => 'bytes'],
'rss' => ['名前' => '常駐セットサイズ', '単位' => 'bytes'],
];
echo sprintf("%-25s %-20s %-20s\n",
"リソース", "ソフトリミット", "ハードリミット");
echo str_repeat("-", 70) . "\n";
foreach ($resources as $key => $info) {
$soft_key = "soft {$key}";
$hard_key = "hard {$key}";
if (!isset($limits[$soft_key])) {
continue;
}
$soft = self::formatValue($limits[$soft_key], $info['単位']);
$hard = self::formatValue($limits[$hard_key], $info['単位']);
echo sprintf("%-25s %-20s %-20s\n",
$info['名前'],
$soft,
$hard
);
}
}
/**
* 値をフォーマット
*/
private static function formatValue($value, $unit) {
if ($value === 'unlimited' || $value === -1) {
return '無制限';
}
if ($unit === 'bytes' && is_numeric($value)) {
return self::formatBytes($value);
}
return $value . ' ' . $unit;
}
/**
* バイト数を人間が読める形式に変換
*/
private static function formatBytes($bytes) {
if ($bytes === 'unlimited' || $bytes === -1) {
return '無制限';
}
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$i = 0;
while ($bytes >= 1024 && $i < count($units) - 1) {
$bytes /= 1024;
$i++;
}
return number_format($bytes, 2) . ' ' . $units[$i];
}
}
ResourceLimitDisplay::displayAll();
?>
例2: ファイル記述子の制限チェック
<?php
class FileDescriptorMonitor {
/**
* ファイル記述子の使用状況を監視
*/
public static function monitor() {
$limits = posix_getrlimit();
if (!$limits) {
throw new RuntimeException("リソース制限の取得に失敗しました");
}
$soft_limit = $limits['soft openfiles'];
$hard_limit = $limits['hard openfiles'];
echo "=== ファイル記述子監視 ===\n\n";
echo "制限:\n";
echo " ソフトリミット: {$soft_limit}\n";
echo " ハードリミット: {$hard_limit}\n\n";
// 現在開いているリソースを取得
$resources = get_resources();
$open_count = count($resources);
echo "現在の状況:\n";
echo " 開いているリソース: {$open_count}\n";
if (is_numeric($soft_limit)) {
$usage_percent = ($open_count / $soft_limit) * 100;
$remaining = $soft_limit - $open_count;
echo " 使用率: " . number_format($usage_percent, 2) . "%\n";
echo " 残り: {$remaining}\n\n";
// 警告レベルの判定
if ($usage_percent >= 90) {
echo "🔴 危険: ファイル記述子がほぼ上限です\n";
} elseif ($usage_percent >= 75) {
echo "🟡 警告: ファイル記述子の使用率が高くなっています\n";
} elseif ($usage_percent >= 50) {
echo "🟢 注意: ファイル記述子を多く使用しています\n";
} else {
echo "✓ 正常: 十分な余裕があります\n";
}
} else {
echo " 制限: 無制限\n";
}
// リソースの種類別集計
echo "\nリソースタイプ別:\n";
$type_counts = [];
foreach ($resources as $resource) {
$type = get_resource_type($resource);
$type_counts[$type] = ($type_counts[$type] ?? 0) + 1;
}
arsort($type_counts);
foreach ($type_counts as $type => $count) {
echo " {$type}: {$count}\n";
}
}
/**
* ファイル記述子が上限に達しそうか確認
*/
public static function isNearLimit($threshold = 0.8) {
$limits = posix_getrlimit();
if (!$limits || !is_numeric($limits['soft openfiles'])) {
return false;
}
$open_count = count(get_resources());
$limit = $limits['soft openfiles'];
return ($open_count / $limit) >= $threshold;
}
/**
* ファイルを安全に開く
*/
public static function safeOpen($filename, $mode, $warn_threshold = 0.8) {
// 制限に近づいているか確認
if (self::isNearLimit($warn_threshold)) {
$limits = posix_getrlimit();
$open_count = count(get_resources());
$limit = $limits['soft openfiles'];
error_log(sprintf(
"Warning: File descriptor usage high: %d/%d (%.1f%%)",
$open_count,
$limit,
($open_count / $limit) * 100
));
}
$handle = @fopen($filename, $mode);
if ($handle === false) {
$errno = posix_get_last_error();
if ($errno === EMFILE) {
throw new RuntimeException(
"ファイル記述子の上限に達しました\n" .
"現在: " . count(get_resources()) . "\n" .
"制限: " . $limits['soft openfiles']
);
}
throw new RuntimeException("ファイルを開けませんでした: {$filename}");
}
return $handle;
}
}
// 使用例
FileDescriptorMonitor::monitor();
// 安全なファイルオープン
try {
$fh = FileDescriptorMonitor::safeOpen('/tmp/test.txt', 'w');
fwrite($fh, "テストデータ\n");
fclose($fh);
} catch (RuntimeException $e) {
echo "エラー: " . $e->getMessage() . "\n";
}
?>
例3: プロセス数の制限チェック
<?php
class ProcessLimitChecker {
/**
* プロセス数の制限を確認
*/
public static function checkProcessLimit() {
$limits = posix_getrlimit();
if (!$limits) {
echo "リソース制限の取得に失敗\n";
return;
}
$soft_limit = $limits['soft nproc'];
$hard_limit = $limits['hard nproc'];
echo "=== プロセス数制限 ===\n\n";
echo "制限:\n";
echo " ソフトリミット: {$soft_limit}\n";
echo " ハードリミット: {$hard_limit}\n\n";
// 現在のユーザーのプロセス数を取得(Linux)
$uid = posix_getuid();
$user = posix_getpwuid($uid);
$ps_output = shell_exec("ps -u {$user['name']} --no-headers | wc -l");
$current_processes = (int)trim($ps_output);
echo "現在のプロセス数:\n";
echo " ユーザー '{$user['name']}': {$current_processes}\n";
if (is_numeric($soft_limit)) {
$usage_percent = ($current_processes / $soft_limit) * 100;
$remaining = $soft_limit - $current_processes;
echo " 使用率: " . number_format($usage_percent, 2) . "%\n";
echo " 残り: {$remaining}\n\n";
if ($usage_percent >= 90) {
echo "⚠ 警告: プロセス数が上限に近づいています\n";
} else {
echo "✓ 正常範囲内です\n";
}
}
}
/**
* 子プロセスをforkする前にチェック
*/
public static function canFork() {
$limits = posix_getrlimit();
if (!$limits || !is_numeric($limits['soft nproc'])) {
return true; // 制限が取得できない場合は許可
}
$uid = posix_getuid();
$user = posix_getpwuid($uid);
$ps_output = shell_exec("ps -u {$user['name']} --no-headers | wc -l");
$current_processes = (int)trim($ps_output);
$limit = $limits['soft nproc'];
// 90%を超えていたらfork不可
return ($current_processes / $limit) < 0.9;
}
/**
* 安全にfork
*/
public static function safeFork() {
if (!self::canFork()) {
throw new RuntimeException(
"プロセス数が上限に近づいているためforkできません"
);
}
$pid = pcntl_fork();
if ($pid === -1) {
$errno = posix_get_last_error();
$errmsg = posix_strerror($errno);
throw new RuntimeException(
"forkに失敗しました: {$errmsg}"
);
}
return $pid;
}
}
// 使用例
ProcessLimitChecker::checkProcessLimit();
?>
例4: スタックサイズの確認
<?php
class StackMonitor {
/**
* スタックサイズの制限を確認
*/
public static function checkStackLimit() {
$limits = posix_getrlimit();
if (!$limits) {
echo "リソース制限の取得に失敗\n";
return;
}
$soft_limit = $limits['soft stack'];
$hard_limit = $limits['hard stack'];
echo "=== スタックサイズ制限 ===\n\n";
echo "制限:\n";
echo " ソフトリミット: " . self::formatBytes($soft_limit) . "\n";
echo " ハードリミット: " . self::formatBytes($hard_limit) . "\n\n";
// 再帰の深さをテスト
echo "再帰深さテスト:\n";
$max_depth = 0;
try {
self::testRecursion($max_depth);
} catch (Error $e) {
echo " 最大再帰深さ: {$max_depth}\n";
echo " エラー: " . $e->getMessage() . "\n";
}
}
/**
* 再帰テスト
*/
private static function testRecursion(&$depth, $max_test = 100000) {
$depth++;
if ($depth >= $max_test) {
return;
}
self::testRecursion($depth, $max_test);
}
/**
* バイト数をフォーマット
*/
private static function formatBytes($bytes) {
if ($bytes === 'unlimited' || $bytes === -1) {
return '無制限';
}
$units = ['B', 'KB', 'MB', 'GB'];
$i = 0;
while ($bytes >= 1024 && $i < count($units) - 1) {
$bytes /= 1024;
$i++;
}
return number_format($bytes, 2) . ' ' . $units[$i];
}
}
StackMonitor::checkStackLimit();
?>
例5: リソース制限の診断ツール
<?php
class ResourceDiagnostics {
/**
* 包括的なリソース診断を実行
*/
public static function diagnose() {
echo "=== システムリソース診断 ===\n";
echo "日時: " . date('Y-m-d H:i:s') . "\n";
echo "ホスト: " . gethostname() . "\n";
echo "PID: " . posix_getpid() . "\n\n";
$limits = posix_getrlimit();
if (!$limits) {
echo "エラー: リソース制限の取得に失敗しました\n";
return;
}
$issues = [];
// 1. ファイル記述子
$fd_check = self::checkFileDescriptors($limits);
if (!$fd_check['ok']) {
$issues[] = $fd_check['message'];
}
// 2. プロセス数
$proc_check = self::checkProcessCount($limits);
if (!$proc_check['ok']) {
$issues[] = $proc_check['message'];
}
// 3. メモリ制限
$mem_check = self::checkMemoryLimits($limits);
if (!$mem_check['ok']) {
$issues[] = $mem_check['message'];
}
// 4. CPU時間
$cpu_check = self::checkCPULimit($limits);
if (!$cpu_check['ok']) {
$issues[] = $cpu_check['message'];
}
// 結果表示
echo "\n【診断結果】\n";
if (empty($issues)) {
echo "✓ すべて正常です\n";
} else {
echo "⚠ 以下の問題が検出されました:\n\n";
foreach ($issues as $i => $issue) {
echo ($i + 1) . ". {$issue}\n";
}
}
}
private static function checkFileDescriptors($limits) {
$soft_limit = $limits['soft openfiles'];
if (!is_numeric($soft_limit)) {
return ['ok' => true];
}
$open_count = count(get_resources());
$usage = ($open_count / $soft_limit) * 100;
echo "【ファイル記述子】\n";
echo " 制限: {$soft_limit}\n";
echo " 使用中: {$open_count}\n";
echo " 使用率: " . number_format($usage, 2) . "%\n";
if ($usage >= 80) {
return [
'ok' => false,
'message' => "ファイル記述子の使用率が高い ({$usage}%)"
];
}
echo " 状態: ✓ 正常\n\n";
return ['ok' => true];
}
private static function checkProcessCount($limits) {
$soft_limit = $limits['soft nproc'];
if (!is_numeric($soft_limit)) {
return ['ok' => true];
}
$uid = posix_getuid();
$user = posix_getpwuid($uid);
$ps_output = shell_exec("ps -u {$user['name']} --no-headers 2>/dev/null | wc -l");
$current = (int)trim($ps_output);
$usage = ($current / $soft_limit) * 100;
echo "【プロセス数】\n";
echo " 制限: {$soft_limit}\n";
echo " 現在: {$current}\n";
echo " 使用率: " . number_format($usage, 2) . "%\n";
if ($usage >= 80) {
return [
'ok' => false,
'message' => "プロセス数が上限に近い ({$usage}%)"
];
}
echo " 状態: ✓ 正常\n\n";
return ['ok' => true];
}
private static function checkMemoryLimits($limits) {
$memory_usage = memory_get_usage(true);
$memory_limit_str = ini_get('memory_limit');
echo "【メモリ】\n";
echo " PHPメモリ制限: {$memory_limit_str}\n";
echo " 現在の使用量: " . self::formatBytes($memory_usage) . "\n";
// メモリ制限をバイトに変換
$memory_limit = self::parseSize($memory_limit_str);
if ($memory_limit > 0) {
$usage = ($memory_usage / $memory_limit) * 100;
echo " 使用率: " . number_format($usage, 2) . "%\n";
if ($usage >= 80) {
return [
'ok' => false,
'message' => "メモリ使用率が高い ({$usage}%)"
];
}
}
echo " 状態: ✓ 正常\n\n";
return ['ok' => true];
}
private static function checkCPULimit($limits) {
$cpu_limit = $limits['soft cpu'];
echo "【CPU時間制限】\n";
echo " 制限: " . ($cpu_limit === 'unlimited' ? '無制限' : "{$cpu_limit}秒") . "\n";
echo " 状態: ✓ 正常\n\n";
return ['ok' => true];
}
private static function formatBytes($bytes) {
$units = ['B', 'KB', 'MB', 'GB'];
$i = 0;
while ($bytes >= 1024 && $i < count($units) - 1) {
$bytes /= 1024;
$i++;
}
return number_format($bytes, 2) . ' ' . $units[$i];
}
private static function parseSize($size) {
$units = ['K' => 1024, 'M' => 1048576, 'G' => 1073741824];
if (preg_match('/^(\d+)([KMG]?)$/i', $size, $matches)) {
$value = (int)$matches[1];
$unit = strtoupper($matches[2]);
return $value * ($units[$unit] ?? 1);
}
return (int)$size;
}
}
// 使用例
ResourceDiagnostics::diagnose();
?>
まとめ
posix_getrlimitは、プロセスのリソース制限を取得する関数です。
主な特徴:
- ✅ プロセスが使用できるリソースの上限を確認
- ✅ ソフトリミットとハードリミットの両方を取得
- ✅ ファイル記述子、プロセス数、メモリなど様々なリソースに対応
- ✅ リソース枯渇の予防に有用
主なリソース:
- ファイル記述子(openfiles)
- プロセス数(nproc)
- スタックサイズ(stack)
- CPU時間(cpu)
- ファイルサイズ(filesize)
- コアダンプサイズ(core)
- メモリロック(memlock)
使用場面:
- リソース枯渇の事前検知
- ファイル記述子不足のデバッグ
- プロセス制限の確認
- システム監視とアラート
関連関数:
posix_setrlimit()– リソース制限を設定(PHP 7.0.0以降)memory_get_usage()– メモリ使用量を取得get_resources()– 開いているリソースを取得
重要なポイント:
- ソフトリミットは通常のプロセスが変更可能
- ハードリミットは特権ユーザーのみ変更可能
- ソフトリミット ≤ ハードリミット
- “unlimited”または-1は無制限を意味する
- システムやシェルの設定で制限値が決まる
ベストプラクティス:
- リソース使用率が80%を超えたら警告
- ファイルを開く前に制限をチェック
- 長時間実行するプロセスは定期的に監視
- エラーハンドリングでEMFILE(ファイル記述子不足)を考慮
- ログにリソース使用状況を記録
制限値の変更方法:
# ulimitコマンドで一時的に変更
ulimit -n 4096 # ファイル記述子
# /etc/security/limits.confで恒久的に変更
username soft nofile 4096
username hard nofile 8192
よくあるエラーと対処:
- ファイル記述子不足(EMFILE)
- 原因: ファイルを閉じ忘れ、制限値が低い
- 対処: 不要なファイルをclose、ulimitで制限値を上げる
- プロセス数超過(EAGAIN)
- 原因: 子プロセスの作りすぎ
- 対処: プロセスプールを使う、古いプロセスをkill
- スタックオーバーフロー
- 原因: 深い再帰、大きなローカル変数
- 対処: 再帰を反復に変換、ulimit -s で制限値を上げる
この関数を理解して、より安定したリソース管理を実現しましょう!
参考リンク
- PHP公式ドキュメント – posix_getrlimit
- PHP公式ドキュメント – posix_setrlimit
- Linux man page – getrlimit(2)
- Linux man page – ulimit
この記事が役に立ったら、ぜひシェアしてください!
