こんにちは!今回はPHPのPOSIX関数の中から、posix_mknod関数について詳しく解説していきます。通常のファイルだけでなく、特殊なファイルタイプを作成できる上級者向けの関数を、実践的なサンプルコードと共にお伝えします。
posix_mknod関数とは?
posix_mknodは、**特殊ファイル(special files)**を作成する関数です。通常のファイルやディレクトリだけでなく、デバイスファイル、名前付きパイプ(FIFO)などを作成できます。
基本構文
posix_mknod(string $filename, int $flags, int $major = 0, int $minor = 0): bool
- 第1引数: ファイルパス
- 第2引数: モードとファイルタイプのフラグ
- 第3引数: メジャーデバイス番号(デバイスファイルの場合)
- 第4引数: マイナーデバイス番号(デバイスファイルの場合)
- 戻り値: 成功時に
true、失敗時にfalse
ファイルタイプとフラグ
主要なファイルタイプ定数
Unix/Linuxシステムには様々なファイルタイプが存在します。
<?php
// ファイルタイプ定数
// S_IFREG - 通常のファイル
// S_IFCHR - キャラクタデバイス
// S_IFBLK - ブロックデバイス
// S_IFIFO - FIFO (名前付きパイプ)
// S_IFSOCK - ソケット
// パーミッションビット
// 0644 - rw-r--r-- (ファイル)
// 0755 - rwxr-xr-x (実行可能)
// 0666 - rw-rw-rw- (全ユーザー読み書き可能)
?>
フラグの組み合わせ
<?php
// フラグはファイルタイプとパーミッションを組み合わせる
$mode = S_IFIFO | 0666; // FIFO、rw-rw-rw-
$mode = S_IFCHR | 0660; // キャラクタデバイス、rw-rw----
?>
実践的な使い方
例1: 基本的なFIFOの作成
<?php
// posix_mkfifoと同等の機能
$fifo_path = '/tmp/myfifo';
// FIFOを作成 (S_IFIFO | パーミッション)
$result = posix_mknod($fifo_path, S_IFIFO | 0666);
if ($result) {
echo "FIFOを作成しました: {$fifo_path}\n";
// ファイルタイプを確認
$type = filetype($fifo_path);
echo "ファイルタイプ: {$type}\n"; // "fifo"
// パーミッションを確認
$perms = fileperms($fifo_path);
echo "パーミッション: " . decoct($perms & 0777) . "\n";
// クリーンアップ
unlink($fifo_path);
} else {
$error = posix_get_last_error();
echo "エラー: " . posix_strerror($error) . "\n";
}
?>
例2: ファイルタイプ判定と作成
<?php
class SpecialFileManager {
public function createFIFO($path, $perms = 0666) {
if (file_exists($path)) {
throw new Exception("ファイルは既に存在します: {$path}");
}
$mode = S_IFIFO | $perms;
if (!posix_mknod($path, $mode)) {
$error = posix_strerror(posix_get_last_error());
throw new Exception("FIFO作成失敗: {$error}");
}
return true;
}
public function createRegularFile($path, $perms = 0644) {
// 通常ファイルの作成
// 注意: posix_mknodではS_IFREGをサポートしない実装が多い
// 通常はfopen/touchを使用する
if (touch($path)) {
chmod($path, $perms);
return true;
}
return false;
}
public function getFileInfo($path) {
if (!file_exists($path)) {
throw new Exception("ファイルが見つかりません: {$path}");
}
$stat = stat($path);
$mode = $stat['mode'];
// ファイルタイプを判定
$type = 'unknown';
if (($mode & S_IFIFO) === S_IFIFO) {
$type = 'FIFO';
} elseif (($mode & S_IFCHR) === S_IFCHR) {
$type = 'Character Device';
} elseif (($mode & S_IFBLK) === S_IFBLK) {
$type = 'Block Device';
} elseif (($mode & S_IFSOCK) === S_IFSOCK) {
$type = 'Socket';
} elseif (is_file($path)) {
$type = 'Regular File';
} elseif (is_dir($path)) {
$type = 'Directory';
} elseif (is_link($path)) {
$type = 'Symbolic Link';
}
return [
'path' => $path,
'type' => $type,
'mode' => decoct($mode & 0777),
'size' => $stat['size'],
'uid' => $stat['uid'],
'gid' => $stat['gid'],
];
}
public function remove($path) {
if (!file_exists($path)) {
return true;
}
return unlink($path);
}
}
// 使用例
$manager = new SpecialFileManager();
try {
// FIFOを作成
$manager->createFIFO('/tmp/test_fifo', 0666);
// ファイル情報を取得
$info = $manager->getFileInfo('/tmp/test_fifo');
echo "=== ファイル情報 ===\n";
foreach ($info as $key => $value) {
echo "{$key}: {$value}\n";
}
// 削除
$manager->remove('/tmp/test_fifo');
echo "\nファイルを削除しました\n";
} catch (Exception $e) {
echo "エラー: " . $e->getMessage() . "\n";
}
?>
例3: 複数のFIFOを管理するシステム
<?php
class FIFOPool {
private $fifos = [];
private $base_path;
public function __construct($base_path = '/tmp/fifo_pool') {
$this->base_path = $base_path;
// ベースディレクトリを作成
if (!is_dir($base_path)) {
mkdir($base_path, 0755, true);
}
}
public function create($name, $perms = 0666) {
$path = $this->base_path . '/' . $name;
if (isset($this->fifos[$name])) {
throw new Exception("FIFO '{$name}' は既に存在します");
}
if (file_exists($path)) {
throw new Exception("パスは既に使用されています: {$path}");
}
$mode = S_IFIFO | $perms;
if (!posix_mknod($path, $mode)) {
$error = posix_strerror(posix_get_last_error());
throw new Exception("FIFO作成失敗: {$error}");
}
$this->fifos[$name] = [
'path' => $path,
'created_at' => time(),
'readers' => [],
'writers' => []
];
echo "FIFO '{$name}' を作成しました: {$path}\n";
return $path;
}
public function get($name) {
if (!isset($this->fifos[$name])) {
throw new Exception("FIFO '{$name}' が見つかりません");
}
return $this->fifos[$name]['path'];
}
public function openReader($name) {
$path = $this->get($name);
$fp = fopen($path, 'r');
if ($fp) {
$this->fifos[$name]['readers'][] = $fp;
return $fp;
}
return false;
}
public function openWriter($name) {
$path = $this->get($name);
$fp = fopen($path, 'w');
if ($fp) {
$this->fifos[$name]['writers'][] = $fp;
return $fp;
}
return false;
}
public function remove($name) {
if (!isset($this->fifos[$name])) {
return false;
}
$fifo_info = $this->fifos[$name];
// 開いているストリームを閉じる
foreach ($fifo_info['readers'] as $fp) {
if (is_resource($fp)) {
fclose($fp);
}
}
foreach ($fifo_info['writers'] as $fp) {
if (is_resource($fp)) {
fclose($fp);
}
}
// FIFOを削除
if (file_exists($fifo_info['path'])) {
unlink($fifo_info['path']);
}
unset($this->fifos[$name]);
echo "FIFO '{$name}' を削除しました\n";
return true;
}
public function removeAll() {
foreach (array_keys($this->fifos) as $name) {
$this->remove($name);
}
}
public function list() {
$result = [];
foreach ($this->fifos as $name => $info) {
$result[$name] = [
'path' => $info['path'],
'created_at' => date('Y-m-d H:i:s', $info['created_at']),
'readers' => count($info['readers']),
'writers' => count($info['writers']),
];
}
return $result;
}
public function __destruct() {
$this->removeAll();
}
}
// 使用例
$pool = new FIFOPool();
// 複数のFIFOを作成
$pool->create('input');
$pool->create('output');
$pool->create('control', 0660);
// FIFOのリストを表示
echo "\n=== FIFO一覧 ===\n";
print_r($pool->list());
// データの送受信
$pid = pcntl_fork();
if ($pid == 0) {
// 子プロセス: データを送信
sleep(1);
$writer = $pool->openWriter('input');
if ($writer) {
fwrite($writer, "Hello from child process\n");
fclose($writer);
}
exit(0);
} else {
// 親プロセス: データを受信
$reader = $pool->openReader('input');
if ($reader) {
$data = fgets($reader);
echo "\n受信したデータ: {$data}";
fclose($reader);
}
pcntl_wait($status);
}
// クリーンアップ
$pool->removeAll();
?>
例4: パーミッションとumaskの管理
<?php
class SecureFileCreator {
private $old_umask;
public function __construct() {
// 現在のumaskを保存
$this->old_umask = umask();
}
public function createWithExactPermissions($path, $type, $perms) {
// umaskを0に設定して、指定したパーミッションを正確に適用
umask(0);
$mode = $type | $perms;
$result = posix_mknod($path, $mode);
// umaskを元に戻す
umask($this->old_umask);
if ($result) {
echo "ファイルを作成: {$path}\n";
echo "パーミッション: " . decoct($perms) . "\n";
// 実際のパーミッションを確認
$actual = fileperms($path) & 0777;
echo "実際のパーミッション: " . decoct($actual) . "\n";
return true;
}
return false;
}
public function createPrivateFIFO($path) {
// 所有者のみアクセス可能なFIFO
return $this->createWithExactPermissions($path, S_IFIFO, 0600);
}
public function createGroupFIFO($path) {
// 所有者とグループがアクセス可能なFIFO
return $this->createWithExactPermissions($path, S_IFIFO, 0660);
}
public function createPublicFIFO($path) {
// 全ユーザーがアクセス可能なFIFO
return $this->createWithExactPermissions($path, S_IFIFO, 0666);
}
public function demonstrateUmaskEffect() {
$test_path = '/tmp/umask_test_';
$counter = 0;
echo "\n=== umaskの影響を確認 ===\n";
// umaskなしで作成
echo "\n1. umask=0で作成\n";
umask(0);
$path1 = $test_path . $counter++;
posix_mknod($path1, S_IFIFO | 0666);
echo " パーミッション: " . decoct(fileperms($path1) & 0777) . "\n";
unlink($path1);
// umask=022で作成
echo "\n2. umask=022で作成\n";
umask(0022);
$path2 = $test_path . $counter++;
posix_mknod($path2, S_IFIFO | 0666);
echo " パーミッション: " . decoct(fileperms($path2) & 0777) . "\n";
echo " (0666 & ~022 = 0644)\n";
unlink($path2);
// umask=077で作成
echo "\n3. umask=077で作成\n";
umask(0077);
$path3 = $test_path . $counter++;
posix_mknod($path3, S_IFIFO | 0666);
echo " パーミッション: " . decoct(fileperms($path3) & 0777) . "\n";
echo " (0666 & ~077 = 0600)\n";
unlink($path3);
// umaskを元に戻す
umask($this->old_umask);
}
public function __destruct() {
// umaskを元に戻す
umask($this->old_umask);
}
}
// 使用例
$creator = new SecureFileCreator();
// 異なるセキュリティレベルのFIFOを作成
echo "=== セキュアなFIFO作成 ===\n";
$creator->createPrivateFIFO('/tmp/private.fifo');
echo "\n";
$creator->createGroupFIFO('/tmp/group.fifo');
echo "\n";
$creator->createPublicFIFO('/tmp/public.fifo');
echo "\n";
// umaskの影響を実演
$creator->demonstrateUmaskEffect();
// クリーンアップ
unlink('/tmp/private.fifo');
unlink('/tmp/group.fifo');
unlink('/tmp/public.fifo');
?>
例5: デバイスファイルの情報取得(参考)
<?php
// 注意: デバイスファイルの作成にはroot権限が必要
// この例は情報取得のみを行います
class DeviceFileInspector {
public function inspect($path) {
if (!file_exists($path)) {
throw new Exception("ファイルが見つかりません: {$path}");
}
$stat = stat($path);
$mode = $stat['mode'];
$info = [
'path' => $path,
'type' => $this->getFileType($mode),
'mode' => sprintf('%04o', $mode & 0777),
'uid' => $stat['uid'],
'gid' => $stat['gid'],
'size' => $stat['size'],
];
// デバイスファイルの場合、デバイス番号を取得
if ($this->isDeviceFile($mode)) {
$info['device'] = $stat['rdev'];
$info['major'] = $this->getMajor($stat['rdev']);
$info['minor'] = $this->getMinor($stat['rdev']);
}
return $info;
}
private function getFileType($mode) {
if (($mode & 0170000) === 0010000) return 'FIFO';
if (($mode & 0170000) === 0020000) return 'Character Device';
if (($mode & 0170000) === 0060000) return 'Block Device';
if (($mode & 0170000) === 0140000) return 'Socket';
if (($mode & 0170000) === 0100000) return 'Regular File';
if (($mode & 0170000) === 0040000) return 'Directory';
if (($mode & 0170000) === 0120000) return 'Symbolic Link';
return 'Unknown';
}
private function isDeviceFile($mode) {
$type = $mode & 0170000;
return $type === 0020000 || $type === 0060000;
}
private function getMajor($dev) {
return ($dev >> 8) & 0xff;
}
private function getMinor($dev) {
return $dev & 0xff;
}
public function listSystemDevices($dir = '/dev') {
$devices = [];
$files = scandir($dir);
foreach ($files as $file) {
if ($file === '.' || $file === '..') {
continue;
}
$path = $dir . '/' . $file;
if (is_dir($path)) {
continue;
}
try {
$info = $this->inspect($path);
if ($info['type'] === 'Character Device' ||
$info['type'] === 'Block Device') {
$devices[] = $info;
}
} catch (Exception $e) {
// アクセス権限がない場合はスキップ
continue;
}
// 最初の10個だけ表示(例として)
if (count($devices) >= 10) {
break;
}
}
return $devices;
}
}
// 使用例
$inspector = new DeviceFileInspector();
// FIFOを作成して検査
$test_fifo = '/tmp/inspect_test.fifo';
posix_mknod($test_fifo, S_IFIFO | 0644);
echo "=== FIFO情報 ===\n";
$info = $inspector->inspect($test_fifo);
foreach ($info as $key => $value) {
echo "{$key}: {$value}\n";
}
unlink($test_fifo);
// システムデバイスの情報を取得(読み取り権限がある場合)
echo "\n=== システムデバイス(抜粋) ===\n";
try {
$devices = $inspector->listSystemDevices('/dev');
foreach ($devices as $device) {
echo "\n{$device['path']}:\n";
echo " Type: {$device['type']}\n";
if (isset($device['major'])) {
echo " Major: {$device['major']}, Minor: {$device['minor']}\n";
}
}
} catch (Exception $e) {
echo "デバイス情報の取得に失敗: " . $e->getMessage() . "\n";
}
?>
注意点とトラブルシューティング
root権限が必要な場合
<?php
// デバイスファイルの作成にはroot権限が必要
if (posix_getuid() !== 0) {
die("警告: デバイスファイルの作成にはroot権限が必要です\n");
}
// キャラクタデバイスを作成(例: /dev/null のような)
// 注意: 実際のシステムデバイスは作成しないでください
$result = posix_mknod('/tmp/test_device', S_IFCHR | 0666, 1, 3);
if (!$result) {
$error = posix_strerror(posix_get_last_error());
echo "エラー: {$error}\n";
}
?>
posix_mkfifoとの使い分け
<?php
// FIFOを作成する場合、以下は同等
// 方法1: posix_mkfifo(シンプル)
posix_mkfifo('/tmp/fifo1', 0666);
// 方法2: posix_mknod(より汎用的)
posix_mknod('/tmp/fifo2', S_IFIFO | 0666);
// FIFOのみを作成する場合はposix_mkfifoが推奨
// 他のファイルタイプも扱う場合はposix_mknodを使用
?>
エラーハンドリング
<?php
function safeCreateSpecialFile($path, $mode, $major = 0, $minor = 0) {
// 既存ファイルチェック
if (file_exists($path)) {
throw new Exception("ファイルは既に存在します: {$path}");
}
// ディレクトリの存在確認
$dir = dirname($path);
if (!is_dir($dir)) {
throw new Exception("ディレクトリが存在しません: {$dir}");
}
// 書き込み権限チェック
if (!is_writable($dir)) {
throw new Exception("ディレクトリに書き込み権限がありません: {$dir}");
}
// ファイル作成
$result = posix_mknod($path, $mode, $major, $minor);
if (!$result) {
$error_num = posix_get_last_error();
$error_msg = posix_strerror($error_num);
throw new Exception("ファイル作成失敗: {$error_msg} (errno: {$error_num})");
}
return true;
}
// 使用例
try {
safeCreateSpecialFile('/tmp/safe_fifo', S_IFIFO | 0666);
echo "ファイルを安全に作成しました\n";
} catch (Exception $e) {
echo "エラー: " . $e->getMessage() . "\n";
}
?>
Windows環境での制限
<?php
if (!function_exists('posix_mknod')) {
die("posix_mknodはこの環境では使用できません\n" .
"Unix/Linux/macOS環境で実行してください\n");
}
// プラットフォーム固有の機能をラップ
class CrossPlatformFileCreator {
public function createFIFO($path, $perms = 0666) {
if (function_exists('posix_mknod')) {
return posix_mknod($path, S_IFIFO | $perms);
} elseif (function_exists('posix_mkfifo')) {
return posix_mkfifo($path, $perms);
} else {
throw new Exception("この環境ではFIFOを作成できません");
}
}
}
?>
関連する便利な関数
ファイルシステム関数との組み合わせ
<?php
// ファイル情報の取得
$path = '/tmp/test.fifo';
posix_mknod($path, S_IFIFO | 0666);
// stat()で詳細情報を取得
$stat = stat($path);
echo "inode: {$stat['ino']}\n";
echo "mode: " . decoct($stat['mode']) . "\n";
echo "links: {$stat['nlink']}\n";
// filetype()でタイプを確認
echo "type: " . filetype($path) . "\n"; // "fifo"
// is_*系関数
var_dump(is_file($path)); // false
var_dump(is_dir($path)); // false
var_dump(is_link($path)); // false
// パーミッション操作
chmod($path, 0644);
echo "新しいパーミッション: " . decoct(fileperms($path) & 0777) . "\n";
unlink($path);
?>
主要な関連関数
posix_mkfifo(): FIFOのみを作成(簡易版)filetype(): ファイルタイプを取得stat(): ファイルの詳細情報を取得fileperms(): パーミッションを取得chmod(): パーミッションを変更unlink(): ファイルを削除
まとめ
posix_mknod関数は以下の特徴があります。
✅ 特殊ファイルを作成する汎用関数 ✅ FIFO、デバイスファイルなどに対応 ✅ パーミッションを細かく制御可能 ✅ posix_mkfifoより汎用的 ✅ システムプログラミングに必須
posix_mknodは低レベルのシステムプログラミングで使用される関数です。通常のアプリケーション開発ではFIFO作成にposix_mkfifoを使用することが多いですが、より高度な制御が必要な場合にposix_mknodが役立ちます。
ベストプラクティス
- FIFOには専用関数: 単純にFIFOを作るなら
posix_mkfifo() - 権限の適切な設定: umaskを考慮してパーミッション設定
- エラーハンドリング: 失敗時の詳細なエラー情報を取得
- クリーンアップ: 使用後は必ずunlink()で削除
- root権限の注意: デバイスファイル作成時の権限管理
セキュリティの考慮事項
- パーミッション: 最小限のアクセス権を設定
- パスの検証: ユーザー入力をパスに使用する場合は検証
- 一時ファイル: /tmpディレクトリ使用時の競合状態に注意
- 所有権: 適切なユーザー/グループ権限の設定
安全なファイル操作を! この記事が役に立ったら、ぜひシェアしてください。PHPのシステムプログラミングについて、他にも知りたいことがあればコメントで教えてくださいね。
