原创 PHP获取服务器性能状态、资源使用情况

2024-10-31 16:16 215 3 3 分类: 物联网 文集: 教程
前言
当我们需要获取linux服务器的状态时,比如APP要获取服务器状态、网页要显示服务器状态,那么我们就可以用exec函数来命令linux,然后读取linux的数据,但是exec函数在PHP中都是推荐禁用的,因为exec函数可能会被注入恶意命令,从而导致服务器被攻击、数据泄露等安全问题。
所以本文不推荐使用exec函数,我们可以通过读取系统文件,来获取状态。
在Linux系统中,/proc目录是一个非常重要的特殊目录,它实际上是一个虚拟文件系统(proc文件系统),提供了对内核和系统进程信息的实时访问。

源码免费下载:https://mbb.eet-china.com/download/316368.html

proc目录介绍
  • /proc目录中的数据并不占用磁盘空间,而是由内核动态生成的,内容实时反映了系统和进程的当前状态。
  • 这个目录为用户和管理员提供了一个方便的接口,以监控和管理系统的状态。
(一)系统信息
①/proc/cpuinfo:提供有关CPU的信息,包括型号、核心数量、频率
②/proc/meminfo:显示有关内存使用情况的信息,包括总内存、可用内存、缓存
③/proc/version:显示当前运行的Linux内核版本和编译信息
④/proc/devices:列出所有已注册的设备及其驱动程序
⑤/proc/partitions:显示磁盘分区的信息
⑥/proc/stat: 系统性能的统计信息,如 CPU 使用情况、中断数量、上下文切换次数

(二)网络信息
①/proc/net/:包含网络相关的信息,如网络连接、路由表、网络接口统计
②/proc/net/tcp:显示TCP连接的信息
③/proc/net/udp:显示UDP连接的信息

编写PHP接口
(一)内存信息/proc/meminfo
①我们先看一下文件的内容,发现由两部分组成,一部分是标签,另一部分就是标签对应的数值
所以思路就是将所需标签的数值提取出来就可以了
需要提取:总内存MemTotal、未使用内存MemFree、缓冲区内存Buffers、缓存Cached
可用内存 = 未使用内存MemFree + 缓冲区内存Buffers + 缓存Cached
已用内存 = 总内存 - 可用内存
使用率 = (已用内存/总内存)* 100

