Не удается прочитать PTY (файл псевдотерминала) из внешнего процесса - PullRequest
0 голосов
/ 14 ноября 2018

Я открываю PTY (в Python / Linux) и пишу в него.Я могу читать с него через minicom отлично.Но я не могу читать из него в другой программе на Python (или C ++).Вот свернутый пример:

provider.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, но мне не удалось найти ничего, что я мог бы использовать.

В идеале, мой продюсер мог бы легко открывать и читать (как в моем примере с потребителем), но если я смогу увидеть эту работу, я готов изменить любой конец ...

Ответы [ 2 ]

0 голосов
/ 15 ноября 2018

Мое основное зависание было в настройках pty.Смотрите мой комментарий под ответом @ rici.

Пересмотренный производитель.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
0 голосов
/ 15 ноября 2018

Что заставляет вас думать, что вы не можете открыть PTY?Ничто в вашем коде не предоставляет информацию о том, какой системный вызов был неудачным.

Скорее всего, произошел сбой вызова os.read() с кодом ошибки EAGAIN (он же EWOULDBLOCK), потому что вы открыли PTY в не-режим блокировки.Нет данных для чтения, потому что PTY - это tty, а ttys изначально находятся в режиме «приготовлено», что означает, что ввод не передается потребителю до тех пор, пока не будет отправлен символ конца строки или какой-либо символ прерывания.Minicom, вероятно, переводит pty в «сырой» режим с помощью вызова termios, и вы тоже должны это сделать.

Я бы предположил, что вы на самом деле не хотите переводить PTY в неблокирующий режим.Если вы не настроите опрос событий или цикл выбора, вы будете постоянно получать EAGAIN «ошибок» (которые на самом деле не являются ошибками), и вы действительно не хотите ждать целую секунду, прежде чем пытаться снова.(Также вы не хотите закрывать и снова открывать PTY.) Рекомендуется оставить PTY в режиме блокировки, но сконфигурируйте его так, чтобы он немедленно возвращался при каждом нажатии клавиши (опять же, с termios).

...