【问题标题】:How to check if a key was pressed in Linux? [closed]如何检查Linux中是否按下了某个键? [关闭]
【发布时间】:2010-10-18 18:40:43
【问题描述】:

我需要知道 Linux 中的哪个中断检查是否按下了任何键?

【问题讨论】:

  • 您不应该进行系统调用来执行此操作吗?
  • Linux ARE 中的系统调用中断。
  • 投票结束,因为不清楚。用什么语言?做什么? C版:stackoverflow.com/questions/2984307/…

标签: linux


【解决方案1】:

我假设您希望在终端模拟器(而不是 X 客户端)上使用此功能,并且您不关心密钥释放。

Linux 的方法是使用 termios(3) 将终端设置为非规范或原始模式,然后使用常用的 libc 函数读取标准输入。

Linux 上的系统调用号位于 /usr/include/asm/unistd.h(或 unistd_64.h)上,但 termios 函数最终会转换为 ioctl() 的。因此,如果由于某些奇怪和不寻常的原因无法调用 libc,则必须查找 ioctl 的系统调用号,以及对应于 termios 函数的 ioctl。

编辑:

显然,您假设 Linux 使用与 DOS 相同的模型,其中控制台输入是键盘的抽象(具有 KEYPRESSED、GETC 等功能),控制台输出是面向字符的显示。

Unix/Linux 抽象是关于 terminals,它可以是物理控制台、串行端口上的终端(或终端仿真器)、xterm,......这里的重点是默认情况下,在终端(或终端仿真器)看到行分隔符之前,程序无法使用输入行。

在 POSIX 上,这些终端由 termios(3) 函数控制。 Linux 最终将这些转换为 ioctl() 调用,如下所示(参见tty_ioctl(4)):

  • 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 也有这样的服务......
  • 不,ninjalj 已经描述了您的需求。在 Linux(i386 架构)上,您可以使用 int 0x80 和 eax 中的服务号进行系统调用。但是,与 DOS 不同,没有直接调用“检查是否按下了键”。原因参见en.wikipedia.org/wiki/Ring_%28computer_security%29en.wikipedia.org/wiki/User_space。类 Unix 系统使用终端抽象,除非您想修改 Linux 本身,否则您必须按照 ninjalj 的说明进行操作。
猜你喜欢
  • 1970-01-01
  • 2018-05-02
  • 2015-04-22
  • 2017-05-26
  • 2017-09-03
  • 1970-01-01
  • 2014-10-12
  • 1970-01-01
相关资源
最近更新 更多