You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Python2中实现Solaris系统netstat信息收集的技术求助

解决Solaris下Python 2采集netstat数据的getmsg调用问题

你遇到的问题很典型——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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:34:46