我正在尝试从结构if_data64读取字段ifi_ibytes/ifi_obytes,如本头文件所述。我已经让ctype工作,我可以进行sysctl调用,并且从中获得了似乎可用的信息。然而,我从非零整数中获得了似乎完全是垃圾的东西。例如,我有一个接口,接收了0个字节,传输了~850万个字节。然而,我的python代码说接收了0个字节,传输了3,590,592,659,456个字节。
我从这个已知的工作程序中复制了这个方法,我从Python得到的字节计数与上面看到的MenuMeter数字没有任何相似之处。我使用了一个稍微不同的sysctl(NET_RT_IFLIST2),它提供了64位整数,我在代码中考虑到了这一点。
这是我的代码:
from ctypes import *
from ctypes.util import find_library
from ctypes import sizeof as c_sizeof
import errno
libc = CDLL(find_library('c'), use_errno=True)
# hardcoded macosx constants!
CTL_NET = 4
PF_ROUTE = 17
NET_RT_IFLIST2 = 6
RTM_IFINFO2 = 0x12
IFF_LOOPBACK = 0x8
AF_LINK = 18
class if_data64(Structure):
_pack_ = 4
_fields_ = [('ifi_type', c_ubyte),
('ifi_typelen', c_ubyte),
('ifi_physical', c_ubyte),
('ifi_addrlen', c_ubyte),
('ifi_hdrlen', c_ubyte),
('ifi_recvquota', c_ubyte),
('ifi_xmitquota', c_ubyte),
('ifi_unused1', c_ubyte),
('ifi_mtu', c_uint32),
('ifi_metric', c_uint32),
('ifi_baudrate', c_uint32),
('ifi_ipackets', c_uint64),
('ifi_ierrors', c_uint64),
('ifi_opackets', c_uint64),
('ifi_oerrors', c_uint64),
('ifi_collisions', c_uint64),
('ifi_ibytes', c_uint64),
('ifi_obytes', c_uint64),
('ifi_imcasts', c_uint64),
('ifi_omcasts', c_uint64),
('ifi_iqdrops', c_uint64),
('ifi_noproto', c_uint64),
('ifi_recvtiming', c_uint32),
('ifi_xmittiming', c_uint32)]
class if_msghdr2(Structure):
_fields_ = [('ifm_msglen', c_ushort),
('ifm_version', c_ubyte),
('ifm_type', c_ubyte),
('ifm_addrs', c_int),
('ifm_flags', c_int),
('ifm_index', c_ushort),
('ifm_snd_len', c_int),
('ifm_snd_maxlen', c_int),
('ifm_snd_drops', c_int),
('ifm_timer', c_int),
('ifm_data', if_data64)]
class sockaddr_dl(Structure):
_fields_ = [('sdl_len', c_ubyte),
('sdl_family', c_ubyte),
('sdl_index', c_ushort),
('sdl_type', c_ubyte),
('sdl_nlen', c_ubyte),
('sdl_alen', c_ubyte),
('sdl_slen', c_ubyte),
('sdl_data', c_char * 12)] # for now
MIB_TYPE = c_int * 6
mib = MIB_TYPE(CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST2, 0)
sysctl_buf_len = c_uint(0)
rval = libc.sysctl(mib, 6, None, byref(sysctl_buf_len), None, 0)
if rval != 0:
raise Exception(errno.errorcode[get_errno()])
sysctl_buf = create_string_buffer(sysctl_buf_len.value)
rval = libc.sysctl(mib, 6, sysctl_buf, byref(sysctl_buf_len), None, 0)
if rval != 0:
raise Exception(errno.errorcode[get_errno()])
# walk the structure. you need to know the length from ifm_msglen
idx = addressof(sysctl_buf)
end = idx + sysctl_buf_len.value
while idx < end:
batch_off = idx - addressof(sysctl_buf)
ifmsg = cast(c_void_p(idx), POINTER(if_msghdr2))
if ifmsg.contents.ifm_type != RTM_IFINFO2:
idx += ifmsg.contents.ifm_msglen
continue
if ifmsg.contents.ifm_flags & IFF_LOOPBACK:
idx += ifmsg.contents.ifm_msglen
continue
# 12 bytes to compensate for 32 bit alignment
sdl = cast(c_void_p(idx + c_sizeof(if_msghdr2) + 12), POINTER(sockaddr_dl))
if sdl.contents.sdl_family != AF_LINK:
idx += ifmsg.contents.ifm_msglen
continue
print sdl.contents.sdl_data[0:sdl.contents.sdl_nlen]
print ifmsg.contents.ifm_data.ifi_ibytes, ifmsg.contents.ifm_data.ifi_obytes
idx += ifmsg.contents.ifm_msglen
在if_data64
中,ifi_baudrate
字段应该是c_uint64
而不是c_uint32
。修复这个应该对齐ifi_bytes
和ifo_bytes
到正确的偏移量。它还缺少最后一个字段,ifi_lastchange
。这是一个8字节的timval32
结构。总之,这解释了为什么sdl
需要额外的12字节偏移量。
class timeval32(Structure):
_fields_ = [('tv_sec', c_int32),
('tv_usec', c_int32)]
class if_data64(Structure):
_pack_ = 4
_fields_ = [('ifi_type', c_ubyte),
('ifi_typelen', c_ubyte),
('ifi_physical', c_ubyte),
('ifi_addrlen', c_ubyte),
('ifi_hdrlen', c_ubyte),
('ifi_recvquota', c_ubyte),
('ifi_xmitquota', c_ubyte),
('ifi_unused1', c_ubyte),
('ifi_mtu', c_uint32),
('ifi_metric', c_uint32),
('ifi_baudrate', c_uint64), # was c_uint32
('ifi_ipackets', c_uint64),
('ifi_ierrors', c_uint64),
('ifi_opackets', c_uint64),
('ifi_oerrors', c_uint64),
('ifi_collisions', c_uint64),
('ifi_ibytes', c_uint64),
('ifi_obytes', c_uint64),
('ifi_imcasts', c_uint64),
('ifi_omcasts', c_uint64),
('ifi_iqdrops', c_uint64),
('ifi_noproto', c_uint64),
('ifi_recvtiming', c_uint32),
('ifi_xmittiming', c_uint32),
('ifi_lastchange', timeval32)] # was missing
我将使用from_buffer
方法(Python2.6)和偏移量来编写循环,而不是使用带有指针的cast
。
buf_offset = 0
while buf_offset < sysctl_buf_len.value:
msg_offset = buf_offset
ifmsg = if_msghdr2.from_buffer(sysctl_buf, msg_offset)
buf_offset += ifmsg.ifm_msglen
if ifmsg.ifm_type != RTM_IFINFO2 or ifmsg.ifm_flags & IFF_LOOPBACK:
continue
sdl_offset = msg_offset + c_sizeof(if_msghdr2)
sdl = sockaddr_dl.from_buffer(sysctl_buf, sdl_offset)
if sdl.sdl_family != AF_LINK:
continue
print sdl.sdl_data[:sdl.sdl_nlen]
print ifmsg.ifm_data.ifi_ibytes, ifmsg.ifm_data.ifi_obytes