linux以C方式和内核交互监听键盘[香橙派搞机日记]

最近在深入研究我的香橙派,不可避免的遇到了怎么认识和使用Linux内核的问题。 我给自己留了一个简单的任务:使用原生C来监听内核,实现读取键盘的消息。

CSDN上也有其他文章来解决这个问题,不过要么是技术不达标(直接和终端交互显然不是我们想要的,这只是一个取巧的方案),要么是收费文章🥲 。楼主也是查阅了很多资料才解决了这个问题。如果这个方案解决了你的问题,还请你点赞,评论,收藏。您的支持是我的动力

如果你要学习知识,那可以从头读下去,如果你要快速使用,可以从 evtest 这个部分开始读,源代码贴在后面

接口在哪里?

在linux中,一切都是文件。哪怕你是一个物理设备,在内核中,你也得有个文件接口!

两个关键文件夹

/dev/input/

所有的设备,都是在dev 目录下统一管理的。让我们看看
/dev/input/

bash 复制代码
orangepi@orangepi3b:/dev/input$ ls
by-id	 event1   event12  event15  event18  event4  event7
by-path  event10  event13  event16  event2   event5  event8
event0	 event11  event14  event17  event3   event6  event9
orangepi@orangepi3b:/dev/input$ ls -l
total 0
drwxr-xr-x 2 root root     220 Apr 11 11:34 by-id
drwxr-xr-x 2 root root     280 Apr 11 11:34 by-path
crw-rw---- 1 root input 13, 64 Apr  6 12:37 event0
crw-rw---- 1 root input 13, 65 Apr  6 12:37 event1
crw-rw---- 1 root input 13, 74 Apr 11 11:34 event10
crw-rw---- 1 root input 13, 75 Apr 11 11:34 event11
crw-rw---- 1 root input 13, 76 Apr 11 11:34 event12
crw-rw---- 1 root input 13, 77 Apr 11 11:34 event13
crw-rw---- 1 root input 13, 78 Apr 11 11:34 event14
crw-rw---- 1 root input 13, 79 Apr 11 11:34 event15
crw-rw---- 1 root input 13, 80 Apr 11 11:34 event16
crw-rw---- 1 root input 13, 81 Apr 11 11:34 event17
crw-rw---- 1 root input 13, 82 Apr 11 11:34 event18
crw-rw---- 1 root input 13, 66 Apr  6 12:37 event2
crw-rw---- 1 root input 13, 67 Apr 11 11:34 event3
crw-rw---- 1 root input 13, 68 Apr 11 11:34 event4
crw-rw---- 1 root input 13, 69 Apr 11 11:34 event5
crw-rw---- 1 root input 13, 70 Apr 11 11:34 event6
crw-rw---- 1 root input 13, 71 Apr 11 11:34 event7
crw-rw---- 1 root input 13, 72 Apr 11 11:34 event8
crw-rw---- 1 root input 13, 73 Apr 11 11:34 event9

每个event代表一个事件,那么,怎么知道事件和设备的对应呢? 这要借助/proc/bus

(这个部分的知识点参考了蓝天居士的博客,他是一位水平非常高的专家,大家可以学习一下)

/proc/bus/input/devices

让我们转到这个目录

bash 复制代码
orangepi@orangepi3b:/proc$ cd bus
orangepi@orangepi3b:/proc/bus$ ls
input  pci
orangepi@orangepi3b:/proc/bus$ cd input
orangepi@orangepi3b:/proc/bus/input$ ls
devices  handlers
orangepi@orangepi3b:/proc/bus/input$ cd devices
bash: cd: devices: Not a directory
orangepi@orangepi3b:/proc/bus/input$ cd devices 
bash: cd: devices: Not a directory
orangepi@orangepi3b:/proc/bus/input$ tree
.
├── devices
└── handlers

0 directories, 2 files

看看设备信息,信息比较长。我们要拆开来看

event0是什么
bash 复制代码
orangepi@orangepi3b:/proc/bus/input$ cat devices 
I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="hdmi_cec_key"
P: Phys=hdmi_cec_key/input0
S: Sysfs=/devices/platform/fe0a0000.hdmi/dw-hdmi-cec.1.auto/input/input0
U: Uniq=
H: Handlers=event0 
B: PROP=0
B: EV=3
B: KEY=10000000000000 0

I: Bus=0019 Vendor=0001 Product=0001 Version=0100 这个信息:设备挂在总线19下,厂商ID(vendor=0001) 产品ID(Product=0001)

产品版本Version=0100

N: Name="hdmi_cec_key" 标识设备名称和用途

P: Phys=hdmi_cec_key/input0 设备的物理路径,标识设备在系统中的物理位置

