You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python 3.14调用os.read读取^4按键时触发崩溃的问题咨询

问题:Python 3.14.4读取^4按键时崩溃(Arch Linux环境)

环境信息

  • 系统:Arch Linux,内核版本6.19.13-arch1-1
  • Python版本:3.14.4
  • 现象:读取^4按键时解释器崩溃,测试其他按键序列未出现此问题;已知^\对应SIGQUIT信号。

stty配置输出

执行stty -a得到以下终端配置:

speed 38400 baud; rows 28; columns 117; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q;
stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc

测试代码

用于复现问题的测试代码:

#!/usr/bin/env -S python3 -BOO
import atexit
import os
import select
import sys
import termios
import tty

class InpHdlr:
    def __init__(self) -> None:
        self.fd = sys.stdin.fileno()
        self.old_sett = termios.tcgetattr(self.fd)
        atexit.register(self.reset_sett)

    def set_new_sett(self):
        tty.setcbreak(self.fd)

    def reset_sett(self) -> None:
        termios.tcsetattr(self.fd, termios.TCSADRAIN, self.old_sett)

    def kbhit(self, timeout=0):
        r, _, _ = select.select([self.fd], [], [], timeout)
        return bool(r)

    def getch(self) -> str:
        return os.read(self.fd, 1).decode()

def test():
    h = InpHdlr()
    h.set_new_sett()
    k = h.getch()
    while h.kbhit():
        k += h.getch()
    print(repr(k))

test()

注:尝试将os.read(...)替换为sys.stdin.read,问题仍未解决。

核心转储详情

崩溃生成的核心转储信息:

PID: 2928085 (python3.14)
           UID: 1000 (vallu)
           GID: 983 (users)
        Signal: 3 (QUIT)
     Timestamp: Sun 2026-04-26 22:35:39 IST (59s ago)
  Command Line: python3.14 -BOO ./src/test.py
    Executable: /usr/bin/python3.14
 Control Group: /user.slice/user-1000.slice/session-c1.scope
          Unit: session-c1.scope
         Slice: user-1000.slice
       Session: c1
     Owner UID: 1000 (vallu)
       Boot ID: 418b7d396009454f8c6ca39c1f9d6356
    Machine ID: 3bcca37ff0fa439883eb9f848307a600
      Hostname: yaazh
       Storage: /var/lib/systemd/coredump/core.python3\x2e14.1000.418b7d396009454f8c6ca39c1f9d6356.2928085.1777223139000000.zst (present)
  Size on Disk: 636.7K
       Message: Process 2928085 (python3.14) of user 1000 dumped core.

                Stack trace of thread 2928085:
                #0  0x00007f838e4933be n/a (libc.so.6 + 0x933be)
                #1  0x00007f838e4933e4 n/a (libc.so.6 + 0x933e4)
                #2  0x00007f838e50d32e read (libc.so.6 + 0x10d32e)
                #3  0x00007f838ea9ab46 n/a (libpython3.14.so.1.0 + 0x29ab46)
                #4  0x00007f838eadb1a9 n/a (libpython3.14.so.1.0 + 0x2db1a9)
                #5  0x00007f838e97261d PyObject_Vectorcall (libpython3.14.so.1.0 + 0x17261d)
                #6  0x00007f838e986473 _PyEval_EvalFrameDefault (libpython3.14.so.1.0 + 0x186473)
                #7  0x00007f838e9816a5 n/a (libpython3.14.so.1.0 + 0x1816a5)
                #8  0x00007f838ea6f6de PyEval_EvalCode (libpython3.14.so.1.0 + 0x26f6de)
                #9  0x00007f838eab0301 n/a (libpython3.14.so.1.0 + 0x2b0301)
                #10 0x00007f838eab0e5b n/a (libpython3.14.so.1.0 + 0x2b0e5b)
                #11 0x00007f838eab08b7 n/a (libpython3.14.so.1.0 + 0x2b08b7)
                #12 0x00007f838eab0527 n/a (libpython3.14.so.1.0 + 0x2b0527)
                #13 0x00007f838ea61dc8 Py_RunMain (libpython3.14.so.1.0 + 0x261dc8)
                #14 0x00007f838ea5b52b Py_BytesMain (libpython3.14.so.1.0 + 0x25b52b)
                #15 0x00007f838e4276c1 n/a (libc.so.6 + 0x276c1)
                #16 0x00007f838e4277f9 __libc_start_main (libc.so.6 + 0x277f9)
                #17 0x000055909fc90045 _start (/usr/bin/python3.14 + 0x1045)
                ELF object binary architecture: AMD x86-64

解决方案

1. 临时修改终端quit信号映射

运行Python脚本前,执行以下命令取消终端对SIGQUIT信号的字符绑定:

stty quit undef

若需恢复默认设置,执行:

stty quit ^\

2. 在代码中禁用终端信号处理

修改set_new_sett方法,关闭终端的ISIG标志,阻止特殊字符触发系统信号:

def set_new_sett(self):
    tty.setcbreak(self.fd)
    # 获取当前终端属性并修改
    attrs = termios.tcgetattr(self.fd)
    attrs[3] &= ~termios.ISIG  # 关闭isig,禁用终端信号转换
    termios.tcsetattr(self.fd, termios.TCSADRAIN, attrs)

此方法让Python可以直接读取^4字符,而不会被终端转换为SIGQUIT信号。

3. 捕获SIGQUIT信号

在代码中注册信号处理函数,覆盖默认的退出行为:

import signal

def handle_sigquit(signum, frame):
    # 自定义处理逻辑,比如忽略或记录日志
    pass

signal.signal(signal.SIGQUIT, handle_sigquit)

该方法保留终端的isig设置,但需确保信号处理逻辑不影响程序正常运行。

内容的提问来源于stack exchange,提问作者segfault

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.01 18:57:27