
저는 직렬 포트를 에뮬레이트하기 위해 Linux의 tty 하위 시스템을 사용하고 있습니다. 아날로그 직렬 포트는 물리적 직렬 포트가 필요한 애플리케이션에서 사용됩니다. 제 경우에는 애플리케이션이 Docker에서 실행되지만 Docker는 이 문제와 관련이 없다고 생각합니다. 그러나 시뮬레이션된 직렬 포트를 관리하는 애플리케이션이 잠겨 있는 문제가 발생했습니다 write
. 통화가 차단되지 않도록 하거나 언제 차단되는지 감지할 수 있는 방법이 있는지 궁금합니다.
시뮬레이션된 직렬 포트를 관리하고 이들 사이에 브로드캐스트를 수행하는 Python 애플리케이션이 있습니다. 즉, 하나의 애플리케이션이 해당 직렬 포트에 쓰면 네트워크의 다른 모든 직렬 포트가 쓰기 정보를 받습니다. 응용 프로그램은 다음과 같습니다.
#!/usr/bin/env python3
import os
import pty
import selectors
import signal
import tty
# read the configuration
all_apps = ...
selector = selectors.DefaultSelector()
# create the emulated serial ports
master_fds = { }
for app in all_apps:
app_master, app_slave = pty.openpty()
tty.setraw(app_master)
master_fds[app] = app_master
selector.register(app_master, selectors.EVENT_READ, app_master)
# make the symlink that gets picked up by the application
os.symlink(os.ttyname(app_slave), "/run/my-app/{}/ttyS0".format(app))
# exit on SIGINT or SIGTERM
run_flag = True
def signal_handler(sig, stack):
global run_flag
run_flag = False
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
# main loop
while run_flag:
events = selector.select(timeout = 1)
for key,mask in events:
fd = key.data
# read up to 1024 bytes of data from the app that sent the message
data = os.read(fd, 1024)
for app_fd in master_fds.values():
# don't broadcast to the application that sent the message
if fd == app_fd:
continue
# send the message to each application
os.write(app_fd, data)
# cleanup
for key in master_fds.keys():
os.remove("/run/my-app/{}/ttyS0".format(key))
os.close(master_fds[key])
일반적으로 이 스크립트는 잘 작동합니다. 응용 프로그램이 시작되면 pty 장치를 직렬 포트로 종속시키고 응용 프로그램 간에 메시지를 브로드캐스트합니다. 그러나 어떤 시점에서는 스크립트가 중단될 수 있습니다. 저는 스크립트가 호출되고 있다고 생각하며 os.write
응용 프로그램이 pty 마스터 중 하나에 전송될 때만 호출됩니다. 이 상태에서는 스크립트가 신호에 응답하지 않습니다(루프가 실행되는 동안에만 run_flag를 확인하고 os.write
루프를 차단하기 때문입니다).
나에게 이해가 되는 유일한 설명은 적어도 하나의 응용 프로그램이 pty의 슬레이브 측에서 데이터를 올바르게 읽지 못한다는 것입니다. 이것이 사실이라면 커널의 일부 pty 지원 버퍼가 가득 차고 호출이 write
버퍼를 오버플로하기 때문에 버퍼가 충분히 소진될 때까지 호출이 차단됩니다(이런 일은 결코 발생하지 않습니다).
cat
포트의 슬레이브 측에서 실행하여 에뮬레이트된 직렬 포트를 소모 하는 것이 때때로 가능하다는 것을 발견했습니다 . 그러나 os.write
호출이 일반적으로 차단될 때 사람의 개입 없이는 차단되지 않도록 충분히 신뢰할 수 있는 애플리케이션이 필요합니다 .
pty 마스터에 대한 호출을 차단 하지 않도록 pty 지원 버퍼의 남은 용량을 측정하는 방법(예: 내 Python 스크립트)이 있습니까 write
?
답변1
해결책을 찾은 것 같지만 반드시 이상적인 것인지는 모르겠습니다.
pty 마스터를 비차단 모드로 설정하도록 스크립트를 편집할 수 있습니다.
import fcntl
...
for app in all_apps:
app_master, app_slave = pty.openpty()
flags = fcntl.fcntl(app_master, fcntl.F_GETFL)
flags |= os.O_NONBLOCK
fnctl.fcntl(app_master, fcntl.F_SETFL, flags)
그런 다음 호스트에 쓸 때마다 쓰기가 정상적으로 차단되면 캡처하여 BlockingIOError
계속할 수 있습니다.
# (try to) send the message to each application
try:
os.write(app_fd, data)
except BlockingIOError:
print("Caught BlockingIOError")
이는 애플리케이션이 오작동하고 직렬 포트를 읽을 수 없다는 근본적인 문제를 해결하지 못하지만, 이 솔루션을 사용하면 스크립트는 최소한 해당 작업을 계속 수행할 수 있습니다. 단점은 일부 데이터가 손실될 수 있다는 것입니다(해당 포트를 소모하지 않는 애플리케이션에 브로드캐스트).