PHP-PROCFS

Written on June 2, 2014 View on GitHub

PHP 的简单解析 PROCFS 的东西,原本是要给单位的私有云添加简单的监控功能,可是单位团队的设计原则是把这功能加入 Ceilometer,所以这部分代码就用不到了。我自己觉得这个挺好用的,单向读取,PHP 权限较低也比较安全,所以就放出代码来。

相比纯字符串解析,PHP-PROCFS 使用正则简化代码和加速,生成 PHP 数据,可方便地转换为 JSON 等格式。

MEMINFO

/proc/meminfo 大致长成这样 MemTotal: 1010504 kB MemFree: 774888 kB Buffers: 11444 kB …. 解析方法,注意Value部分没有做单位转换。

	function proc_meminfo()
	{
		$meminfo = array();
		$count = preg_match_all('/(?<=^|[\r\n])([^:]+?)\s*:\s*(.*)(?=$|[\r\n])/', file_get_contents("/proc/meminfo"), $matches);
		for ($i=0; $i<$count; $i++) {
			$meminfo[$matches[1][$i]] = $matches[2][$i];
		}
		return $meminfo;
	}

事实上这个操作很通用,所以我改写成这样。

	function general_map($content)
	{
		$map = array();
		$count = preg_match_all('/(?<=^|[\r\n])([^:]+?)\s*:\s*(.*)(?=$|[\r\n])/', $content, $matches);
		for ($i=0; $i<$count; $i++) {
			$map[$matches[1][$i]] = $matches[2][$i];
		}
		return $map;
	}
	function proc_meminfo()
	{
		return general_map(file_get_contents("/proc/meminfo"));
	}

开始考虑过是否需要做单位转换,但是考虑到我对这块不了解,怕有什么特例情况,所以先这样了。 当然一个转换函数还是有必要写的

	function system_byteval($content)
	{
	    $count = preg_match('/(\d+)\s*(\w+)/', $content, $matches);
		if ($count == 0) return intval($content)
	    $unit = strtolower($matches[2]);
		$result = intval($matches[1]);
	    switch ($unit) {
	    case 'kb':
	        $result = $result*1024;
	        break;
	    case 'mb':
	        $result = $result*1024*1024;
	        break;
	    case 'gb':
	        $result = $result*1024*1024*1024;
	        break;
	    case 'tb':
	        $result = $result*1024*1024*1024*1024;
	        break;
	    default:
	        break;
	    }
	    return $result;
	}

CPUINFO

/proc/cpuinfo 的格式跟 meminfo稍稍有点不同。

	processor       : 0
	vendor_id       : GenuineIntel
	cpu family      : 6
	model           : 69
	model name      : Intel(R) Core(TM) i5-4288U CPU @ 2.60GHz
	...
	cpuid level     : 13
	wp              : yes
	flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts mmx fxsr sse sse2 ss syscall nx pdpe1gb ...
	bogomips        : 5199.99
	...
	power management:

如果没有特殊需求,可直接使用 general_map。 function proc_cpuinfo() { return general_map(file_get_contents(“/proc/cpuinfo”)); } 同样的,flags字段是可以考虑做分割的,暂时也没有做分割。

STAT

/proc/stat 则不一样,他实际上看上去像一片内存,总之是个数字数组。

	cpu  400 214 1003 282543 18 185 0 0 0 0
	cpu0 400 214 1003 282543 18 185 0 0 0 0
	intr 58560 53 10 0 0 0 0 2 0 1 0 0 0 152 0 0 0 0 4505 158 4592 .... 好多好多数字
	ctxt 123219
	btime 1401680123
	processes 1403
	procs_running 3
	procs_blocked 0
	softirq 48506 1 35077 571 4590 5818 0 57 0 15 2377

	function proc_stat()
	{
		$stat = array();
		$count = preg_match_all('/(?<=^|[\r\n])([^\s]+)\s+(.*)(?=$|[\r\n])/', file_get_contents("/proc/stat"), $matches);
		for ($i=0; $i<$count; $i++) {
			$stat[$matches[1][$i]] = preg_split ('/\s+/', $matches[2][$i], 0, PREG_SPLIT_NO_EMPTY);
		}
		return $stat;
	}

DISKSTATS

/proc/diskstats 的第一个值(设备类型)和第二个值(设备索引),联合起来是一个键,并且跟第三个值(设备名称)意义相同,所以从使用角度,我使用第三个值作为键。

	   1       0 ram0 0 0 0 0 0 0 0 0 0 0 0
	   1       1 ram1 0 0 0 0 0 0 0 0 0 0 0
	...
	   7       0 loop0 0 0 0 0 0 0 0 0 0 0 0
	   7       1 loop1 0 0 0 0 0 0 0 0 0 0 0
	...
	   2       0 fd0 0 0 0 0 0 0 0 0 0 0 0
	   8       0 sda 5674 31 253478 4180 814 575 13304 144 0 2228 4324
	   8       1 sda1 5248 0 249834 4120 814 575 13304 144 0 2184 4264
	   8       2 sda2 2 0 4 0 0 0 0 0 0 0 0
	   8       5 sda5 243 31 2192 48 0 0 0 0 0 48 48
	  11       0 sr0 0 0 0 0 0 0 0 0 0 0 0

同样只做了分割字符串

	function proc_diskstats()
	{
		$diskstats = array();
		$count = preg_match_all('/(?<=^|[\r\n])\s+([\d]+)\s+([\d]+)\s+([\w\d]+)\s+([\d\s]+)(?=$|[\r\n])/', file_get_contents("/proc/diskstats"), $matches);
		for ($i=0; $i<$count; $i++) {
			$diskstats[$matches[3][$i]] = preg_split ('/\s+/', $matches[0][$i], 0, PREG_SPLIT_NO_EMPTY);
		}
		return $diskstats;
	}

NET DEV

/proc/net/dev 这样的则比较复杂。 一般的解析脚本都认为它的字段是固定的,既然这样那我们也认为它的字段是固定的。所以思路是忽略前N行,直接取字段,一般重要的标识符号为冒号。 function proc_net_dev() { $net_dev = array(); $count = preg_match_all(‘/(?<=^|[\r\n])\s*([\w\d]+):([\d\s]+)(?=$|[\r\n])/’, file_get_contents(“/proc/net/dev”), $matches); for ($i=0; $i<$count; $i++) { $net_dev[$matches[1][$i]] = preg_split (‘/\s+/’, $matches[2][$i], 0, PREG_SPLIT_NO_EMPTY); } return $net_dev; }

MOUNTS

/proc/mounts 这样的,则比较尴尬,因为它不存在类似ID的东西。 所以这里的做法就是分割成数组,注意这里使用了 PREG_SET_ORDER

function proc_mounts()
{
	$mounts = array();
	$count = preg_match_all('/(?<=^|[\r\n])([\S]+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\d+)\s+(\d+)(?=$|[\r\n])/', file_get_contents("/proc/mounts"), $matches, PREG_SET_ORDER);
	for ($i=0; $i<$count; $i++) {
		array_push($mounts, array_slice($matches[$i], 1));
	}
	return $mounts;
}

总结

这些函数主要是对 /proc 下的文件进行简单的分割,代码非常省,没有做格式转换什么的,从实际应用的角度出发,应该对他们稍微加强再使用比较合适。 如可以对 proc_meminfo 进行单位转换,对 proc_net_dev 进行数据合并等等。