目录
分析
在讲解安卓输入系统之前,我们先了解一下Linux编程基础的inotify与epoll_P,在我们使用笔记本电脑的时候,感觉自带的键盘并不太好用,一般我们都会外接一个键盘,并且当我们按键按下的时候,笔记本能分辨是哪个键盘按下,都会有一下功能:
1.键盘即插即用。
2.可以使用任意一个键盘
那么出现了两个问题:
1.如何检测键盘的接入和拔出
2.怎么知道是哪个键盘按下按键
对于第一个问题有多个:
1.使用hotplug:内核发现键盘插入或者拔出,会启动一个hotplug进程,该进程会发送一个消息给输入系统,然后输入系统进行处理(android系统)
2.inotify:输入系统使用inotify来监测一个/dev/input目录,当有键盘接入或者拔出时,该目录下就会创建或者删除设备节点,该些变化通过inotify就会被察觉,然后传递消息给输入子系统,相对于hotplug少了【启动一个hotplug进程】
对于第二个问题:我们使用epoll机制,epoll可以监测多个文件,每个键盘对应一个文件,那个键盘有输入,对应的文件就会发生变化,这样就知道是哪个键盘在进行输入。
在编写代码之前,我们先简述一下流程
inotify的使用(检测目录/文件的变化)
1.fd = inotify_init()
2.inotify_add_watch(目录/文件,创建/删除)
3.read(fd):返回一个或者多个结构体如下
struct inotify_event {
__s32 wd; /* watch descriptor */
__u32 mask; /* watch mask(发生的变化,创建或者删除) */
__u32 cookie; /* cookie to synchronize two events */
__u32 len; /* length (including nulls) of name (那么的长度)*/
char name[0]; /* stub for possible name(发生变化的文件) */
};
下面我们编写程序,了解怎么实现inotify与epoll,可以参考:SDK/frameworks\native\services\inputflinger\EventHub.cpp
程序编写-inotify
创建inotify.c文件,编写代码如下:
#include <unistd.h>
#include <stdio.h>
#include <sys/inotify.h>
#include <string.h>
#include <errno.h>
/*
*参考: frameworks\native\services\inputflinger\EventHub.cpp
*/
/*Usage: inotify <dir> */
int read_process_inotify_fd(int fd)
{
int res;
char event_buf[512];
int event_size;
int event_pos = 0;
struct inotify_event *event;
/* read */
res = read(fd, event_buf, sizeof(event_buf));
if(res < (int)sizeof(*event)) {
if(errno == EINTR)
return 0;
printf("could not get event, %s\n", strerror(errno));
return -1;
}
/* process
* 读到的数据是1个或多个inotify_event
* 它们的长度不一样
* 逐个处理
*/
while(res >= (int)sizeof(*event)) {
event = (struct inotify_event *)(event_buf + event_pos);
//printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
if(event->len) {
if(event->mask & IN_CREATE) {
printf("create file: %s\n", event->name);
} else {
printf("delete file: %s\n", event->name);
}
}
event_size = sizeof(*event) + event->len;
res -= event_size;
event_pos += event_size;
}
return 0;
}
int main(int argc, char **argv)
{
int mINotifyFd;
int result;
if (argc != 2)
{
printf("Usage: %s <dir>\n", argv[0]);
return -1;
}
/* inotify_init */
mINotifyFd = inotify_init();
/* add watch */
result = inotify_add_watch(mINotifyFd, argv[1], IN_DELETE | IN_CREATE);
/* read */
while (1)
{
read_process_inotify_fd(mINotifyFd);
}
return 0;
}
该代码比较简单所以重诉一下大致流程(从main函数开始看起):
read_process_inotify_fd(int fd)
read(fd, event_buf, sizeof(event_buf));//当我们指定的目录删除或者创建了文件,该函数返回
event->mask & IN_CREATE //判断为创建或者删除
main()
mINotifyFd = inotify_init(); //获取一个句柄
inotify_add_watch(mINotifyFd, argv[1], IN_DELETE | IN_CREATE); //根据传入的argv[1]目录进行监测(监测:创建和删除)
while (1)
read_process_inotify_fd(mINotifyFd);
编写文件之后,我们下载到虚拟机上进行编译执行:gcc inotify.c -o inotify
然后创建一个目录: mkdir tmp
后台运行该程序:./inotify tmp/ &
然后在tmp目录下创建或者删除文件就能看见打印信息
下面我们开始编写epoll.c文件
程序编写-epoll.c
在编写代码之前我们先讲解一个epoll的使用方法,用来检测多个文件(之前检测的目录):
1.有无数据读出
2.有无空间供写入
我们依旧还是参考SDK/frameworks\native\services\inputflinger\EventHub.cpp
创建epoll.c文件,编写代码如下:
#include <sys/epoll.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#if 0
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
#endif
#define DATA_MAX_LEN 500
/* usage: epoll <file1> [file2] [file3] ... */
int add_to_epoll(int fd, int epollFd)
{
int result;
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.fd = fd;
result = epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &eventItem);
return result;
}
void rm_from_epoll(int fd, int epollFd)
{
epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, NULL);
}
int main(int argc, char **argv)
{
int mEpollFd;
int i;
char buf[DATA_MAX_LEN];
// Maximum number of signalled FDs to handle at a time.
static const int EPOLL_MAX_EVENTS = 16;
// The array of pending epoll events and the index of the next event to be handled.
struct epoll_event mPendingEventItems[EPOLL_MAX_EVENTS];
if (argc < 2)
{
printf("Usage: %s <file1> [file2] [file3] ...\n", argv[0]);
return -1;
}
/* epoll_create */
mEpollFd = epoll_create(8);
/* for each file:
* open it
* add it to epoll: epoll_ctl(...EPOLL_CTL_ADD...)
*/
for (i = 1; i < argc; i++)
{
//int tmpFd = open(argv[i], O_RDONLY|O_NONBLOCK);
int tmpFd = open(argv[i], O_RDWR);
add_to_epoll(tmpFd, mEpollFd);
}
/* epoll_wait */
while (1)
{
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, -1);
for (i = 0; i < pollResult; i++)
{
printf("Reason: 0x%x\n", mPendingEventItems[i].events);
int len = read(mPendingEventItems[i].data.fd, buf, DATA_MAX_LEN);
buf[len] = '\0';
printf("get data: %s\n", buf);
//sleep(3);
}
}
return 0;
}
该程序也比较简单,下面列出其大致过程(从main函数开始阅读):
add_to_epoll(int fd, int epollFd)
eventItem.events = EPOLLIN; //监听文件的读操作,即可读时epoll返回
eventItem.data.fd = fd;//设置epoll你需要监听的文件描述符
epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &eventItem) //调用接口函数添加到epoll
main()
mEpollFd = epoll_create(8); //先创建一个epoll,8代表你预计要检测的文件数目
for (i = 1; i < argc; i++)
tmpFd = open(argv[i], O_RDWR) //获得文件描述符
add_to_epoll(tmpFd, mEpollFd); //把文件描述符提交到epill,这样epoll才知道你需要监测那些文件
编写完文件后下载到虚拟机执行:gcc epoll.c -o epoll
在tmp目录下创建两个管道文件:mkfifo 1 2 (注意:一定要使用mkfifo创建管道文件)
后台运行程序:./epoll ./tmp/1 ./tmp/2 &
往文件写入任意文字:echo aaa >> ./tmp/1
然后我们可以看到:
Reason: 0x1
get data: aaa
的打印信息,代表实验成功。
inotify_epoll.c
编写 inotify_epoll.c, 用它来监测tmp/目录: 有文件被创建/删除, 有文件可读出数据
a. 当在tmp/下创建文件时, 会立刻监测到,并且使用epoll监测该文件
b. 当文件有数据时,读出数据
c. 当tmp/下文件被删除时,会立刻监测到,并且把它从epoll中移除不再监测
inotify_epoll.c
gcc -o inotify_epoll inotify_epoll.c
mkdir tmp
./inotify_epoll tmp/ &
mkfifo tmp/1 tmp/2 tmp/3
echo aaa > tmp/1
echo bbb > tmp/2
rm tmp/3
#include <sys/epoll.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/inotify.h>
#include <stdlib.h>
#include <errno.h>
#define DATA_MAX_LEN 500
#define MAX_FILES 1000
static char *base_dir;
static char *epoll_files[MAX_FILES];
#if 0
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
#endif
/* usage: epoll <file1> [file2] [file3] ... */
int add_to_epoll(int fd, int epollFd)
{
int result;
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.fd = fd;
result = epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &eventItem);
return result;
}
void rm_from_epoll(int fd, int epollFd)
{
epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, NULL);
}
int get_epoll_fd_for_name(char *name)
{
int i;
char name_to_find[500];
sprintf(name_to_find, "%s/%s", base_dir, name);
for (i = 0; i < MAX_FILES; i++)
{
if (!epoll_files[i])
continue;
if (!strcmp(epoll_files[i], name_to_find))
return i;
}
return -1;
}
/*
*参考: frameworks\native\services\inputflinger\EventHub.cpp
*/
/*Usage: inotify <dir> */
int read_process_inotify_fd(int mINotifyFd, int mEpollFd)
{
int res;
char event_buf[512];
int event_size;
int event_pos = 0;
struct inotify_event *event;
/* read */
res = read(mINotifyFd, event_buf, sizeof(event_buf));
if(res < (int)sizeof(*event)) {
if(errno == EINTR)
return 0;
printf("could not get event, %s\n", strerror(errno));
return -1;
}
/* process
* 读到的数据是1个或多个inotify_event
* 它们的长度不一样
* 逐个处理
*/
while(res >= (int)sizeof(*event)) {
event = (struct inotify_event *)(event_buf + event_pos);
//printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
if(event->len) {
if(event->mask & IN_CREATE) {
printf("create file: %s\n", event->name);
char *name = malloc(512);
sprintf(name, "%s/%s", base_dir, event->name);
int tmpFd = open(name, O_RDWR);
printf("add to epoll: %s\n", name);
add_to_epoll(tmpFd, mEpollFd);
epoll_files[tmpFd] = name;
} else {
printf("delete file: %s\n", event->name);
int tmpFd = get_epoll_fd_for_name(event->name);
if (tmpFd >= 0)
{
printf("remove from epoll: %s/%s\n", base_dir, event->name);
rm_from_epoll(tmpFd, mEpollFd);
free(epoll_files[tmpFd]);
}
}
}
event_size = sizeof(*event) + event->len;
res -= event_size;
event_pos += event_size;
}
return 0;
}
int main(int argc, char **argv)
{
int mEpollFd;
int i;
char buf[DATA_MAX_LEN];
int mINotifyFd;
int result;
// Maximum number of signalled FDs to handle at a time.
static const int EPOLL_MAX_EVENTS = 16;
// The array of pending epoll events and the index of the next event to be handled.
struct epoll_event mPendingEventItems[EPOLL_MAX_EVENTS];
if (argc != 2)
{
printf("Usage: %s <tmp>\n", argv[0]);
return -1;
}
base_dir = argv[1];
/* epoll_create */
mEpollFd = epoll_create(8);
/* inotify_init */
mINotifyFd = inotify_init();
/* add watch */
result = inotify_add_watch(mINotifyFd, base_dir, IN_DELETE | IN_CREATE);
add_to_epoll(mINotifyFd, mEpollFd);
/* epoll_wait */
while (1)
{
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, -1);
for (i = 0; i < pollResult; i++)
{
if (mPendingEventItems[i].data.fd == mINotifyFd)
{
read_process_inotify_fd(mINotifyFd, mEpollFd);
}
else
{
printf("Reason: 0x%x\n", mPendingEventItems[i].events);
int len = read(mPendingEventItems[i].data.fd, buf, DATA_MAX_LEN);
buf[len] = '\0';
printf("get data: %s\n", buf);
//sleep(3);
}
}
}
return 0;
}