epoll高效機制其實就是內核回調,我們知道linux把socket也是當成file處理的,只是底層驅動不一樣。
先來看看內核文件結構:
struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char *, size_t, loff_t *); ssize_t (*write) (struct file *, const char *, size_t, loff_t *); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); ..
可以看到結構體中有一個 poll 函數,這部分由驅動實現,其核心函數是:
static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p) { if (p && wait_address) p->qproc(filp, wait_address, p); }
polltable 其實不是表,是一個結構體:
typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *); typedef struct poll_table_struct { poll_queue_proc qproc; } poll_table;
包含一個回調函數,當驅動有事件的時候,會調用這個回調。由此可以將epoll 內核實現分成兩層:業務層和驅動層。業務層實現epoll的各個函數,並將新增文件加到驅動層中,驅動層實現有事件時通知業務層。業務層有兩個函數與驅動層交互:設置隊列函數A,異步回調函數B
poll_table pt; pt.qproc = A;
當新增一個文件 sockt1 時,會調用
tfile->f_op->poll(socket1, &pt );
A立即調用,A要負責創建一個隊列元素,該隊列元素包含異步回調函數B,並將該元素插入到驅動層隊列中。當驅動層有事件時,會回調B,B再將對應socket1 插入到通知列表中,由epoll_wait 取走。這樣,epoll 的內核實現就清晰了,希望對大家有所幫助。