I. overview
I have written several articles about the Input system before, but they are scattered. This article begins to prepare to take a look at the Android Input subsystem based on the Android 11 code. First, let's learn the INotify and Epoll mechanisms under Linux. These two mechanisms mainly monitor the addition and deletion of file nodes under the Input subsystem and the events of file nodes.
II. INotify mechanism
INotify is a mechanism provided by Linux to monitor kernel file system changes in user mode. It can monitor the addition and deletion of files / directories. The usage of INotify is very simple. First, you need to call the following code to create a file descriptor:
int inotifyfd = inotify_init();
Then you need to pass inotify_add_watch adds the events we focus on to the listener:
int wd = inotify_add_watch(inotifyfd, path, event_mask)
inotify_ add_ The first parameter of watch is inotify_init creates a file descriptor. The second parameter is the path to listen to, and the third parameter is the type of event, such as file creation IN_CREATE, file delete IN_DELETE, etc.
After the above two steps are completed, when the events we want to listen to occur in the specified path, they will be written to inotifyfd. At this time, we can read inotifyfd through the read function:
char event_buf[512]; int ret; struct inotify_event *event; ret = read(inotifyfd, event_buf, sizeof(event_buf));
The read information is encapsulated as inotify_event structure. All events can be read out by using the while loop:
while(ret > (int)sizeof(struct inotify_event)) { event = (struct inotify_event*)(event_buf + event_pos); ...... }
inotify_ The information of event structure is as follows:
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 */ };
In fact, INotify can be used in three steps:
- Using inotify_init creates an inotify object
- Using inotify_add_watch listens to the file path
- Use read to read the monitored events
In fact, the Android SDK provides a file monitoring class FileObserver. Its underlying principle is to use the INotify mechanism. If you are interested, you can take a look at several native functions inside it.
Next, enter the practice phase, write a simple INotify test class, and create two classes in the following directory:
main.cpp
//demo code #include <sys/inotify.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <log/log.h> int read_events(int fd) { char event_buf[512]; int ret; int event_pos = 0; int event_size = 0; struct inotify_event *event; ALOGD("dongjiao...block read........"); //Read the events occurring in the target file path through the read function. If there is no event, it will be blocked ret = read(fd, event_buf, sizeof(event_buf)); //The return value of read indicates the actual read event size. If it is less than one event size, it indicates that the read event failed if(ret < (int)sizeof(struct inotify_event)) { ALOGD("dongjiao...read error,could get event"); return -1; } //Take out all event loops while(ret > (int)sizeof(struct inotify_event)) { event = (struct inotify_event*)(event_buf + event_pos); ALOGD("dongjiao...event->len = :%d",event->len); if(event->len) { if(event->mask & IN_CREATE) { //File creation ALOGD("dongjiao...create file:%s successfully \n", event->name); } else { //File deletion ALOGD("dongjiao...delete file:%s successfully \n", event->name); } } event_size = sizeof(struct inotify_event) + event->len; ret -= event_size; event_pos += event_size; } return 0; } int main(int argc, char** argv) { //inotify ends when it reads an event. Here, an endless loop is used while(true){ int inotifyFd; int ret; const char* path = argv[1]; ALOGD("dongjiao...argc = :%d",argc); //Initialize inotify inotifyFd = inotify_init(); if(inotifyFd == -1) { ALOGD("dongjiao...inotify_init error!\n"); return -1; } ALOGD("dongjiao...listen target patch: %s \n", path); //Listen to the target file path. The listening event is file creation IN_CREATE, and file delete IN_DELETE ret = inotify_add_watch(inotifyFd, path, IN_CREATE | IN_DELETE) ; //Wait for the event for the destination file path to occur read_events(inotifyFd); //Delete inotifyFd if(inotify_rm_watch(inotifyFd, ret) == -1) { ALOGD("dongjiao...notify_rm_watch error!\n"); return -1; } //Close inotifyFd close(inotifyFd); } return 0; }
Android.bp
cc_binary { name: "main", srcs: ["main.cpp"], shared_libs: [ "liblog", ], }
Mmm frameworks / native / services / inputlinker / temp /
After successful push into the phone:
Run the executable main:
The log is as follows:
Next, we operate the file in the dev/input directory, and first create a file 1 txt,
Take a look at the log:
Delete file log:
We can see that the use of INotify is still very simple. The Input subsystem uses INotify to listen for node changes in the dev/input / directory. We will see it later.
Three Epoll mechanism
One problem with INotify is that it needs to actively call the read function to read events, which is not what the Input system wants. The Input system needs to notify itself after the target path monitored by INotify changes, rather than actively read. This needs to be implemented in combination with another machine epoll. Epoll is an I/O multiplexing technology, The main function is to monitor fd under Linux. When these fd events occur, epoll will be called back.
Epoll provides three operation functions epoll_create,epoll_ctl,epoll_wait.
1.epoll_create
int epoll_create(int size);
epoll_create is used to create an epoll object, and size is used to tell the kernel the fd number to listen.
2. epoll_ctl
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event)
epoll_ctl is used to perform op operations on file descriptors (fd) to be monitored, epoll_ The first parameter epfd of CTL is epoll_ The return value of create. The second parameter op indicates the operation mode of fd:
EPOLL_CTL_ADD(add to), EPOLL_CTL_DEL(delete), EPOLL_CTL_MOD((modified)
The last parameter, event, indicates the specific event to listen to. This is a structure epoll_event:
typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t; struct epoll_event { uint32_t events; /* Epoll Event type */ epoll_data_t data; /*User data, including fd monitored*/ }
Epoll event types are usually as follows:
EPOLLIN : Indicates that the corresponding file descriptor can be read (including the opposite end) SOCKET Normal shutdown); EPOLLOUT: Indicates that the corresponding file descriptor can be written; EPOLLPRI: Indicates that the corresponding file descriptor has urgent data readability (here it should indicate the arrival of out of band data); EPOLLERR: Indicates that an error occurred in the corresponding file descriptor; EPOLLHUP: Indicates that the corresponding file descriptor is hung up; EPOLLET: take EPOLL Set as edge trigger(Edge Triggered)Mode, which is triggered relative to the horizontal(Level Triggered)Say of EPOLLONESHOT: Only monitor one event. After listening to this event, if you need to continue to monitor this event socket If so, You need to put this again socket Add to EPOLL In the queue EPOLLWAKEUP:The system will keep waking up when events are queued, from epoll_wait The call starts and continues for the next time epoll_wait call
Usually epoll_ This is how CTL is used:
struct epoll_event eventItem = {}; eventItem.events = EPOLLIN | EPOLLWAKEUP; eventItem.data.fd = mINotifyFd; int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem);
- epoll_wait
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
epoll_wait is used to wait for event reporting. The first parameter is epoll_ The return value of create. The second parameter, events, is used to obtain the collection of events obtained by the kernel, usually an epoll_event array. The third parameter maxevents is the maximum number of events, and the fourth parameter is the timeout return time.
The steps for using Epoll are also very simple:
- Through epoll_create creates an epoll object.
- Build an epoll for fd to listen_ Event structure and register with epoll_ctl to monitor.
- Call epoll_wait enters the listening state and an epoll is passed in_ The event structure array is used to collect the monitored events.
- Traverse the epoll in step 3_ Event structure array, and take out the event processing in turn.
Next, let's go to the practice link. We use INotify and Epoll together:
main.cpp
//demo code #include <sys/inotify.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <sys/epoll.h> #include <sys/ioctl.h> #include <log/log.h> int read_events(int fd) { char event_buf[512]; int ret; int event_pos = 0; int event_size = 0; struct inotify_event *event; ALOGD("dongjiao...block read........"); //Read the events occurring in the target file path through the read function. If there is no event, it will be blocked ret = read(fd, event_buf, sizeof(event_buf)); //The return value of read indicates the actual read event size. If it is less than one event size, it indicates that the read event failed if(ret < (int)sizeof(struct inotify_event)) { ALOGD("dongjiao...read error,could get event"); return -1; } //Take out all event loops while(ret > (int)sizeof(struct inotify_event)) { event = (struct inotify_event*)(event_buf + event_pos); ALOGD("dongjiao...event->len = :%d",event->len); if(event->len) { if(event->mask & IN_CREATE) { //File creation ALOGD("dongjiao...create file:%s successfully \n", event->name); } else { //File deletion ALOGD("dongjiao...delete file:%s successfully \n", event->name); } } event_size = sizeof(struct inotify_event) + event->len; ret -= event_size; event_pos += event_size; } return 0; } int main(int argc, char** argv) { //inotify ends when it reads an event. Here, an endless loop is used while(true){ int inotifyFd; int ret; int mEpollFd; int result; int EPOLL_MAX_EVENTS = 16; struct epoll_event mPendingEventItems[EPOLL_MAX_EVENTS]; const char* path = argv[1]; ALOGD("dongjiao...argc = :%d",argc); //Initialize inotify inotifyFd = inotify_init(); //Initialize epoll mEpollFd = epoll_create1(EPOLL_CLOEXEC); //Create a structure epoll that encapsulates inotifyFd_ event struct epoll_event eventItem = {}; eventItem.events = EPOLLIN | EPOLLWAKEUP; eventItem.data.fd = inotifyFd; //Add inotifyFd to epoll for listening result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, inotifyFd, &eventItem); if(inotifyFd == -1) { ALOGD("dongjiao...inotify_init error!\n"); return -1; } ALOGD("dongjiao...listen target patch: %s \n",path); //Listen to the target file path. The listening event is file creation IN_CREATE, and file delete IN_DELETE ret = inotify_add_watch(inotifyFd, path, IN_CREATE | IN_DELETE) ; ALOGD("dongjiao...epoll_wait....."); //Waiting for the event to occur will block int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, -1); ALOGD("dongjiao...epoll event happened pollResult = :%d",pollResult); for(auto &event:mPendingEventItems){ if(event.data.fd == inotifyFd){ //When an event occurs on inotifyFd, the event is read read_events(inotifyFd); } } //Delete inotifyFd if(inotify_rm_watch(inotifyFd, ret) == -1) { ALOGD("dongjiao...notify_rm_watch error!\n"); return -1; } //Close inotifyFd close(inotifyFd); } return 0; }
Android.bp
cc_binary { name: "main", srcs: ["main.cpp"], shared_libs: [ "liblog", ], }
After successful compilation, push to the phone: ADB push out / target / product / bank_ TF/system/bin/main /system/bin/
adb shell /system/bin/main dev/input runs, and the log is as follows:
You can see the blockage in epoll_wait, and then create the test file under dev/input:
The log is as follows:
Epoll will wake up from the blocking after listening to an event on inotifyFd. At this time, we determine that the current fd type is event data. fd = = inotifyFd can read the corresponding events. In this way, we can realize the function of passive monitoring file events by combining INotify and epoll mechanism. In fact, Android Input subsystem does so. The test code I write refers to the relevant implementation of Input. With this foundation, it is very easy to analyze this part of the code of Input system later.