epoll()是由epoll_create()、epoll_ctl()、epoll_wait()三个系统调用实现。
epoll_create()调用内核函数sys_epoll_create(),在参数检查后,sys_epoll_create()中,最为重要的两个内核函数为ep_getfd()、ep_file_init();
1.在 ep_getfd()中为epoll事件分配file、inode、以及文件描述符fd;
file = get_empty_filp();
//在该函数中初始化一系列文件结构以及参数检查
//f = kmem_cache_alloc(filp_cachep, GFP_KERNEL);//分配空间
//eventpoll_init_file(f);
inode = ep_eventpoll_inode();
//分配节点空间以及初始化
//struct inode *inode = new_inode(eventpoll_mnt->mnt_sb);
error = get_unused_fd();
//分配文件描述符fd
fd = error;
2.分配dentry结构体的空间并利用中间结构体qstr将其初始化,将file结构体初始化,并将当前进程与file、dentry、inode联系在一起;
sprintf(name, "[%lu]", inode->i_ino);
this.name = name;
this.len = strlen(name);
this.hash = inode->i_ino;
//通过中间结构体将inode与dentry联系在一起
dentry = d_alloc(eventpoll_mnt->mnt_sb->s_root, &this);
dentry->d_op = &eventpollfs_dentry_operations;
d_add(dentry, inode);
//初始化file结构体
file->f_vfsmnt = mntget(eventpoll_mnt);
file->f_dentry = dentry;
//file与dentry联系在一起
file->f_mapping = inode->i_mapping;
file->f_pos = 0;
file->f_flags = O_RDONLY;
file->f_op = &eventpoll_fops;
file->f_mode = FMODE_READ;
file->f_version = 0;
file->private_data = NULL;
fd_install(fd, file);
//files->fd[fd] = file;
//将file插入到当前进程PCB监测文件结构体中
3.在ep_file_init(),分配eventpoll结构体的空间并初始化,将file与该结构体联系起来。
if (!(ep = kmalloc(sizeof(struct eventpoll), GFP_KERNEL)))
return -ENOMEM;
memset(ep, 0, sizeof(*ep));
rwlock_init(&ep->lock);
init_rwsem(&ep->sem);
init_waitqueue_head(&ep->wq);
init_waitqueue_head(&ep->poll_wait);
INIT_LIST_HEAD(&ep->rdllist);
ep->rbr = RB_ROOT;
file->private_data = ep;
epoll_ctl()调用内核函数sys_epoll_ctl(),在参数检查后,通过file结构体找到eventpoll结构体,再从其中的红黑树根节点遍历寻找新的sockfd是否已存在,最后通过判断操作OP,确定是否添加、删除或者更改。
1.获取到eventpoll结构体,然后寻找sockfd是否已经存在;
ep = file->private_data;
epi = ep_find(ep, tfile, fd);
2.判断OP操作是什么并作出相应处理;
switch (op)
{
case EPOLL_CTL_ADD:
if (!epi)
{
epds.events |= POLLERR | POLLHUP;
error = ep_insert(ep, &epds, tfile, fd);
} else
error = -EEXIST;
break;
case EPOLL_CTL_DEL:
if (epi)
error = ep_remove(ep, epi);
else
error = -ENOENT;
break;
case EPOLL_CTL_MOD:
if (epi)
{
epds.events |= POLLERR | POLLHUP;
error = ep_modify(ep, epi, &epds);
} else
error = -ENOENT;
break;
}
3.在该函数中最为重要的是ep_insert()函数,在ep_insert()中在申请epitem节点并初始化后,为此监听的sockfd在设备驱动中注册了一个回调函数,并将此节点插至eventpoll的红黑树中以便查找监听。
EP_RB_INITNODE(&epi->rbn);
INIT_LIST_HEAD(&epi->rdllink);
INIT_LIST_HEAD(&epi->fllink);
INIT_LIST_HEAD(&epi->txlink);
INIT_LIST_HEAD(&epi->pwqlist);
epi->ep = ep;
EP_SET_FFD(&epi->ffd, tfile, fd);
epi->event = *event;
atomic_set(&epi->usecnt, 1);
epi->nwait = 0;
//将epitem初始化
epq.epi = epi;
init_poll_funcptr(&epq.pt, ep_ptable_queue_proc);
//ep_pqueue结构体中有一个指向epitem的指针,还有一个回调函数
//在此注册第一个回调函数
revents = tfile->f_op->poll(tfile, &epq.pt);
//调用第一个回调函数,并在其中注册第二个回调函数ep_poll_callback
//以下皆为将初始化好的epitem挂入监听的结构中
list_add_tail(&epi->fllink, &tfile->f_ep_links);
ep_rbtree_insert(ep, epi);
if ((revents & event->events) && !EP_IS_LINKED(&epi->rdllink))
{
list_add_tail(&epi->rdllink, &ep->rdllist);
if (waitqueue_active(&ep->wq))
wake_up(&ep->wq);
if (waitqueue_active(&ep->poll_wait))
pwake++;
}
retry:
if (list_empty(&ep->rdllist)
{
init_waitqueue_entry(&wait, current);
add_wait_queue(&ep->wq, &wait);
for (;;)
{
set_current_state(TASK_INTERRUPTIBLE);
//rdllist不空,break跳出循环,而不空的条件是有事件时设备驱动调用回调函数将事件加入rdllist
if (!list_empty(&ep->rdllist) || !jtimeout)
break;
if (signal_pending(current))
{
res = -EINTR;
break;
}
write_unlock_irqrestore(&ep->lock, flags);
jtimeout = schedule_timeout(jtimeout);
write_lock_irqsave(&ep->lock, flags);
}
remove_wait_queue(&ep->wq, &wait);
set_current_state(TASK_RUNNING);
}
//此中ep_events_transfer()实现向用户传送数据,将txlist作为中间链实现ET和LT
//如果ET,将rdllist断链并链入txlist中由txlist发送给用户
//如果LT,会将事件重复加入rdlliat中再次通知用户
if (!res && eavail &&!(res = ep_events_transfer(ep, events, maxevents)) && jtimeout)
goto retry;
epoll()实现至此,在剖析源码过程中,其实还有若干未解,如:
if (EP_OP_HASH_EVENT(op) && copy_from_user(&epds, event, sizeof(struct epoll_event)))
goto eexit_1;
该判断,前半句为判断OP事件是否为删除,如不是删除返回值为真,可执行从用户态拷贝数据到内核态,但是在if条件判断后,执行的操作为退出?
此篇博客还有诸多不完善之处,如有错误和完善意见,麻烦大家发送至[email protected],欢迎大家指正~