epoll源码剖析

epoll的实现主要依赖于一个文件系统eventpoll。
epoll使用中有三个重要的函数:epoll_create(), epoll_ctl(), epoll_wait
epoll有四个重要的数据结构:struct eventpoll, struct epitem,struct epoll_event, struct eppoll_entry

struct eventpoll
{
    rwlock_t lock;
    struct rw_semaphore sem;

    wait_queue_head_t wq;

    wait_queue_head_t poll_wait;

    //链接的是epitem->rdllink,该epitem对应的fd有事件就绪。
    struct list_head rdllist;

    //该连接监听的fd的epitem
    struct rb_root rbr;
};

struct epitem
{
    //红黑树的根节点,它的结点都为epitem变量。方便查找和删除
    struct rb_node rbn; 

    //链表中的每个结点即为epitem中的rdllink,当epitem所对应的fd存在已经就绪的I/O事件,ep_poll_callback回调函数会将该结点连接到eventpoll中的rdllist循环链表中去,这样就将就绪的epitem都串起来了。      
    struct list_head rdllink;

    //将fd和file绑定起来
    struct epoll_filefd ffd;

    int nwait;

    //指向包含此epitem的所有poll wait queue,insert时,pwqlist=eppoll_entry->llink;
    struct list_head pwqlist;

    //eventpoll的指针,每个epitem都有这样一个指针,它指向对应的eventpoll变量。只要拿到了epitem,就可以根据它找到eventpoll
    struct eventpoll *ep;

    //存放从用户空间拷贝的epoll_event
    struct epoll_event event;

    atomic_t usecnt;

    //通过这个节点,将epitem挂到file->f_op_links文件操作等待队列中
    struct list_head fllink;

    //通过这个节点,将epitem挂到transfer链表中
    struct list_head txlink;

    //判断是否要重新插入rdllist中  ET/LT
    unsigned int revents;
};

struct epoll_event 
{
    __u32 events;//关心的事件EPOLLIN/EPOLLOUT/EPOLLONESHOT
    __u64 data;//fd
};

//等待队列节点
struct eppoll_entry
{
    /* List header used to link this structure to the "struct epitem" */
    struct list_head llink;

    //指向其e对应的pitem
    void *base;

    //等待队列的项,insert时,将该等待队列挂在设备驱动的等待队列中,并设置wait->proc=ep_poll_callback。事件就绪时,设备状态发生改变,设备驱动调用回调函数,将epi->rdllink挂到ep->rdllist上
    wait_queue_t wait;

    /* The wait queue head that linked the "wait" wait queue item */
    wait_queue_head_t *whead;
};

1.epoll_create
该函数的作用是创建一系列数据结构并建立之间的连接关系,为epoll_ctl做准备。

创建fd、inode、file结构体,初始化后,建立file与inode的连接关系(file->dentry->inode)、task_struct与file的连接关系(files->fd[fd]=file)

生成eventpoll对象,初始化wq、poll_wait、rdllist三个等待队列和红黑树根节点rbroot,建立file与eventpoll的连接关系(file->private_data=ep)

在epoll_create中,size是只要>=0即可,size没有实际意义。

asmlinkage long sys_epoll_create(int size)
{
    int error, fd;
    struct inode *inode;
    struct file *file;


    if (size <= 0)
        goto eexit_1;

    //获取fd、inode、file结构体,初始化,建立连接关系。
    error= ep_getfd(&fd, &inode, &file);

    //获取eventpoll文件系统,初始化,file->private_data=ep;
    error= ep_file_init(file);

    return fd;
}
下面是ep_getfd()函数的实现过程,重要的代码我都做了注释,有兴趣的同学自行阅读。

该函数创建fd、inode、file结构体,初始化后,建立file与inode的连接关系、task_struct与file的连接关系

static int ep_getfd(int *efd, struct inode **einode, struct file **efile)
{
    struct qstr this;
    char name[32];
    struct dentry *dentry;
    struct inode *inode;
    struct file *file;
    int error, fd;

    /* Get an ready to use file */
    error = -ENFILE;
    //申请file数据结构
    file = get_empty_filp();
    if (!file)
        goto eexit_1;

    /* Allocates an inode from the eventpoll file system */
    //申请inode数据结构
    inode = ep_eventpoll_inode();
    error = PTR_ERR(inode);
    if (IS_ERR(inode))
        goto eexit_2;

    /* Allocates a free descriptor to plug the file onto */
    //获得文件描述符
    error = get_unused_fd();
    if (error < 0)
        goto eexit_3;
    fd = error;

    /*
     * Link the inode to a directory entry by creating a unique name
     * using the inode number.
     */
    error = -ENOMEM;
    sprintf(name, "[%lu]", inode->i_ino);
    this.name = name;
    this.len = strlen(name);
    this.hash = inode->i_ino;

    //file和inode必须通过dentry结构才能连接起来,除了做file和inode的桥梁,这个数据结构本身没有什么大的作用
    dentry = d_alloc(eventpoll_mnt->mnt_sb->s_root, &this);
    if (!dentry)
        goto eexit_4;
    dentry->d_op = &eventpollfs_dentry_operations;

    //dentry->d_inode=inode 
    d_add(dentry, inode);

    //初始化file结构体
    file->f_vfsmnt = mntget(eventpoll_mnt);
    file->f_dentry = dentry;
    file->f_mapping = inode->i_mapping;

    file->f_pos &#
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值