S: Sysfs=/devices/platform/fe0a0000.hdmi/dw-hdmi-cec.1.auto/input/input0 设备早sysfs 文件系统中的路径,用于内核和用户空间交互

U: Uniq= 设备唯一标识符号,这里为空

H: Handlers=event0 备对应的事件处理程序是event0 标识该设备的输入事件通过events0暴露 这个信息是关键信息

B: PROP=0 设备属性标识,这里为0 标识无特殊属性

B: EV=3 标识设备支持的事件类型

B: KEY=10000000000000 0 标识设备支持的按键键值


鼠标

可见,一个设备可以有多个输入

bash 复制代码
I: Bus=0003 Vendor=046d Product=c08b Version=0111
N: Name="Logitech G502 HERO Gaming Mouse"
P: Phys=usb-fd800000.usb-1.1/input0
S: Sysfs=/devices/platform/fd800000.usb/usb1/1-1/1-1.1/1-1.1:1.0/0003:046D:C08B.0001/input/input3
U: Uniq=0470323E3232
H: Handlers=event3 
B: PROP=0
B: EV=17
B: KEY=ffff0000 0 0 0 0
B: REL=1943
B: MSC=10

I: Bus=0003 Vendor=046d Product=c08b Version=0111
N: Name="Logitech G502 HERO Gaming Mouse Keyboard"
P: Phys=usb-fd800000.usb-1.1/input1
S: Sysfs=/devices/platform/fd800000.usb/usb1/1-1/1-1.1/1-1.1:1.1/0003:046D:C08B.0002/input/input4
U: Uniq=0470323E3232
H: Handlers=sysrq kbd event4 
B: PROP=0
B: EV=100013
B: KEY=1000000000007 ff800000000007ff febeffdfffefffff fffffffffffffffe
B: MSC=10

I: Bus=0003 Vendor=046d Product=c08b Version=0111
N: Name="Logitech G502 HERO Gaming Mouse Consumer Control"
P: Phys=usb-fd800000.usb-1.1/input1
S: Sysfs=/devices/platform/fd800000.usb/usb1/1-1/1-1.1/1-1.1:1.1/0003:046D:C08B.0002/input/input5
U: Uniq=0470323E3232
H: Handlers=kbd event5 
B: PROP=0
B: EV=1f
B: KEY=306ff 0 0 483ffff17aff32d bfd4444600000000 1 130ff38b17c000 677bfad9415fed 9ed68000004400 10000002
B: REL=1040
B: ABS=100000000
B: MSC=10

I: Bus=0003 Vendor=046d Product=c08b Version=0111
N: Name="Logitech G502 HERO Gaming Mouse System Control"
P: Phys=usb-fd800000.usb-1.1/input1
S: Sysfs=/devices/platform/fd800000.usb/usb1/1-1/1-1.1/1-1.1:1.1/0003:046D:C08B.0002/input/input6
U: Uniq=0470323E3232
H: Handlers=kbd event6 
B: PROP=0
B: EV=13
B: KEY=c000 10000000000000 0
B: MSC=10
键盘
bash 复制代码
I: Bus=0003 Vendor=320f Product=5055 Version=0110
N: Name="VXE V87 VXE V87"
P: Phys=usb-fd800000.usb-1.3/input0
S: Sysfs=/devices/platform/fd800000.usb/usb1/1-1/1-1.3/1-1.3:1.0/0003:320F:5055.0003/input/input9
U: Uniq=2020-12-15
H: Handlers=sysrq kbd leds event7 
B: PROP=0
B: EV=120013
B: KEY=1000000000007 ff9f207ac14057ff febeffdfffefffff fffffffffffffffe
B: MSC=10
B: LED=1f


I: Bus=0003 Vendor=320f Product=5055 Version=0110
N: Name="VXE V87 VXE V87 Keyboard"
P: Phys=usb-fd800000.usb-1.3/input1
S: Sysfs=/devices/platform/fd800000.usb/usb1/1-1/1-1.3/1-1.3:1.1/0003:320F:5055.0004/input/input10
U: Uniq=2020-12-15
H: Handlers=kbd event8 
B: PROP=0
B: EV=100013
B: KEY=ff80000000000000 80b0ffcd01cfffff febffbffdffffffe
B: MSC=10

I: Bus=0003 Vendor=320f Product=5055 Version=0110
N: Name="VXE V87 VXE V87 System Control"
P: Phys=usb-fd800000.usb-1.3/input1
S: Sysfs=/devices/platform/fd800000.usb/usb1/1-1/1-1.3/1-1.3:1.1/0003:320F:5055.0004/input/input11
U: Uniq=2020-12-15
H: Handlers=kbd event9 
B: PROP=0
B: EV=13
B: KEY=c000 10000000000000 0
B: MSC=10

