POSIX 函数
概述
PHP 的 POSIX 扩展提供了访问 POSIX(可移植操作系统接口)标准的函数,主要用于获取进程信息、用户/组管理和文件权限操作。POSIX 扩展仅在 Unix/Linux 系统上可用。
适用场景
- 获取进程/用户信息
- 守护进程开发
- 权限管理
- 用户/组操作
基础概念
核心函数
| 函数 | 功能 | 返回值 |
|---|---|---|
posix_getpid() | 获取当前进程 ID | int |
posix_getppid() | 获取父进程 ID | int |
posix_getuid() | 获取当前用户 UID | int |
posix_getgid() | 获取当前组 GID | int |
posix_getpwuid() | 获取用户信息 | array|false |
posix_getgrgid() | 获取组信息 | array|false |
posix_getpwnam() | 按用户名获取信息 | array|false |
posix_getgrnam() | 按组名获取信息 | array|false |
posix_kill() | 发送信号给进程 | bool |
posix_setsid() | 创建新会话 | int |
posix_getlogin() | 获取当前登录名 | string|false |
posix_uname() | 获取系统信息 | array|false |
平台限制
POSIX 扩展仅在 Unix/Linux/macOS 上可用,不支持 Windows。
语法与代码示例
进程信息
php
<?php
echo "进程 ID: " . posix_getpid() . PHP_EOL;
echo "父进程 ID: " . posix_getppid() . PHP_EOL;
echo "当前用户 UID: " . posix_getuid() . PHP_EOL;
echo "当前组 GID: " . posix_getgid() . PHP_EOL;
// 系统信息
$uname = posix_uname();
print_r($uname);
/*
[
'sysname' => 'Linux',
'nodename' => 'server01',
'release' => '5.15.0',
'version' => '#1 SMP',
'machine' => 'x86_64',
]
*/
// 当前登录名
$login = posix_getlogin();
echo "当前登录用户: {$login}\n";用户/组管理
php
<?php
// 按 UID 获取用户信息
$userInfo = posix_getpwuid(1000);
print_r($userInfo);
/*
[
'name' => 'alice',
'passwd' => 'x',
'uid' => 1000,
'gid' => 1000,
'gecos' => 'Alice,,',
'dir' => '/home/alice',
'shell' => '/bin/bash',
]
*/
// 按用户名获取
$userInfo = posix_getpwnam('www-data');
echo "www-data UID: {$userInfo['uid']}\n";
echo "www-data Home: {$userInfo['dir']}\n";
// 按 GID 获取组信息
$groupInfo = posix_getgrgid(33);
print_r($groupInfo);
/*
[
'name' => 'www-data',
'passwd' => 'x',
'members' => ['alice', 'bob'],
'gid' => 33,
]
*/
// 按组名获取
$groupInfo = posix_getgrnam('www-data');
echo "www-data GID: {$groupInfo['gid']}\n";
echo "成员: " . implode(', ', $groupInfo['members']) . "\n";posix_kill 发送信号
php
<?php
// 向进程发送信号
$pid = 12345;
// SIGTERM (15) - 优雅终止
posix_kill($pid, SIGTERM);
// SIGUSR1 (10) - 自定义信号
posix_kill($pid, SIGUSR1);
// 检查进程是否存在
if (!posix_kill($pid, 0)) {
echo "进程 {$pid} 不存在\n";
} else {
echo "进程 {$pid} 正在运行\n";
}posix_setsid 创建会话
php
<?php
// setsid 创建新会话和新进程组
// 常用于守护进程
$sid = posix_setsid();
echo "新会话 ID: {$sid}\n";
// 配合 fork 使用
$pid = pcntl_fork();
if ($pid === 0) {
// 子进程
$sid = posix_setsid();
// 现在是一个新的会话领导者
}实战示例
守护进程管理
php
<?php
declare(strict_types=1);
class DaemonProcess
{
private string $pidFile;
private string $name;
public function __construct(string $name, string $pidDir = '/var/run')
{
$this->name = $name;
$this->pidFile = $pidDir . '/' . $name . '.pid';
}
public function daemonize(): void
{
// 第一次 fork
$pid = pcntl_fork();
if ($pid < 0) throw new RuntimeException("Fork failed");
if ($pid > 0) exit(0);
// 创建新会话
posix_setsid();
// 第二次 fork
$pid = pcntl_fork();
if ($pid < 0) throw new RuntimeException("Second fork failed");
if ($pid > 0) exit(0);
// 切换目录
chdir('/');
// 重设 umask
umask(0);
// 关闭标准文件描述符
fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);
// 写入 PID 文件
$this->writePid(posix_getpid());
// 注册信号处理
pcntl_async_signals(true);
pcntl_signal(SIGTERM, [$this, 'shutdown']);
pcntl_signal(SIGINT, [$this, 'shutdown']);
}
public function writePid(int $pid): void
{
file_put_contents($this->pidFile, (string)$pid);
}
public function readPid(): ?int
{
if (!file_exists($this->pidFile)) return null;
return (int)file_get_contents($this->pidFile);
}
public function isRunning(): bool
{
$pid = $this->readPid();
if ($pid === null) return false;
return posix_kill($pid, 0);
}
public function shutdown(int $signo = 0): void
{
@unlink($this->pidFile);
exit(0);
}
public function stop(): bool
{
$pid = $this->readPid();
if ($pid === null) return false;
posix_kill($pid, SIGTERM);
sleep(2);
if (posix_kill($pid, 0)) {
posix_kill($pid, SIGKILL);
}
return true;
}
}
// 使用
$daemon = new DaemonProcess('myworker');
// $daemon->daemonize();
// echo "Running: " . ($daemon->isRunning() ? 'yes' : 'no') . "\n";
// $daemon->stop();注意事项
需要正确的系统权限
php
<?php
// posix_kill() 需要对目标进程有权限
// 只能向自己或同组的进程发送信号(非 root 用户)
// posix_setsid() 需要适当的权限
// 如果当前进程已经是进程组领导者,setsid 会失败扩展检查
php
<?php
// 检查 POSIX 扩展是否可用
if (!function_exists('posix_getpid')) {
die("POSIX 扩展未加载。请安装: pecl install posix\n");
}
// 或通过扩展检查
if (!extension_loaded('posix')) {
die("POSIX 扩展未加载\n");
}最佳实践
1. 使用 PID 文件管理进程
php
<?php
class PidManager
{
public static function write(string $file, int $pid): void
{
$dir = dirname($file);
if (!is_dir($dir)) mkdir($dir, 0755, true);
file_put_contents($file, (string)$pid);
}
public static function read(string $file): ?int
{
if (!file_exists($file)) return null;
$pid = (int)trim(file_get_contents($file));
return $pid > 0 ? $pid : null;
}
public static function isAlive(string $file): bool
{
$pid = self::read($file);
return $pid !== null && posix_kill($pid, 0);
}
}2. 获取当前用户信息
php
<?php
function getCurrentUser(): array
{
$uid = posix_getuid();
$userInfo = posix_getpwuid($uid);
return [
'uid' => $uid,
'username' => $userInfo['name'],
'home' => $userInfo['dir'],
'shell' => $userInfo['shell'],
];
}进阶用法
调试与测试技巧
php
<?php
declare(strict_types=1);
// 单元测试辅助函数
function createTestResource(): mixed
{
return match (true) {
default => new stdClass(),
};
}
// 调试输出函数
function debugOutput(mixed , string = ''): void
{
= ? ": " : '';
.= print_r(, true);
fwrite(STDERR, . "\n");
}
// 性能基准测试
function benchmark(callable , int = 1000): float
{
= hrtime(true);
for ($i = 0; $i < $iterations; $i++) {
$fn();
}
return (hrtime(true) - $start) / 1e9;
}日志记录实践
php
<?php
declare(strict_types=1);
/**
* 简易日志记录器
*/
class SimpleLogger
{
private string $logFile;
private string $level = 'INFO';
public function __construct(string $logFile)
{
$this->logFile = $logFile;
}
public function info(string $message, array $context = []): void
{
$this->log('INFO', $message, $context);
}
public function warning(string $message, array $context = []): void
{
$this->log('WARNING', $message, $context);
}
public function error(string $message, array $context = []): void
{
$this->log('ERROR', $message, $context);
}
private function log(string $level, string $message, array $context): void
{
$timestamp = date('Y-m-d H:i:s');
$contextStr = $context ? ' ' . json_encode($context, JSON_UNESCAPED_UNICODE) : '';
$line = "[{$timestamp}] [{$level}] {$message}{$contextStr}\n";
file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX);
}
}配置与环境检测
php
<?php
declare(strict_types=1);
// 环境检测工具
class EnvironmentChecker
{
public static function checkRequirements(array $requirements): array
{
$results = [];
foreach ($requirements as $name => $check) {
$results[$name] = is_callable($check) ? $check() : false;
}
return $results;
}
public static function getSystemInfo(): array
{
return [
'php_version' => PHP_VERSION,
'os' => PHP_OS,
'sapi' => PHP_SAPI,
'memory_limit' => ini_get('memory_limit'),
'max_execution_time' => ini_get('max_execution_time'),
'loaded_extensions' => get_loaded_extensions(),
];
}
}常见问题排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 连接超时 | 网络问题/配置错误 | 检查配置,增加超时时间 |
| 权限不足 | 文件/目录权限 | 使用 chmod/chown 修正 |
| 性能下降 | 索引缺失/数据量大 | 添加索引,优化查询 |
| 数据不一致 | 并发冲突/事务残留 | 使用锁机制和事务 |
| 内存溢出 | 大数据集/未释放资源 | 增大内存限制,分批处理 |
故障排除步骤
- 检查错误日志和异常信息
- 确认配置和环境是否正确
- 使用调试工具逐步排查
- 参考官方文档查找已知问题
版本兼容性说明
| 功能 | 最低版本 | 说明 |
|---|---|---|
| 基础功能 | PHP 8.1 | 本文档基准版本 |
| 只读属性 | PHP 8.1 | public readonly 修饰符 |
| 枚举类型 | PHP 8.1 | enum 类型和 match 表达式 |
| Fiber | PHP 8.1 | 协程/轻量级并发 |
| 命名参数 | PHP 8.0 | foo(arg_name: value) |
| 联合类型 | PHP 8.0 | `int |
| Null 安全运算符 | PHP 8.0 | $obj?->method() |
| 析构器 promotion | PHP 8.0 | __construct(public $x) |
php
<?php
declare(strict_types=1);
// 版本兼容性检测
function ensureVersion(string $minVersion): void
{
if (version_compare(PHP_VERSION, $minVersion, '<')) {
throw new RuntimeException(
sprintf('需要 PHP %s+, 当前版本: %s', $minVersion, PHP_VERSION)
);
}
}
ensureVersion('8.1.0');