在Linux中如何检测按键是否被按下?

3

我需要知道在Linux中哪个中断会检查是否有按键被按下?


你应该调用系统调用来做这件事吗? - Carl Norum
Linux中的系统调用是中断。 - Jens Björnhager
投票关闭,原因不明确。使用什么语言?做什么?C版本:https://dev59.com/THA85IYBdhLWcg3wBOtO - Ciro Santilli OurBigBook.com
1个回答

7
我假设您想在终端仿真器上执行此操作(而不是在X客户端上),并且您不关心按键释放。Linux的方法是使用termios(3)将终端设置为非规范或原始模式,然后使用通常的libc函数读取stdin。
Linux上的系统调用号在/usr/include/asm/unistd.h(或unistd_64.h)上,但termios函数最终会转换为ioctl()。因此,如果由于某种奇怪而不寻常的原因无法调用libc,则必须查找ioctl的系统调用号以及与termios函数对应的ioctl。
编辑:显然,您假设Linux使用与DOS相同的模型,在该模型中,控制台输入是键盘的抽象(具有诸如KEYPRESSED、GETC等功能),而控制台输出是面向字符的显示器的抽象。
Unix/Linux的抽象是关于终端的,可以是物理控制台,串行端口上的终端(或终端仿真器),xterm等。这里的一个重要点是,默认情况下,输入行在终端(或终端仿真器)看到行分隔符之前不会提供给程序。
在POSIX上,这些终端受termios(3)函数控制。 Linux最终将它们转换为ioctl()调用,如下所示(请参见
  • tcgetattr(fd,arg) => ioctl(fd,TCGETS,arg)
  • tcsetattr(fd,TCSANOW,arg) => ioctl(fd,TCSETS,arg)
  • tcsetattr(fd,TCSADRAIN,arg) => ioctl(fd,TCSETSW,arg)
  • tcsetattr(fd,TCSAFLUSH,arg) => ioctl(fd,TCSETSF,arg)
  • ...
  • 因此,使用termios(3)poll(2)执行您要求的操作的C程序(出于简洁和清晰的原因省略了错误检查):
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include <unistd.h>
    #include <poll.h>
    #include <signal.h>
    #include <termios.h>
    #include <sys/ioctl.h>
    
    static sig_atomic_t end = 0;
    
    static void sighandler(int signo)
    {
        end = 1;
    }
    
    int main()
    {
        struct termios oldtio, curtio;
        struct sigaction sa;
    
        /* Save stdin terminal attributes */
        tcgetattr(0, &oldtio);
    
        /* Make sure we exit cleanly */
        memset(&sa, 0, sizeof(struct sigaction));
        sa.sa_handler = sighandler;
        sigaction(SIGINT, &sa, NULL);
        sigaction(SIGQUIT, &sa, NULL);
        sigaction(SIGTERM, &sa, NULL);
    
        /* This is needed to be able to tcsetattr() after a hangup (Ctrl-C)
         * see tcsetattr() on POSIX
         */
        memset(&sa, 0, sizeof(struct sigaction));
        sa.sa_handler = SIG_IGN;
        sigaction(SIGTTOU, &sa, NULL);
    
        /* Set non-canonical no-echo for stdin */
        tcgetattr(0, &curtio);
        curtio.c_lflag &= ~(ICANON | ECHO);
        tcsetattr(0, TCSANOW, &curtio);
    
        /* main loop */
        while (!end) {
                struct pollfd pfds[1];
                int ret;
                char c;
    
                /* See if there is data available */
                pfds[0].fd = 0;
                pfds[0].events = POLLIN;
                ret = poll(pfds, 1, 0);
    
                /* Consume data */
                if (ret > 0) {
                        printf("Data available\n");
                        read(0, &c, 1);
                }
        }
    
        /* restore terminal attributes */
        tcsetattr(0, TCSANOW, &oldtio);
    
        return 0;
    }
    

    现在,ioctlpoll都是系统调用,在/usr/include/asm/unistd.h中可以找到它们的编号(在x86上为54和168),而/usr/include/asm/ioctls.h则包含了你需要的ioctl常量(在x86上为TCGETS=0x5401、TCSETS=0x5402、TCSETSW=0x5403、TCSETSF=0x5404)。

    我想使用中断服务。我知道 DOS 中有这样的中断服务,所以我认为 Linux 也会有。 - Sason
    1
    不,ninjalj已经描述了你所需要的。在Linux(i386架构)上,你可以使用eax中的服务号通过int 0x80来进行系统调用。然而,与DOS不同的是,没有直接的调用“检查按键是否被按下”。原因请参考http://en.wikipedia.org/wiki/Ring_%28computer_security%29和http://en.wikipedia.org/wiki/User_space。类Unix系统使用终端抽象层,除非你想修改Linux本身,否则你必须遵循ninjalj的指示。 - Robie Basak

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