I: Bus=0003 Vendor=320f Product=5055 Version=0110
N: Name="VXE V87 VXE V87 Consumer Control"
P: Phys=usb-fd800000.usb-1.3/input1
S: Sysfs=/devices/platform/fd800000.usb/usb1/1-1/1-1.3/1-1.3:1.1/0003:320F:5055.0004/input/input12
U: Uniq=2020-12-15
H: Handlers=kbd event10 
B: PROP=0
B: EV=1f
B: KEY=3f000307ff 0 0 483ffff17aff32d bfd4444600000000 1 130ff38b17d000 677bfad9415fed 19ed68000004400 10000002
B: REL=1040
B: ABS=100000000
B: MSC=10

I: Bus=0003 Vendor=320f Product=5055 Version=0110
N: Name="VXE V87 VXE V87"
P: Phys=usb-fd800000.usb-1.3/input1
S: Sysfs=/devices/platform/fd800000.usb/usb1/1-1/1-1.3/1-1.3:1.1/0003:320F:5055.0004/input/input13
U: Uniq=2020-12-15
H: Handlers=event11 
B: PROP=0
B: EV=9
B: ABS=10000000000

I: Bus=0003 Vendor=320f Product=5055 Version=0110
N: Name="VXE V87 VXE V87 Mouse"
P: Phys=usb-fd800000.usb-1.3/input1
S: Sysfs=/devices/platform/fd800000.usb/usb1/1-1/1-1.3/1-1.3:1.1/0003:320F:5055.0004/input/input14
U: Uniq=2020-12-15
H: Handlers=event12 
B: PROP=0
B: EV=17
B: KEY=1f0000 0 0 0 0
B: REL=1943
B: MSC=10

I: Bus=0003 Vendor=320f Product=5088 Version=0111
N: Name="Telink VXE V87 2.4G Dongle"
P: Phys=usb-fd800000.usb-1.4/input0
S: Sysfs=/devices/platform/fd800000.usb/usb1/1-1/1-1.4/1-1.4:1.0/0003:320F:5088.0007/input/input21
U: Uniq=
H: Handlers=sysrq kbd leds event13 
B: PROP=0
B: EV=120013
B: KEY=1000000000007 ff9f207ac14057ff febeffdfffefffff fffffffffffffffe
B: MSC=10
B: LED=1f

I: Bus=0003 Vendor=320f Product=5088 Version=0111
N: Name="Telink VXE V87 2.4G Dongle Keyboard"
P: Phys=usb-fd800000.usb-1.4/input1
S: Sysfs=/devices/platform/fd800000.usb/usb1/1-1/1-1.4/1-1.4:1.1/0003:320F:5088.0008/input/input22
U: Uniq=
H: Handlers=kbd event14 
B: PROP=0
B: EV=100013
B: KEY=ff80000000000000 80b0ffcd01cfffff febffbffdffffffe
B: MSC=10

I: Bus=0003 Vendor=320f Product=5088 Version=0111
N: Name="Telink VXE V87 2.4G Dongle System Control"
P: Phys=usb-fd800000.usb-1.4/input1
S: Sysfs=/devices/platform/fd800000.usb/usb1/1-1/1-1.4/1-1.4:1.1/0003:320F:5088.0008/input/input23
U: Uniq=
H: Handlers=kbd event15 
B: PROP=0
B: EV=13
B: KEY=c000 10000000000000 0
B: MSC=10

I: Bus=0003 Vendor=320f Product=5088 Version=0111
N: Name="Telink VXE V87 2.4G Dongle Consumer Control"
P: Phys=usb-fd800000.usb-1.4/input1
S: Sysfs=/devices/platform/fd800000.usb/usb1/1-1/1-1.4/1-1.4:1.1/0003:320F:5088.0008/input/input24
U: Uniq=
H: Handlers=kbd event16 
B: PROP=0
B: EV=1f
B: KEY=3f000307ff 0 0 483ffff17aff32d bfd4444600000000 1 130ff38b17d000 677bfad9415fed 19ed68000004400 10000002
B: REL=1040
B: ABS=100000000
B: MSC=10

I: Bus=0003 Vendor=320f Product=5088 Version=0111
N: Name="Telink VXE V87 2.4G Dongle"
P: Phys=usb-fd800000.usb-1.4/input1
S: Sysfs=/devices/platform/fd800000.usb/usb1/1-1/1-1.4/1-1.4:1.1/0003:320F:5088.0008/input/input25
U: Uniq=
H: Handlers=event17 
B: PROP=0
B: EV=9
B: ABS=10000000000

