在Linux上,我如何使用系统调用等待按键中断?

3

我希望在我的nasm程序中,当用户按下像F1-12这样的特殊按键时,能够接收到一个中断。我只需要在我的主函数开头等待一个功能按键即可。我知道在BIOS中可以使用int 16h来实现,并返回一个扫描码。但是在Linux下该怎么做呢?


不那么容易!这可能会帮助你:http://www.unusedino.de/linuxassembly/articles/rawkb.html - Frank Kotler
谢谢,我已经尝试过了,但它不起作用。我也尝试使用/dev/tty,但没有结果。 - freakz
1个回答

4
这需要一些复杂的代码;最终我用原始ioctl、read和write方法找到了如何在C中检查F1。如果您熟悉汇编和Linux系统调用,那么将其转换为nasm应该很简单。
这并不完全符合您的要求,因为它只检查F1,而不是其他按键。F1的序列是0x1b、0x4f、0x50。您可以使用“od -t x1”命令查找其他序列,并按下相应的按键。例如,F2是0x1b、0x4f、0x51。
基本思路是获取当前终端属性,将其更新为原始属性(cfmakeraw),然后再设置回去。这需要使用ioctl系统调用。
在原始模式下的终端上,“read()”将获取用户输入的任何字符,而“cooked”模式下内核则使用退格键和控制-u进行行编辑,直到用户通过按Enter或Control-D(EOF)提交该行。
#include <unistd.h>
#include <sys/ioctl.h>
#include <termios.h>

struct ktermios {
    tcflag_t c_iflag;
    tcflag_t c_oflag;
    tcflag_t c_cflag;
    tcflag_t c_lflag;
    cc_t c_line;
    cc_t c_cc[19];
};

int getch() {
    unsigned char c;
    read(0, &c, sizeof(c));
    return c;
}

int main(int argc, char *argv[]) {
    struct ktermios orig, new;
    ioctl(0, TCGETS, &orig);
    ioctl(0, TCGETS, &new);   // or more simply  new = orig;

    // from cfmakeraw documentation
    new.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    new.c_oflag &= ~OPOST;
    new.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    new.c_cflag &= ~(CSIZE | PARENB);
    new.c_cflag |= CS8;

    ioctl(0, TCSETS, &new);

    while (1) {
        if (getch() == 0x1b && getch() == 0x4f && getch() == 0x50) {
            break;
        }
    }

    write(1, "Got F1!\n", 8);
    ioctl(0, TCSETS, &orig);    // restore original settings before exiting!
    return 0;
}

我基于这个答案进行了翻译,这个答案非常有帮助。

1
你不需要非阻塞;如果你只是要在read()上旋转,使用阻塞式的read即可!这里只有原始部分是一个好主意。如果你有一个游戏循环,在每一帧或模拟步骤中想要检查按键,那么是的,你需要非阻塞(或使用select),但这只是一个没有理由的巨大的CPU时间浪费。(就像你的汇编版本的答案一样,你不需要2个ioctl系统调用,只需执行new = orig,然后修改它即可。) - Peter Cordes
实际上,这段代码没有使用 fcntl(0, F_SETFL, O_NONBLOCK) 来使 stdin 非阻塞,因此 read(2) 会一直阻塞,直到有按键输入。编辑以删除答案文本中的错误建议。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接