之前一直奇怪应用程序是如何得到键盘输入的,首先这肯定与键盘的驱动程序有关,但驱动程序是如何把键盘输入传递给用户进程的?这个问题在用了windows下一个按键精灵后达到了顶峰,该案件精灵使用一个脚本表达模拟发送的键盘按键和鼠标移动,之后可以自动进行操作,这说明上层应用有操作鼠标键盘的能力.查找资料后,发现原来对于linux,鼠标和键盘驱动是这样写的:
鼠标\键盘的输入会转为/dev/input下的io事件event*,设备对应的event号可通过cat /proc/bus/input/devices查看.比如我查看该文件的内容:
I: Bus=0019 Vendor=0000 Product=0003 Version=0000
N: Name="Sleep Button"
P: Phys=PNP0C0E/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0E:00/input/input0
U: Uniq=
H: Handlers=kbd event0
B: PROP=0
B: EV=3
B: KEY=4000 0 0
I: Bus=0019 Vendor=0000 Product=0001 Version=0000
N: Name="Power Button"
P: Phys=PNP0C0C/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0C:00/input/input1
U: Uniq=
H: Handlers=kbd event1
B: PROP=0
B: EV=3
B: KEY=10000000000000 0
I: Bus=0019 Vendor=0000 Product=0001 Version=0000
N: Name="Power Button"
P: Phys=LNXPWRBN/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXPWRBN:00/input/input2
U: Uniq=
H: Handlers=kbd event2
B: PROP=0
B: EV=3
B: KEY=10000000000000 0
I: Bus=0019 Vendor=0000 Product=0006 Version=0000
N: Name="Video Bus"
P: Phys=LNXVIDEO/video/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/LNXVIDEO:00/input/input3
U: Uniq=
H: Handlers=kbd event3
B: PROP=0
B: EV=3
B: KEY=3e000b00000000 0 0 0
I: Bus=0003 Vendor=413c Product=2113 Version=0111
N: Name="Dell KB216 Wired Keyboard"
P: Phys=usb-0000:00:14.0-1/input0
S: Sysfs=/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/0003:413C:2113.0001/input/input4
U: Uniq=
H: Handlers=sysrq kbd event4 leds
B: PROP=0
B: EV=120013
B: KEY=1000000000007 ff9f207ac14057ff febeffdfffefffff fffffffffffffffe
B: MSC=10
B: LED=1f
I: Bus=0003 Vendor=413c Product=2113 Version=0111
N: Name="Dell KB216 Wired Keyboard"
P: Phys=usb-0000:00:14.0-1/input1
S: Sysfs=/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.1/0003:413C:2113.0002/input/input5
U: Uniq=
H: Handlers=kbd event5
B: PROP=0
B: EV=1f
B: KEY=300ff 0 0 483ffff17aff32d bf54444600000000 1 130c730b17c000 267bfad941dfed 9e168000004400 10000002
B: REL=40
B: ABS=100000000
B: MSC=10
I: Bus=0003 Vendor=03f0 Product=134a Version=0111
N: Name="PixArt HP USB Optical Mouse"
P: Phys=usb-0000:00:14.0-2/input0
S: Sysfs=/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/0003:03F0:134A.0003/input/input6
U: Uniq=
H: Handlers=mouse0 event6
B: PROP=0
B: EV=17
B: KEY=ff0000 0 0 0 0
B: REL=103
B: MSC=10
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="HDA Intel PCH Front Mic"
P: Phys=ALSA
S: Sysfs=/devices/pci0000:00/0000:00:1f.3/sound/card0/input7
U: Uniq=
H: Handlers=event7
B: PROP=0
B: EV=21
B: SW=10
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="HDA Intel PCH Rear Mic"
P: Phys=ALSA
S: Sysfs=/devices/pci0000:00/0000:00:1f.3/sound/card0/input8
U: Uniq=
H: Handlers=event8
B: PROP=0
B: EV=21
B: SW=10
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="HDA Intel PCH Line"
P: Phys=ALSA
S: Sysfs=/devices/pci0000:00/0000:00:1f.3/sound/card0/input9
U: Uniq=
H: Handlers=event9
B: PROP=0
B: EV=21
B: SW=2000
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="HDA Intel PCH Line Out Front"
P: Phys=ALSA
S: Sysfs=/devices/pci0000:00/0000:00:1f.3/sound/card0/input10
U: Uniq=
H: Handlers=event10
B: PROP=0
B: EV=21
B: SW=40
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="HDA Intel PCH Line Out Surround"
P: Phys=ALSA
S: Sysfs=/devices/pci0000:00/0000:00:1f.3/sound/card0/input11
U: Uniq=
H: Handlers=event11
B: PROP=0
B: EV=21
B: SW=40
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="HDA Intel PCH Line Out CLFE"
P: Phys=ALSA
S: Sysfs=/devices/pci0000:00/0000:00:1f.3/sound/card0/input12
U: Uniq=
H: Handlers=event12
B: PROP=0
B: EV=21
B: SW=40
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="HDA Intel PCH Front Headphone"
P: Phys=ALSA
S: Sysfs=/devices/pci0000:00/0000:00:1f.3/sound/card0/input13
U: Uniq=
H: Handlers=event13
B: PROP=0
B: EV=21
B: SW=4
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="HDA Intel PCH HDMI/DP,pcm=3"
P: Phys=ALSA
S: Sysfs=/devices/pci0000:00/0000:00:1f.3/sound/card0/input14
U: Uniq=
H: Handlers=event14
B: PROP=0
B: EV=21
B: SW=140
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="HDA Intel PCH HDMI/DP,pcm=7"
P: Phys=ALSA
S: Sysfs=/devices/pci0000:00/0000:00:1f.3/sound/card0/input15
U: Uniq=
H: Handlers=event15
B: PROP=0
B: EV=21
B: SW=140
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="HDA Intel PCH HDMI/DP,pcm=8"
P: Phys=ALSA
S: Sysfs=/devices/pci0000:00/0000:00:1f.3/sound/card0/input16
U: Uniq=
H: Handlers=event16
B: PROP=0
B: EV=21
B: SW=140
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="HDA Intel PCH HDMI/DP,pcm=9"
P: Phys=ALSA
S: Sysfs=/devices/pci0000:00/0000:00:1f.3/sound/card0/input17
U: Uniq=
H: Handlers=event17
B: PROP=0
B: EV=21
B: SW=140
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="HDA Intel PCH HDMI/DP,pcm=10"
P: Phys=ALSA
S: Sysfs=/devices/pci0000:00/0000:00:1f.3/sound/card0/input18
U: Uniq=
H: Handlers=event18
B: PROP=0
B: EV=21
B: SW=140
I: Bus=0019 Vendor=0000 Product=0000 Version=0000
N: Name="Eee PC WMI hotkeys"
P: Phys=eeepc-wmi/input0
S: Sysfs=/devices/platform/eeepc-wmi/input/input19
U: Uniq=
H: Handlers=rfkill kbd event19
B: PROP=0
B: EV=100013
B: KEY=7e40000 0 800000000000 0 0 1400b00100000 300180001100800 e000000000000 2
B: MSC=10
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="HDA NVidia HDMI/DP,pcm=3"
P: Phys=ALSA
S: Sysfs=/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1/input20
U: Uniq=
H: Handlers=event20
B: PROP=0
B: EV=21
B: SW=140
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="HDA NVidia HDMI/DP,pcm=7"
P: Phys=ALSA
S: Sysfs=/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1/input21
U: Uniq=
H: Handlers=event21
B: PROP=0
B: EV=21
B: SW=140
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="HDA NVidia HDMI/DP,pcm=8"
P: Phys=ALSA
S: Sysfs=/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1/input22
U: Uniq=
H: Handlers=event22
B: PROP=0
B: EV=21
B: SW=140
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="HDA NVidia HDMI/DP,pcm=9"
P: Phys=ALSA
S: Sysfs=/devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card1/input23
U: Uniq=
H: Handlers=event23
B: PROP=0
B: EV=21
B: SW=140
鼠标对应event6比较明显,键盘可能是event4或event5,应用程序试了两次发现event5无效,event4有效,至于为什么有2个event对应键盘就不知道了.
应用程序如下:
//读取键盘输入
#include <stdio.h>
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd_kb;
struct input_event event_kb;
fd_kb = open("/dev/input/event4", O_RDONLY); //键盘输入
if (fd_kb <= 0)
{
printf("open device error\n");
return 0;
}
while (1)
{
if (read(fd_kb, &event_kb, sizeof(event_kb)) == sizeof(event_kb))
{
if (event_kb.type == EV_KEY)
{
//if (event_kb.value == 0 || event_kb.value == 1)//1表示按下,0表示释放,会检测到两次
if (event_kb.value == 1) //键按下
{
//printf("key %d %s\n", event_kb.code, (event_kb.value) ? "Pressed" : "Released");
if (event_kb.code == KEY_ESC)
break;
if (event_kb.code == KEY_Q)
printf("q\n");
if (event_kb.code == KEY_W)
printf("w\n");
if (event_kb.code == KEY_E)
printf("e\n");
if (event_kb.code == KEY_R)
printf("r\n");
}
}
}
}
close(fd_kb);
return 0;
}
以上程序不管是不是在前台输入,程序都能捕捉到Q\W\E\R按键,并且多个进程居然可以同时捕捉到事件;推测只让前台进程获取到事件是内核的职责,前台进程是与终端绑定的概念,内核的终端调度程序(不是调度器!)负责把信号传给前台进程,并清掉信号,这就和我们平时看到的现象一致了.按键精灵是通过模拟键盘\鼠标事件实现的,demo程序如下:
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
void simulate_key(int fd, int kval)
{
struct input_event event;
event.type = EV_KEY;
event.value = 1;
event.code = kval;
gettimeofday(&event.time, 0);
write(fd, &event, sizeof(event));
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
write(fd, &event, sizeof(event));
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, NULL);
event.type = EV_KEY;
event.code = kval;
event.value = 0;
write(fd, &event, sizeof(event));
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
write(fd, &event, sizeof(event));
}
void simulate_mouse(int fd)
{
struct input_event event;
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, NULL);
event.type = EV_REL;
event.code = REL_X;
event.value = 10;
write(fd, &event, sizeof(event));
event.type = EV_REL;
event.code = REL_Y;
event.value = 10;
write(fd, &event, sizeof(event));
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
write(fd, &event, sizeof(event));
}
int main()
{
int fd_kbd;
int fd_mouse;
fd_kbd = open("/dev/input/event4", O_RDWR);
if (fd_kbd <= 0)
{
printf("error open keyboard:\n");
return -1;
}
fd_mouse = open("/dev/input/event6", O_RDWR);
if (fd_mouse <= 0)
{
printf("error open mouse\n");
return -2;
}
int i = 0;
for (i = 0; i < 10; i++)
{
simulate_key(fd_kbd, KEY_A + i);
simulate_mouse(fd_mouse);
sleep(1);
}
close(fd_kbd);
}
以上程序居然可以让键盘\鼠标自己输出,自己移动,可以明显看到现象!
参考了:
linux 处理键盘 鼠标事件
Linux如何查看与/dev/input目录下的event对应的设备
linux c语言 模拟键盘输入