Android系统_输入系统_Linux编程知识inotify和epoll

本文深入解析了Linux系统中的inotify和epoll机制,详细介绍了如何利用这两种机制实现键盘即插即用功能和多键盘输入事件的处理。通过具体的代码示例,展示了inotify监测目录变化和epoll监测文件读写状态的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android系统是基于Linux内核的,所以Android的系统输入就离不开Linux的系统设备输入。而输入设备的连接断开以及输入设备所传递的输入事件是如何被Android系统所感知的呢?下面我们就来了解一下Linux关于输入系统的两个重要机制 inotify和epoll。

问题1、键盘即插即用,即我们是怎么监测键盘接入与拔出的呢?
答:
① hotplug:内核发现键盘接入/拔出,会启动hotplug进程,再发消息告诉输入系统
② inotify:输入系统使用inotify监测目录 /dev/input
问题2、在插入多个键盘使用时,我们是怎么知道哪个键盘被按下?
答:epoll
hotplug的机制过于复杂,在我们Android系统中,使用的是inotify机制。

首先我们来看inotify的使用(监测目录/文件的变化)
① fd = inotify_init() //inotify初始化获得文件句柄
② inotify_add_watch( 目录/文件,创建/删除)//添加对指定的目录或文件的创建或删除的监听
③ read(fd )//开始监听并读取inotify_event 事件,并返回结果

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;
}


编译可执行文件inotify: gcc -o inotify inotify.c
在这里插入图片描述
试验步骤:
mkdir tmp
在这里插入图片描述
./inotify tmp & //对tmp目录进行监测
echo > tmp/1 //在tmp目录下创建文件1
echo > tmp/2 //创建文件2
当监测到文件1 2的创建后,执行了printf create file的打印
在这里插入图片描述
rm tmp/1 tmp/2 //删除文件1 2
当监测到文件1 2被删除,执行了printf delete file的打印
在这里插入图片描述
接下来我们看看epoll的用法(用来监测多个文件:1.有无数据供读出 2.有无空间供写入)
① epoll_create(创建fd)
② 对每个文件,执行epoll_ctl(…,EPOLL_CTL_ADD,…),表示要监测它
③ epoll_wait(等待某个文件可用)
④ 不再想监测某文件:epoll_ctl(…,EPOLL_CTL_DEL,…)

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;
}

编译可执行文件epoll: gcc -o epoll epoll.c
在这里插入图片描述
试验步骤:
mkdir test
mkfifo test/1 test/2 test/3 //创建FIFO(命名管道)1 2 3
./epoll test/1 test/2 test/3 & //监测FIFO 1 2 3
echo aaa > test/1 //向管道输入数据
echo bbb > test/2
在这里插入图片描述
最后我们将inotify和epoll结合起来,来模拟键盘的连接插入事件和按下键盘的输入事件。

inotify_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>
#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;
}

编译可执行文件inotify_epoll: gcc -o inotify_epoll inotify_epoll.c
创建文件夹和命名管道并监测
mkdir tmp
./inotify_epoll tmp/ &
mkfifo tmp/1 tmp/2 tmp/3 //键盘1 2 3 插入
echo aaa > tmp/1 //按下键盘1 输入aaa
echo bbb > tmp/2 //按下键盘2 输入bbb
rm tmp/3 //拔出键盘3
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值