I: Bus=0003 Vendor=320f Product=5088 Version=0111
N: Name="Telink VXE V87 2.4G Dongle Mouse"
P: Phys=usb-fd800000.usb-1.4/input1
S: Sysfs=/devices/platform/fd800000.usb/usb1/1-1/1-1.4/1-1.4:1.1/0003:320F:5088.0008/input/input26
U: Uniq=
H: Handlers=event18 
B: PROP=0
B: EV=17
B: KEY=1f0000 0 0 0 0
B: REL=1943
B: MSC=10

看起来有些复杂。不过我们很容易看出event* 和设备的对应关系。你觉得有些麻烦? 已经有人为我们制作了工具!

使用evtest来测试你的输入设备

bash 复制代码
orangepi@orangepi3b:~/bop_code$ sudo apt install evtest
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
evtest is already the newest version (1:1.34-1).
0 upgraded, 0 newly installed, 0 to remove and 1 not upgraded.
扫描设备
bash 复制代码
orangepi@orangepi3b:~/bop_code$ sudo evtest
No device specified, trying to scan all of /dev/input/event*
Available devices:
/dev/input/event0:      hdmi_cec_key
/dev/input/event1:      rk805 pwrkey
/dev/input/event2:      rockchip-rk809 Headset
/dev/input/event3:      Logitech G502 HERO Gaming Mouse
/dev/input/event4:      Logitech G502 HERO Gaming Mouse Keyboard
/dev/input/event5:      Logitech G502 HERO Gaming Mouse Consumer Control
/dev/input/event6:      Logitech G502 HERO Gaming Mouse System Control
/dev/input/event7:      VXE V87 VXE V87
/dev/input/event8:      VXE V87 VXE V87 Keyboard
/dev/input/event9:      VXE V87 VXE V87 System Control
/dev/input/event10:     VXE V87 VXE V87 Consumer Control
/dev/input/event11:     VXE V87 VXE V87
/dev/input/event12:     VXE V87 VXE V87 Mouse
/dev/input/event13:     Telink VXE V87 2.4G Dongle
/dev/input/event14:     Telink VXE V87 2.4G Dongle Keyboard
/dev/input/event15:     Telink VXE V87 2.4G Dongle System Control
/dev/input/event16:     Telink VXE V87 2.4G Dongle Consumer Control
/dev/input/event17:     Telink VXE V87 2.4G Dongle
/dev/input/event18:     Telink VXE V87 2.4G Dongle Mouse
选取并监听

这里以键盘为例子

bash 复制代码
Select the device event number [0-18]: 7
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x320f product 0x5055 version 0x110
Input device name: "VXE V87 VXE V87"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 1 (KEY_ESC)
    Event code 2 (KEY_1)
    Event code 3 (KEY_2)
    Event code 4 (KEY_3)
    ....
    Event code 190 (KEY_F20)
    Event code 191 (KEY_F21)
    Event code 192 (KEY_F22)
    Event code 193 (KEY_F23)
    Event code 194 (KEY_F24)
    Event code 240 (KEY_UNKNOWN)
  Event type 4 (EV_MSC)
    Event code 4 (MSC_SCAN)
  Event type 17 (EV_LED)
    Event code 0 (LED_NUML) state 0
    Event code 1 (LED_CAPSL) state 0
    Event code 2 (LED_SCROLLL) state 0
    Event code 3 (LED_COMPOSE) state 0
    Event code 4 (LED_KANA) state 0
Key repeat handling:
  Repeat type 20 (EV_REP)
    Repeat code 0 (REP_DELAY)
      Value    250
    Repeat code 1 (REP_PERIOD)
      Value     33
Properties:
Testing ... (interrupt to exit)
Event: time 1744374523.592185, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70028
Event: time 1744374523.592185, type 1 (EV_KEY), code 28 (KEY_ENTER), value 0
Event: time 1744374523.592185, -------------- SYN_REPORT ------------
Event: time 1744374526.435125, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70013
Event: time 1744374526.435125, type 1 (EV_KEY), code 25 (KEY_P), value 1
Event: time 1744374536.039071, -------------- SYN_REPORT ------------
Event: time 1744374536.207057, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70006
Event: time 1744374536.207057, type 1 (EV_KEY), code 46 (KEY_C), value 1
Event: time 1744374536.207057, -------------- SYN_REPORT ------------
Event: time 1744374536.295063, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70006
Event: time 1744374536.295063, type 1 (EV_KEY), code 46 (KEY_C), value 0
Event: time 1744374536.295063, -------------- SYN_REPORT ------------

使用代码实现内核监听

我们在刚刚,已经获得了键盘输入在event7的信息

!!!注意! 你必须使用evtest来观察你的键盘的正确位置!!! 否则代码会失败!

下面按照这个来写,这是源代码

cpp 复制代码
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <linux/input.h>


int main(int argc, char **argv) {
    const char* devloc = "/dev/input/event7";
    int fd = open(devloc, O_RDONLY) ;

    if (fd == -1) {
        perror("无法打开输入设备");
        return 1;
    }
    struct input_event ev;


   while(true)
   {
        ssize_t n = read(fd , &ev ,sizeof(ev));

        if(n ==-1)
        {
            perror("this error  \n");
            break;
        }

        if(n!= sizeof(ev))
        {
            continue;
        }

        if(ev.type ==EV_KEY && ev.value ==1)
        {
            printf(" key!!!\n");
            switch ((ev.code))
            {
            case  KEY_P:
                printf("hello");
                break;
            case  KEY_K:
                printf(" keyboard");
                break;            
            default:
                break;
            }

        }


   }


    close(fd);
 
    return 0;
}

让我们把代码封装成类吧

cpp 复制代码
#include <iostream>
#include <thread>
#include <unordered_map>
#include <functional>


#include <linux/input.h>
#include <fcntl.h>  //open函数在这个库
#include <unistd.h> //read在这个库

class bop_keylistener
{
private:
    int fd;
    std::unordered_map<int, std::function<void()>> keyCallbacks;
    std::thread listenerThread;
    struct input_event ev;


    bool running = true;
    int millsec_wait ;
    void listenkey()
    {
        
        while(running)
        {
            ssize_t n = read(fd , &ev ,sizeof(ev));

            if(n ==-1)
            {
                std::cerr << "this event message  error  "<<std::endl;
            }
    
            if(n!= sizeof(ev))
            {
                continue;
            }
    
            if(ev.type ==EV_KEY && ev.value ==1)
            {
                for(const auto& pair :keyCallbacks)
                {
                    auto keyi=pair.first;
                    if(keyi == ev.code)
                    {
                        pair.second();
                    }
                }

                std::this_thread::sleep_for(std::chrono::milliseconds(millsec_wait));
    
            }

        }
        
    }
public:
    bop_keylistener(const char* devloc , int millsecwait = 50)
    {
        millsec_wait =millsecwait;

        fd = open(devloc, O_RDONLY) ;
        if (fd == -1) {
            perror("无法打开输入设备");
        }

        listenerThread = std::thread(&bop_keylistener::listenkey, this);
    }

    ~bop_keylistener()
    {
        running = false;
        if (listenerThread.joinable()) {
            listenerThread.join();
        }
    }

      // 注册按键和对应的回调函数
      void registerKeyCallback(int key, const std::function<void()>& callback) {
        keyCallbacks[key] = callback;
    }

};


// 示例回调函数
void exampleCallback() {
    std::cout << "Example callback executed!" << std::endl;
}

int main() {

    bop_keylistener mkeyl("/dev/input/event7" );
   

    // 注册按键 'A' 和对应的回调函数
    mkeyl.registerKeyCallback(KEY_1, exampleCallback);

    // 保持主线程运行
    std::this_thread::sleep_for(std::chrono::hours(1));

    return 0;
}

创作不易,还请多多支持

相关推荐
零涂毕业设计7 分钟前
毕业设计模块开发-OLED显示屏(IIC协议0.96寸)STM32 ESP32 FPGA Linux驱动免费分享
linux·stm32·单片机·嵌入式·esp32·fpga·oled
tedcloud12318 分钟前
OmniRoute怎么部署?开源AI模型路由平台Linux部署教程
linux·服务器·人工智能·开源·音视频
SakitamaX35 分钟前
LVS(Linux Virtual Server)概念详解
linux·运维·lvs
小张同学a.44 分钟前
Linux系统管理
linux·运维
阿龙的工作记录1 小时前
深入理解Linux设备驱动模型
linux·嵌入式硬件·工作记录
代码村新手1 小时前
Linux的基本指令
linux·运维·服务器
aaaameliaaa3 小时前
指针之总结
c语言·笔记·算法
agathakuan4 小时前
Wireshark 解密並導出TLS 1.2 / TLS 1.3 明文的方法(可控制 Client 端)
c语言·wireshark·ssl·openwrt
caimouse4 小时前
protoc-gen-c 支持 proto3 `optional` 关键字修改记录
c语言·学习
撩得Android一次心动4 小时前
Linux编程笔记3【个人用】
linux·笔记