第六章:输入系统(1)-必备Linux编程知识_inotify和epoll_P

在讲解安卓输入系统之前,我们先了解一下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
的打印信息,代表实验成功。

猜你喜欢

转载自blog.csdn.net/weixin_43013761/article/details/87728338