簡體   English   中英

從管道實時獲取基於行的輸出

[英]Get line-based output from pipe in real-time

我想實時(接近)實時讀取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秒交付,但在“空”之間捕獲。 為什么沒有在“空”之間傳遞? 這種情況很少發生,但是每隔一秒鍾彈出隊列就會導致沒有包裹交付,這是為什么呢?

在第二個“空”之后的第一個包在10:33:03.408408處捕獲並交付1552905184438,在前一個數據包之后1秒交付,但在“空”之間捕獲。

給定您的代碼,僅當for line in iter(out.readline, b'')返回一個新項時,才計算和打印系統時間戳,因此延遲似乎來自此。

我懷疑stdio緩沖是罪魁禍首。 在Linux(即libc / glibc)上,如果STDOUT描述符引用了TTY,則啟用行緩沖。 如果它引用了其他內容(例如管道),則STDOUT描述符已完全緩沖; 在調用寫入系統調用之前,您的進程需要填充4096字節(Linux上為默認值)。
根據此處顯示的輸出進行非常粗略的計算,您的子進程似乎每〜0.025秒生成〜65個字節。 給定4kB緩沖區,將需要約1.625秒的時間將其填滿並觸發刷新/寫入。

從該subprocess.PIPE進程中讀取subprocess.PIPE並將輸出發送到主進程的stdout所花費的時間要少得多,因此,您將看到帶有tcpdump輸出的突發,即在幾微秒內打印(從stdout迭代器接收到)間隔約25ms,並且您的程序隨后等待,直到刷新下一個4kB。

如果可以安裝第3方軟件包(並使用Python> = 2.7),則可能需要看看pexpect 該程序包的子程序連接到PTY,使系統將它們視為交互式程序,因此其stdout描述符是行緩沖的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM