Android Framework 输入子系统(01)核心机制 inotify和epoll

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/vviccc/article/details/91350407

系列文章解读&说明:

Android Framework 输入子系统 的 分析主要分为以下部分:

(01)核心机制 inotify和epoll

(02)核心机制 双向通信(socketpair+binder)

(03)输入系统框架

(04)InputReader解读

(05)InputDispatcher线程

(06)APP建立联系

(07)activity window decor view

(08)InputStage简介

本模块分享的内容:核心机制 inotify和epoll

本章关键点总结 & 说明:

本章节主要关注➕ 以上思维导图即可。

inotify机制类似 PC端的hotplug热插拔机制(Hotplug机制案例说明:内核发现键盘接入/拔出->启动hotplug进程->消息传入输入系统),但这种机制过于繁琐,因此Android并不使用这种机制,而是在输入系统中使用inotify检测目录/dev/input下的文件变化。接下来,怎么知道哪个键盘被按下?Android使用 epoll机制,这是一个监听文件中内容变化的机制。

总结下:

  1. intotify监听 文件 增加/删除的 变化,对应实际操作中设备的变化。
  2. epoll监听文件中内容的变化,对应实际操作中设备中数据的变化。

首先使用案例的方式说明inofity和epoll的使用,最后综合使用两种机制来完成上面1和2两种功能。这里世界上都是参照EventHub的实现来抽取部分内容来做demo,目的是让我们更清楚其中的独立机制。

1 inotify机制案例

1.1 代码如下:

#include <unistd.h>
#include <stdio.h>
#include <sys/inotify.h>
#include <string.h>
#include <errno.h>

int read_process_inotify_fd(int fd)
{
    int res;
    char event_buf[512];
    int event_size;
    int event_pos = 0;
    struct inotify_event *event;

    //注意:这里一次返回1-N个inotify_event 结构体
    res = read(fd, event_buf, sizeof(event_buf));//读到的数据放入event_buf
    if(res < (int)sizeof(*event)) {
        if(errno == EINTR)
            return 0;
        printf("could not get event, %s\n", strerror(errno));
        return -1;
    }

    /* 读到的数据是1个或多个inotify_event,它们的长度不一样,逐个处理,逐个取出event_buf里面的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;
}

/* Usage: inotify <dir> */
 int main(int argc, char **argv)
 {
    int mINotifyFd;
    int result;

    if(argc != 2)
    {
        printf("Usage: %s <dir>\n", argv[0]);
        return -1;
    }

    mINotifyFd = inotify_init();
    result = inotify_add_watch(mINotifyFd, argv[1], IN_DELETE | IN_CREATE);
    while (1)
    {
        read_process_inotify_fd(mINotifyFd); //读数据
    }
    return 0;
 }

1.2 总结下inotify机制的整体步骤:

  1. fd = inotify_init()
  2. inotify_add_watch(监听 目录/文件,创建/删除)
  3. read(fd )只要有文件发生变化(创建/删除)则返回,返回一个或多个inotify_event结构体,结构体内容如下:
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 */
};

1.3 测试步骤

gcc -o inotify inotify.c
mkdir tmp
./inotify tmp & //测试
echo > tmp/1 //在tmp文件夹下创建文件1
echo > tmp/2 //同上
rm tmp/1 tmp/2

2 epoll机制案例

2.1 代码如下:

#include <sys/epoll.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define DATA_MAX_LEN 500

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; //监听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);
}

//usage: epoll <file1> [file2] [file3] ...
int main(int argc, char **argv)
{
    int mEpollFd;
    int i;
    char buf[DATA_MAX_LEN];

    //每次调用epoll_wait,最多可得到16个事件
    static const int EPOLL_MAX_EVENTS = 16;
    struct epoll_event mPendingEventItems[EPOLL_MAX_EVENTS];

    if (argc < 2)
    {
        printf("Usage: %s <file1> [file2] [file3] ...\n", argv[0]);
        return -1;
    }

    mEpollFd = epoll_create(8);
    for (i = 1; i < argc; i++)   
    {
        int tmpFd = open(argv[i], O_RDWR);
        add_to_epoll(tmpFd, mEpollFd);
    }

    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);
            
            //从 mPendingEventItems[i].data.fd 读到的数据放到 buf 里面
            int len = read(mPendingEventItems[i].data.fd, buf, DATA_MAX_LEN);
            buf[len] = '\0';
            printf("get data: %s\n", buf);
            //sleep(5);
        }
    }
    return 0;
}

2.2 epoll的实现步骤总结:

  1. epoll_create
  2. 对每个文件,执行epoll_ctl(…, epoll_ctl_add, …),表示要检测它
  3. 执行epoll_wait(等待某个文件可用)
  4. 不再想检测某个文件:epoll_ctl(…, epoll_ctl_del, …)

2.3 测试步骤

gcc -o epoll epoll.c
mkdir tmp
mkfifo tmp/1 tmp/2 tmp/3
./epoll tmp/1 tmp/2 tmp/3 &
echo aaa > tmp/1
echo bbb > tmp/2

3 inotify和epoll综合案例

3.1 代码如下

#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];

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

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

    while(res >= (int)sizeof(*event)) {
        event = (struct inotify_event *)(event_buf + event_pos);
        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;
    static const int EPOLL_MAX_EVENTS = 16;
    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;
}

3.2 流程总结如下为代码所示:

初始化{
	epoll_create
	fd = inotify_init()
	inotify_add_watch(监听 目录/文件,创建/删除)
}
while(1){
	对每个文件,执行epoll_ctl(…, epoll_ctl_add, …),表示要检测它
	执行epoll_wait(等待某个文件可用)
	if(设备事件){
		read(fd )只要有文件发生变化(创建/删除)则返回,返回一个或多个inotify_event结构体
			{如果创建文件,直接打开并加入到epoll的监听队列中}
			{如果删除文件,通过文件名找到对应句柄,并从epoll监听队列中删除}
	}else(普通事件){
		读取事件
		打印信息
	}
}

3.3 测试步骤简述

该案例就综合上面案例1 和 案例2 所有的功能。即测试流程按照 案例1和案例2均可。

猜你喜欢

转载自blog.csdn.net/vviccc/article/details/91350407