26

我正在使用以下 bash 命令模式运行 memcached:

memcached -vv 2>&1 | tee memkeywatch2010098.log 2>&1 | ~/bin/memtracer.py | tee memkeywatchCounts20100908.log

尝试在整个平台范围内追踪无与伦比的 get 设置。

memtracer 脚本在下面,可以按需要工作,但有一个小问题。观察中间日志文件的大小,直到 memkeywatchYMD.log 的大小约为 15-18K,memtracer.py 才开始获取输入。有没有更好的读取标准输入的方法,或者可能有一种方法可以将缓冲区大小减少到 1k 以下以加快响应时间?

#!/usr/bin/python

import sys
from collections import defaultdict

if __name__ == "__main__":


    keys = defaultdict(int)
    GET = 1
    SET = 2
    CLIENT = 1
    SERVER = 2

    #if <
    for line in sys.stdin:
        key = None
        components = line.strip().split(" ")
        #newConn = components[0][1:3]
        direction = CLIENT if components[0].startswith("<") else SERVER

        #if lastConn != newConn:        
        #    lastConn = newConn

        if direction == CLIENT:            
            command = SET if components[1] == "set" else GET
            key = components[2]
            if command == SET:                
                keys[key] -= 1                                                                                    
        elif direction == SERVER:
            command = components[1]
            if command == "sending":
                key = components[3] 
                keys[key] += 1

        if key != None:
            print "%s:%s" % ( key, keys[key], )
4

6 回答 6

37

您可以使用 python 的-u标志从 stdin/stdout 中完全删除缓冲:

-u     : unbuffered binary stdout and stderr (also PYTHONUNBUFFERED=x)
         see man page for details on internal buffering relating to '-u'

并且手册页澄清了:

   -u     Force stdin, stdout and stderr to  be  totally  unbuffered.   On
          systems  where  it matters, also put stdin, stdout and stderr in
          binary mode.  Note that there is internal  buffering  in  xread-
          lines(),  readlines()  and  file-object  iterators ("for line in
          sys.stdin") which is not influenced by  this  option.   To  work
          around  this, you will want to use "sys.stdin.readline()" inside
          a "while 1:" loop.

除此之外,不支持更改现有文件的缓冲,但您可以使用os.fdopen创建一个具有与现有文件描述符相同的底层文件描述符的新文件对象,并且可能具有不同的缓冲。IE,

import os
import sys
newin = os.fdopen(sys.stdin.fileno(), 'r', 100)

应该绑定newin到一个文件对象的名称,该文件对象读取与标准输入相同的 FD,但一次仅缓冲大约 100 个字节(并且您可以sys.stdin = newin从那里继续使用新文件对象作为标准输入)。我说“应该”是因为这个领域曾经在某些平台上存在许多错误和问题(提供具有完全通用性的跨平台功能是相当困难的)——我不确定它现在的状态是什么,但我d 绝对建议在所有感兴趣的平台上进行彻底的测试,以确保一切顺利。( -u,完全删除缓冲,应该可以在所有平台上解决更少的问题,如果这可能满足您的要求)。

于 2010-09-08T17:40:36.983 回答
23

您可以简单地使用sys.stdin.readline()而不是sys.stdin.__iter__()

import sys

while True:
    line = sys.stdin.readline()
    if not line: break # EOF

    sys.stdout.write('> ' + line.upper())

这给了我在 Ubuntu 13.04 上使用 Python 2.7.4 和 Python 3.3.1 的行缓冲读取。

于 2013-08-14T15:03:34.140 回答
12

仍然是行缓冲的,通过使用2 参数形式的迭代sys.stdin.__iter__器,可以拥有一个行为几乎相同的迭代器(在 EOF 处停止,而stdin.__iter__不会):itersys.stdin.readline

import sys

for line in iter(sys.stdin.readline, ''):
    sys.stdout.write('> ' + line.upper())

None作为哨兵提供(但请注意,您需要自己处理 EOF 条件)。

于 2015-03-07T20:50:14.323 回答
4

这在 Python 3.4.3 中对我有用:

import os
import sys

unbuffered_stdin = os.fdopen(sys.stdin.fileno(), 'rb', buffering=0)

文档fdopen()说它只是open().

open()有一个可选buffering参数:

buffering是一个可选整数,用于设置缓冲策略。传递 0 以关闭缓冲(仅在二进制模式下允许),1 以选择行缓冲(仅在文本模式下可用),以及整数 > 1 以指示固定大小的块缓冲区的大小(以字节为单位)。

换句话说:

  • 完全无缓冲的标准输入需要二进制模式并传递零作为缓冲区大小。
  • 行缓冲需要文本模式。
  • 任何其他缓冲区大小似乎都适用于二进制文本模式(根据文档)。
于 2015-12-06T22:52:13.763 回答
2

可能您的麻烦不在于 Python,而在于 Linux shell 在使用管道链接命令时注入的缓冲。当这是问题时,输入不是按行缓冲,而是按 4K 块缓冲。

要停止此缓冲,请在命令链之前加上包中的unbuffer命令expect,例如:

unbuffer memcached -vv 2>&1 | unbuffer -p tee memkeywatch2010098.log 2>&1 | unbuffer -p ~/bin/memtracer.py | tee memkeywatchCounts20100908.log

unbuffer命令-p在管道中间使用时需要该选项。

于 2019-09-18T10:26:32.617 回答
0

我可以用 python 2.7 做到这一点的唯一方法是:

tty.setcbreak(sys.stdin.fileno())

来自Python 非阻塞控制台输入。这完全禁用缓冲并抑制回声。

编辑:关于亚历克斯的回答,-u在我的情况下,第一个命题(用 调用 python)是不可能的(见shebang 限制)。

当我使用 0 或 1 的缓冲区时,第二个命题(用较小的缓冲区复制 fd: os.fdopen(sys.stdin.fileno(), 'r', 100))不起作用,因为它用于交互式输入,我需要立即处理按下的每个字符。

于 2017-02-03T16:45:45.247 回答