②代码展示
function getMemoryInfo() {  
    // 读取 /proc/meminfo 文件内容  
    $meminfo = file('/proc/meminfo');  
  
    $freeMemory = 0;  
    $availableMemory = 0;  
    $totalMemory = 0;  
  
    foreach ($meminfo as $line) {  
        if (strpos($line, 'MemTotal:') === 0) {  
            // 获取总内存(MemTotal)  
            list(, $memTotalKb) = explode(':', trim($line));  
            $totalMemory = (int)$memTotalKb * 1024; // 转换为字节  
        } elseif (strpos($line, 'MemFree:') === 0) {  
            // 获取完全没有被使用的内存(MemFree)  
            list(, $memFreeKb) = explode(':', trim($line));  
            $freeMemory = (int)$memFreeKb * 1024; // 转换为字节  
        } elseif (strpos($line, 'Buffers:') === 0) {  
            // 获取被用作缓冲区的内存(Buffers)  
            list(, $buffersKb) = explode(':', trim($line));  
            $buffersMemory = (int)$buffersKb * 1024; // 转换为字节  
        } elseif (strpos($line, 'Cached:') === 0) {  
            // 获取被用作缓存的内存(Cached)  
            list(, $cachedKb) = explode(':', trim($line));  
            $cachedMemory = (int)$cachedKb * 1024; // 转换为字节  
        }  
    }  
  
    // 可用内存 = MemFree + Buffers + Cached  
    $availableMemory = $freeMemory + $buffersMemory + $cachedMemory;  
  
    // 计算内存使用率(百分比)  
    $usedMemory = $totalMemory - $availableMemory;  
    $memoryUsagePercent = ($usedMemory / $totalMemory) * 100;  
// 获取内存信息  
$memoryInfo = getMemoryInfo();  
  
// 打印空闲内存、可用内存、总内存和内存使用率(以MB为单位和百分比表示)  

echo "Free Memory: " . number_format($memoryInfo['free'] / (1024 * 1024), 2, '.', '') . " MB\n";  
echo "Available Memory: " . number_format($memoryInfo['available'] / (1024 * 1024), 2, '.', '') . " MB\n";  
echo "Total Memory: " . number_format($memoryInfo['total'] / (1024 * 1024), 2, '.', '') . " MB\n";  
echo "Memory Usage: " . number_format($memoryInfo['usage_percent'], 2, '.', '') . " %\n";

③代码解析

 foreach ($meminfo as $line)
使用foreach循环遍历$meminfo数组中的每一行。$line为当前的行

strpos($line, 'MemTotal:') === 0
strpos函数用于查找字符串在另一个字符串中首次出现的位置:strpos($line, 'MemTotal:') === 0,MemTotal在$line中首次出现

list(, $memTotalKb) = explode(':', trim($line));  
trim($line);用于将$line首位的空白去掉
explode(':', trim($line));explode将处理后的line从冒号":"开始分割,分成数组
去空白:MemTotal: 16384256 kB
分割成数组:["MemTotal", " 16384256 kB"]

list(, $cachedKb);读取数组的信息,第一位是空字符,即忽略MemTotal,从第二位获取,则16384256 kB
$cachedMemory = (int)$cachedKb * 1024; // 转换为字节 ,转不转都可以

    return [  
        'free' => $freeMemory,  
        'available' => $availableMemory,  
        'total' => $totalMemory,  
        'usage_percent' => $memoryUsagePercent  
    ]; 
返回函数,后续使用时,$memoryInfo = getMemoryInfo();  调用$memoryInfo['free'],简洁明了

number_format($memoryInfo['free'] / (1024 * 1024), 2, '.', '')
由于我取值时转为了字节,所以字节转到MB时,要计算,要让字节/1024*1024
然后取值2位小数,小数标点符号位“.”,不使用千位分隔符

(二)网络信息/proc/net/dev
①观察文件,前两行都为无效内容,所以在遍历的时候要忽略前两行,我们需要的数据为lo和wlan0两行的接收、发送数据
用一秒后的数据,减去一秒前的数据,就可以得到1秒数据大小

②代码展示
// 函数:获取所有网络接口的流量信息  
function getAllNetworkTraffic() {  
    // 读取 /proc/net/dev 文件的内容  
    $stats = file('/proc/net/dev');  
    $totalTraffic = [  
        'totalReceiveBytes' => 0,  
        'totalTransmitBytes' => 0,  
    ];  
      
    // 遍历每一行,除了前两行(标题和汇总行)  
    foreach ($stats as $lineNum => $line) {  
        // 跳过前两行  
        if ($lineNum < 2) {  
            continue;  
        }  
          
        // 使用正则表达式匹配网络接口的行并提取接收和发送的字节数  
        if (preg_match('/^\s*(\S+):\s+(\d+)\s+(\d+)/', $line, $matches)) {  
            $interface = $matches[1]; // 网络接口名称(可能包含冒号和数字,如 eth0:0)  
            $receiveBytes = intval($matches[2]);  
            $transmitBytes = intval($matches[3]);  
              
            // 累积接收和发送的字节数  //因为要获取前1秒的数据
            $totalTraffic['totalReceiveBytes'] += $receiveBytes;  
            $totalTraffic['totalTransmitBytes'] += $transmitBytes;  
        }  
    }  
      
    // 返回总流量信息  
    return $totalTraffic;  
}  
  
// 获取初始的所有网络接口流量信息  
$prevTotalTraffic = getAllNetworkTraffic();  
  
// 等待一秒钟  
sleep(1);  
  
// 获取当前的所有网络接口流量信息  
$currTotalTraffic = getAllNetworkTraffic();  
  
// 检查是否成功获取了流量信息  
if ($prevTotalTraffic && $currTotalTraffic) {  
    // 计算接收和发送的字节差异  
    $totalReceiveDiff = $currTotalTraffic['totalReceiveBytes'] - $prevTotalTraffic['totalReceiveBytes'];  
    $totalTransmitDiff = $currTotalTraffic['totalTransmitBytes'] - $prevTotalTraffic['totalTransmitBytes'];  
      
    // 将字节差异转换为 KB/s(千字节每秒),注意这里应该是 KBps 而不是 Kbps(因为 Kbps 通常指千比特每秒)  
    $totalReceiveSpeedKBps = $totalReceiveDiff / 1024;  
    $totalTransmitSpeedKBps = $totalTransmitDiff / 1024;  
      
    // 输出结果  
	//echo "所有接口综合下行流量(接收): " . number_format($totalReceiveSpeedKBps, 2, '.', '') . " KB/s\n";  
  //  echo "所有接口综合上行流量(发送): " . number_format($totalTransmitSpeedKBps, 2, '.', '') . " KB/s\n"; 
} else {  
    // 如果无法获取流量信息,则输出错误信息  
    echo "无法获取所有网络接口的流量信息。\n";  
}

③代码解析
用foreach遍历同上,要注意的是,直接跳过前两行的遍历,从第三行开始

(preg_match('/^\s*(\S+):\s+(\d+)\s+(\d+)/', $line, $matches)) {  
            $interface = $matches[1]; // 网络接口名称(可能包含冒号和数字,如 eth0:0)  
            $receiveBytes = intval($matches[2]);  
            $transmitBytes = intval($matches[3]);  
正则如上图所示
^\s*:匹配行首的任意数量的空白字符(空格、制表符等)
(\S+):匹配一个或多个非空白字符,这通常代表网络接口的名称(可能包含冒号和数字,如eth0:0)。这个匹配项被捕获到$matches[1]中
:\s+:匹配一个冒号后跟任意数量的空白字符
(\d+):匹配一个或多个数字,这代表接收的字节数。这个匹配项被捕获到$matches[2]中
\s+(\d+):再次匹配任意数量的空白字符后跟一个或多个数字,这代表发送的字节数。这个匹配项被捕获到$matches[3]中

$totalTraffic['totalReceiveBytes'] += $receiveBytes;  
$totalTraffic['totalTransmitBytes'] += $transmitBytes;
将遍历的结果累计到变量


// 获取初始的所有网络接口流量信息  
$prevTotalTraffic = getAllNetworkTraffic();  
  
// 等待一秒钟  
sleep(1);  
  
// 获取当前的所有网络接口流量信息  
$currTotalTraffic = getAllNetworkTraffic();  
当前的流量 - 1秒前的流量 = 1秒内的流量

(三)硬盘信息
查询硬盘信息要关闭防跨站攻击
①硬盘就简单很多,只需要获取根目录已经用的内存,和总内存,内存的使用率
②代码展示
$directory = '/';  
  
// 获取总磁盘空间和可用磁盘空间  
$totalSpace = disk_total_space($directory);  
$freeSpace = disk_free_space($directory);  
  
$totalSpaceGB = $totalSpace / (1024 * 1024 * 1024); // 或者使用 1073741824,但这种方式更清晰  
$freeSpaceGB = $freeSpace / (1024 * 1024 * 1024);  
// 计算已用空间  
$usedSpace = $totalSpace - $freeSpace;  
$usedSpaceGB = $totalSpaceGB - $freeSpaceGB;  
// 计算使用率(百分比)  
$usagePercentage = ($usedSpace / $totalSpace) * 100;  
  

③代码解析
disk_total_space函数:获取目录所在磁盘的总空间(以字节为单位)
disk_free_space函数:获取可用空间(同样以字节为单位)
将字节转换成GB,在字节上除以1024*1024*1024

(四)CPU使用率
①CPU使用率比较复杂,但是也和上面的差不多,也是正则匹配问题
②代码展示
function getCpuInfo() {  
    $stats = file('/proc/stat');  
    $cpuInfo = [];  
    foreach ($stats as $line) {  
        if (preg_match('/^cpu(\d*)\s+(.+)/', $line, $matches)) {  
            $cpu = $matches[1];  
            if ($cpu == '') {  
                $cpu = 'total';  
            }  
            $values = preg_split('/\s+/', $matches[2], -1, PREG_SPLIT_NO_EMPTY);  
            $cpuInfo[$cpu] = array_map('intval', $values);  
        }  
    }  
    return $cpuInfo;  
}  
  
function calculateCpuUsage($prev, $curr) {  
    $prevIdle = $prev['total'][3] + $prev['total'][4]; // idle + iowait (Linux 2.5.41+)  
    $currIdle = $curr['total'][3] + $curr['total'][4];  
  
    $prevNonIdle = array_sum($prev['total']) - $prevIdle;  
    $currNonIdle = array_sum($curr['total']) - $currIdle;  
  
    $prevTotal = array_sum($prev['total']);  
    $currTotal = array_sum($curr['total']);  
  
    $totalDiff = $currTotal - $prevTotal;  
    $idleDiff = $currIdle - $prevIdle;  
  
    $cpuUsage = 100 * (($totalDiff - $idleDiff) / $totalDiff);  
    return $cpuUsage;  
}  
  
// 获取初始 CPU 信息  
$prevCpuInfo = getCpuInfo();  
  
// 等待一秒(可以根据需要调整时间间隔)  
sleep(1);  
  
// 获取当前 CPU 信息  
$currCpuInfo = getCpuInfo();  
  
// 计算 CPU 使用率  
$cpuUsage = calculateCpuUsage($prevCpuInfo, $currCpuInfo);  
  
//echo "CPU Usage: " . number_format($cpuUsage, 2, '.', '') . "%\n";  

③代码讲解
使用 file('/proc/stat') 读取文件内容,每行作为数组的一个元素
遍历每行,使用正则表达式 '^cpu(\d*)\s+(.+)' 匹配 CPU 统计信息
cpu(\d*) 匹配 CPU 编号,如 cpu0, cpu1 等,没有编号的即为总体 CPU 信息(cpu)
\s+(.+) 匹配该 CPU 的统计值,这些值之间用空格分隔
如果匹配成功,$cpu 变量存储 CPU 编号(或 total),$values 数组存储解析出的统计值(如用户态时间、系统态时间、空闲时间等),并将这些值转换为整数存储到 $cpuInfo 数组中。

 
计算空闲时间(idle)和 I/O 等待时间(iowait)的总和,这两个时间值分别位于 $prev['total'] 和 $curr['total'] 数组的索引 3 和 4
计算非空闲时间(non-idle),即总时间减去空闲时间
计算总时间的差值(totalDiff)和空闲时间的差值(idleDiff)
CPU 使用率计算公式为:100 * (($totalDiff - $idleDiff) / $totalDiff)

执行流程:
调用 getCpuInfo 函数获取初始 CPU 信息($prevCpuInfo)
等待一秒(sleep(1)),以获取足够的时间间隔来观察 CPU 使用情况的变化
再次调用 getCpuInfo 函数获取当前 CPU 信息($currCpuInfo) 
调用 calculateCpuUsage 函数计算 CPU 使用率



作者: 小恶魔owo, 来源:面包板社区

链接: https://mbb.eet-china.com/blog/uid-me-4067534.html

版权声明:本文为博主原创,未经本人允许,禁止转载!

文章评论0条评论)

登录后参与讨论
我要评论
0
3
关闭 站长推荐上一条 /2 下一条