在Python2中实现Solaris系统netstat信息收集的技术求助
你遇到的问题很典型——Python 2标准库确实没有直接封装Solaris/Illumos依赖的getmsg/putmsg流系统调用,不过结合ctypes直接调用C库接口,完全可以绕过这个限制,而且符合你不能用外部库、只能用Python 2的要求。
先理清楚核心思路:Solaris的netstat是通过STREAMS框架与内核交互的,你已经打开了/dev/arp,但还缺两个关键步骤:加载必要的STREAMS模块,以及用ctypes封装getmsg/putmsg和对应的数据结构。下面一步步来实现:
1. 先补全/dev/arp的IOCTL初始化
从你提供的truss输出可以看到,netstat打开/dev/arp后,会用I_PUSH命令加载tcp/udp/icmp模块,这一步必须做,否则内核不会返回对应协议的统计数据。
Python 2的fcntl模块可以调用ioctl,我们先定义I_PUSH的常量(对应Solaris头文件里的_IO('I', 1)),然后执行加载:
import os import fcntl # 定义Solaris的I_PUSH命令常量 I_PUSH = 0x80490001 # 对应_IO('I', 1)的计算值 # 打开/dev/arp sd = os.open('/dev/arp', os.O_RDWR) # 加载需要的STREAMS模块 for mod in ['tcp', 'udp', 'icmp']: fcntl.ioctl(sd, I_PUSH, mod)
2. 用ctypes封装getmsg/putmsg和StrBuf结构
getmsg和putmsg是POSIX标准的STREAMS调用,在Solaris的libc.so.1中存在,我们可以用ctypes直接调用。首先需要定义C语言中的struct strbuf结构,然后绑定函数原型:
import ctypes from ctypes import cdll, c_int, c_char_p, POINTER # 定义struct strbuf的Python对应结构 class StrBuf(ctypes.Structure): _fields_ = [ ("maxlen", c_int), # 缓冲区最大长度 ("len", c_int), # 实际数据长度 ("buf", c_char_p) # 缓冲区指针 ] # 加载Solaris的libc库 libc = cdll.LoadLibrary('libc.so.1') # 绑定getmsg函数的参数和返回值类型 libc.getmsg.argtypes = [c_int, POINTER(StrBuf), POINTER(StrBuf), POINTER(c_int)] libc.getmsg.restype = c_int # 绑定putmsg函数(后续发送MIB查询需要用到) libc.putmsg.argtypes = [c_int, POINTER(StrBuf), POINTER(StrBuf), c_int] libc.putmsg.restype = c_int
3. 实现MIB查询与数据读取
接下来的逻辑和netstat.c里的mibget一致:构造一个控制缓冲区(ctlbuf)发送MIB查询请求,然后循环调用getmsg读取返回的数据。
比如,以查询TCP统计信息为例,你需要构造对应的MIB请求(参考Illumos的netstat.c里的TCPCMD定义),核心调用逻辑如下:
# 初始化flags变量 flags = c_int(0) # 构造控制缓冲区:以TCP统计查询为例,具体内容参考netstat.c的MIB定义 ctl_buf = StrBuf() ctl_buf.maxlen = 1024 # 足够容纳MIB请求的缓冲区 ctl_buf.buf = ctypes.create_string_buffer(1024) # 这里需要填充MIB查询命令,比如netstat.c里的TCPCMD相关内容 # 示例:假设我们要获取TCP全局统计,填充对应的命令字节(实际需替换为正确的MIB指令) ctl_buf.len = 4 ctypes.memmove(ctl_buf.buf, '\x00\x00\x00\x01', 4) # 发送putmsg请求 ret = libc.putmsg(sd, ctypes.byref(ctl_buf), None, 0) if ret != 0: raise OSError("putmsg failed") # 循环读取getmsg返回的数据 data_buf = StrBuf() data_buf.maxlen = 4096 data_buf.buf = ctypes.create_string_buffer(4096) while True: ret = libc.getmsg(sd, None, ctypes.byref(data_buf), ctypes.byref(flags)) if ret == -1: break # 读取完毕或出错 if data_buf.len > 0: # 处理读取到的二进制数据,解析成netstat需要的统计字段 # 解析逻辑需要参考netstat.c里的MIB数据格式(如struct tcpstat) print("Received data length:", data_buf.len) print("Raw data:", data_buf.buf[:data_buf.len])
关键注意事项
- MIB命令与数据解析:你需要对照Illumos的
netstat.c里的MIB定义(比如struct tcpstat的内存结构),把读取到的二进制数据解析成对应的统计字段,这部分可以用ctypes定义对应结构体来快速解析,或者手动处理字节序和偏移。 - 错误处理:要添加系统调用的错误检查(比如
getmsg返回-1时,用ctypes.get_errno()获取错误码排查问题)。 - psutil参考:psutil的Solaris netstat实现确实用了类似思路——通过
ctypes调用getmsg/putmsg和ioctl,你可以参考它的源码逻辑来完善MIB命令和数据解析的细节。
内容的提问来源于stack exchange,提问作者drop table

