当系统启动时,epoll进行初始化:
- static int __init eventpoll_init(void)
- {
- mutex_init(&pmutex);
- ep_poll_safewake_init(&psw);
- epi_cache = kmem_cache_create(“eventpoll_epi”,sizeof(struct epitem),
- 0,SLAB_HWCACHE_ALIGN|EPI_SLAB_DEBUG|
- SLAB_PANIC,NULL);
- pwq_cache = kmem_cache_create(“eventpoll_pwq”,sizeof(struct
- eppoll_entry),0,EPI_SLAB_DEBUG|SLAB_PANIC,NULL);
-
- return 0;
- }
上面的代码实现一些数据结构的初始化,通过fs/eventpoll.c中的注释可以看出,有三种类型的锁机制使用场景:
1.epmutex(mutex):用户关闭文件描述符,但是没有调用EPOLL_CTL_DEL
2.ep->mtx(mutex):用户态与内核态的转换可能会睡眠
3.ep->lock(spinlock):内核态与具体设备中断过程中的转换,poll回调
接下来就是使用slab分配器动态分配内存,第一个结构为当系统中添加一个fd时,就创建一epitem结构体,内核管理的基本数据结构:
- struct epitem
- {
- struct rb_node rbn;//用于主结构管理的红黑树
- struct list_head rdllink;//事件就绪队列
- struct epitem *next;//用于主结构体中的链表
- struct epoll_filefd ffd;//每个fd生成的一个结构
- int nwait;//
- struct list_head pwqlist;//poll等待队列
- struct eventpoll *ep;//该项属于哪个主结构体
- struct list_head fllink;//链接fd对应的file链表
- struct epoll_event event;//注册的感兴趣的事件,也就是用户空间的epoll_event
- }
而每个epoll fd对应的主要数据结构为:
- struct eventpoll {
- spin_lock_t lock;//对本数据结构的访问
- struct mutex mtx;//防止使用时被删除
- wait_queue_head_t wq;//sys_epoll_wait() 使用的等待队列
- wait_queue_head_t poll_wait;//file->poll()使用的等待队列
- struct list_head rdllist;//事件满足条件的链表
- struct rb_root rbr;//用于管理所有fd的红黑树
- struct epitem *ovflist;//将事件到达的fd进行链接起来发送至用户空间
- }
该结构主要在epoll_create时进行创建:
- //原来使用的是hash表,所以有size,现在改为红黑树,故不使用.
- long sys_epoll_create(int size)
- {
- int error,fd = -1;
- struct eventpoll *ep;
- struct inode *inode;
- struct file *file;
-
- ….
- error = -EINVAL;
- //分配空间
- if(size <= 0 || (error = ep_alloc(&ep)!=0))
- goto errror_return;
- //创建一个struct file结构,由于没有任何文件系统,为匿名文件,
- 并将主结构体放入file->private项中进行保存
- error = anon_inode_getfd(&fd,&inode,&file,”[eventpoll]”,
- &eventpoll_fops,ep);
- if(error)
- goto error_free;
- return fd;
- ...
- }
上面注册的操作eventpoll_fops定义如下:
- static const struct file_operations eventpoll_fops = {
- .release = ep_eventpoll_release;
- .poll = ep_eventpoll_poll,
- }
这样说来,内核中维护了一棵红黑树,大致的结构如下: