从realtim中的管道获取基于行的输出

2024-04-25 21:49:26 发布

您现在位置:Python中文网/ 问答频道 /正文

我想(近乎)实时地逐行读取tcpdump子进程的输出,但是我需要评估管道是否为空(因此队列)的选项。线程等待0.5秒,获取所有排队的输出行,对其进行处理(例如,包在0.5秒内的平均分布)并返回一些内容。你知道吗

最小非工作示例:

millis = lambda: int(round(time.time() * 1000))
def enqueue_output(out, queue):
    for line in iter(out.readline, b''):
        print(millis())
        print(line)
        queue.put(line)
    out.close()

def infiniteloop1():
    p = Popen( [ 'sudo', 'tcpdump', '-i', 'wlan0', '-nn', '-s0', '-q', '-l', '-p', '-S' ], stdout=subprocess.PIPE, stderr=STDOUT)
    q = Queue()
    t = Thread(target=enqueue_output, args=(p.stdout, q))
    t.daemon = True # thread dies with the program
    t.start()

    while True:
        while True:
            # read line without blocking
            try: 
                row = q.get_nowait() # or q.get(timeout=.1)
            except Empty:
                print('empty')
                break
            else:
                pass
        time.sleep(0.5)
thread1 = threading.Thread(target=infiniteloop1)
thread1.daemon = True
thread1.start()

捕获连续的包流时的输出:

[...]
1552905183422
10:33:03.334167 IP 192.168.1.2.36189 > a.b.c.d.443: tcp 437
1552905183422
10:33:03.357215 IP a.b.c.d.443 > 192.168.1.2.36189: tcp 0
1552905183423
10:33:03.385145 IP 192.168.1.2.36189 > a.b.c.d.443: tcp 437
empty
empty
1552905184438
10:33:03.408408 IP a.b.c.d.443 > 192.168.1.2.36189: tcp 0
1552905184439
10:33:03.428045 IP 192.168.1.2.36189 > a.b.c.d.443: tcp 437
1552905184439
10:33:03.451235 IP a.b.c.d.443 > 192.168.1.2.36189: tcp 0
[...]

注意两个连续的“空”。tcpdump在10:33:03.385145捕获第一个“空”之前的最后一个数据包,并在1552905183423发送到队列,耗时38 ms。在两个“空”之间,没有数据包被发送到队列。第二个“空”之后的第一个包在10:33:03.408408被捕获并交付1552905184438,它在前一个包之后1秒被交付,但在“空”之间被捕获。为什么不在“空”之间传递?这并不是很少发生,但是每一秒弹出队列都会导致没有包被传递,这是为什么呢?你知道吗


Tags: iptrueoutputtime队列deflineout
1条回答
网友
1楼 · 发布于 2024-04-25 21:49:26

The first package after the second "empty" was captured at 10:33:03.408408 and delivered 1552905184438, it was delivered 1 second after the previous packet but captured between the "emptys".

给定您的代码,只有当for line in iter(out.readline, b'')中的迭代器返回一个新项时,才会计算和打印系统时间戳,因此这就是延迟的来源。你知道吗

我怀疑斯特迪奥·布林是罪魁祸首。在Linux(即libc/glibc)上,如果STDOUT描述符引用TTY,则启用行缓冲。如果它引用了其他内容(例如管道),则STDOUT描述符将被完全缓冲;在调用write系统调用之前,您的进程需要填充4096字节(Linux上的默认值)。
非常粗略地计算,根据您在这里显示的输出,您的子进程似乎每~0.025秒生成~65字节。如果有4kB的缓冲区,则需要约1.625秒来填满它并触发刷新/写入。

读取subprocess.PIPE并将输出发送到主进程的stdout所花费的时间要少得多,因此您可以看到带有tcpdump输出的突发,即在几微秒内打印(从stdout迭代器接收)相隔约25ms,随后您的程序将等待下一个4kB被刷新。你知道吗

如果您有可能安装第三方软件包(并使用Python>;=2.7),您可能需要查看pexpect。该包的子包连接到PTY,使系统将它们视为交互式程序,因此它们的标准输出描述符是行缓冲的。你知道吗

相关问题 更多 >