我正在打开PTY(在Python / Linux中)并对其进行写入。我可以通过minicom
完美地阅读它。但是,我无法在另一个Python(或C ++)程序中读取它。这是一个最小化的示例:
producer.py(打开pty /对其进行写入):
import os, sys
from time import sleep
master_fd, slave_fd = os.openpty()
print "minicom -D %s" % os.ttyname( slave_fd )
for i in range(0,30):
d = str(i % 10)
os.write( master_fd, d )
sys.stdout.write( d )
sys.stdout.flush()
sleep( 2 )
os.close( slave_fd )
os.close( master_fd )
print "\nDone"
consumer.py(尝试打开/读取):
import os, sys
from time import sleep
pts=raw_input("Enter pts number:")
while True:
fd=0
try:
fd=os.open('/dev/pts/%d' % (pts,),
os.O_RDONLY | os.O_NONBLOCK )
sys.stdout.write( os.read(fd, 1 ) )
sys.stdout.flush()
except Exception as e: print e
if fd: os.close(fd)
sleep(1)
读取结果始终为:
[Errno 11]资源暂时不可用
如果我以阻止模式阅读,它只会阻止直到制作人终止。然后,它说该文件不存在。
我花了几天的时间来尝试设置各种模式,权限,锁等,但似乎没有任何事情可以使我无所适从。这种事情可以很容易地与常规文件配合使用。另外,请再次注意, minicom可以毫无障碍地阅读pty 。此外,使用lsof
可以看到minicom和我的consumer.py脚本确实打开了文件-在python示例中,仅读取无效。那么,minicom的秘密是什么?我尝试在minicom源代码中找到这样的内容,但没有成功找到我可以使用的任何内容。
理想情况下,我的制作人可以很容易地打开和阅读(就像在我的消费者示例中一样),但是如果我能看到此作品,那么我愿意修改任一端...
答案 0 :(得分:2)
是什么让您认为无法打开PTY?您的代码中没有任何内容提供有关哪个系统调用失败的信息。
最可能的情况是,os.read()
调用失败,错误代码为EAGAIN
(又名EWOULDBLOCK
),因为您已在非阻塞模式下打开了PTY。因为PTY是tty,所以没有要读取的数据,并且ttys最初处于“煮熟”模式,这意味着在发送行尾字符或某些中断字符之前,不会将任何输入传递给使用者。 Minicom可能会通过termios调用将pty置于“原始”模式,您也应该这样做。
我想您真的不希望将PTY置于非阻止模式。除非您设置事件轮询或选择循环,否则您将反复遇到EAGAIN
个“错误”(这不是真正的错误),并且您真的不想等待一秒钟再尝试。 (您也不是真的要关闭并重新打开PTY。)最好将PTY保留为阻塞模式,但将其配置为在每次击键时立即返回(再次使用termios
)。
答案 1 :(得分:0)
我的主要挂断是在pty设置中。看到我在@rici的回答下的评论。
修订后的producer.py:
import os, sys
from time import sleep
import fcntl
import termios
# create a new Psdeuo Terminal (pty), with a dynamically
# assigned path to it, and display the minicom command to
# open it as a test consumer
master_fd, slave_fd = os.openpty()
print "minicom -D %s" % os.ttyname( slave_fd )
# termios attribute index constants
iflag = 0
oflag = 1
cflag = 2
lflag = 3
ispeed = 4
ospeed = 5
cc = 6
# get current pty attributes
termAttr = termios.tcgetattr( master_fd )
# disable canonical and echo modes
termAttr[lflag] &= ~termios.ICANON & ~termios.ECHO
# disable interrupt, quit, and suspend character processing
termAttr[cc][termios.VINTR] = '\x00'
termAttr[cc][termios.VQUIT] = '\x00'
termAttr[cc][termios.VSUSP] = '\x00'
# set revised pty attributes immeaditely
termios.tcsetattr( master_fd, termios.TCSANOW, termAttr )
# enable non-blocking mode on the file descriptor
flags = fcntl.fcntl( master_fd, fcntl.F_GETFL )
flags |= os.O_NONBLOCK
fcntl.fcntl( master_fd, fcntl.F_SETFL, flags )
# write some example data for a couple of minutes
for i in range(0,60):
d = str(i % 10)
os.write( master_fd, d )
sys.stdout.write( d )
sys.stdout.flush()
sleep( 2 )
# release the resources
os.close( slave_fd )
os.close( master_fd )
print "\nDone"
修改后的consumer.py:
import os, sys
from time import sleep
from errno import EAGAIN, EBUSY
ERRORS_TO_IGNORE = [EAGAIN, EBUSY]
# the PTS will be dynamically assigned to the producer,
# so the consumer needs to have that supplied
pts=raw_input("Enter pts number:")
fd=None
print "Press Ctrl+Z to exit"
while True:
sleep(1)
# if the pty is not open, attempt to open it
# in readonly, non-blocking mode
try:
if not fd:
fd=os.open('/dev/pts/%s' % (pts,),
os.O_RDONLY | os.O_NONBLOCK )
except Exception as e:
print e
if fd: fd = os.close(fd)
continue
# attempt to read/pop a character from the stream
# and then display it in this terminal
try:
c = os.read( fd, 1 )
sys.stdout.write( str(c) )
sys.stdout.flush()
except Exception as e:
# ignore some "normal" / "race condition" errors
if( isinstance(e, OSError) and
e.errno in ERRORS_TO_IGNORE ):pass
else : print e