博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
NIO底层的实现
阅读量:5329 次
发布时间:2019-06-14

本文共 4665 字,大约阅读时间需要 15 分钟。

poll函数的定义如下:

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

 其中的参数类型pollfd的定义如下:

struct pollfd {    int   fd;    short events;     short revents;};

poll系统调用对应的内核中的代码为sys_poll,整体的流程是先把用户想知道的事件从用户内存弄到内核空间中来。然后调用文件的poll函数来监听对应的事件。如果在指定的时间内发生了,那么就要通知用户态的进程了。为了更清楚地阅读代码,先来看涉及到的数据结构,poll_wqueues用来保存发生的事件:

poll_list用来保存从用户空间得到的用户感兴趣的事件:

asmlinkage long sys_poll(struct pollfd __user * ufds, unsigned int nfds, long timeout){    struct poll_wqueues table;    int fdcount, err;    unsigned int i;    struct poll_list *head;    struct poll_list *walk;    if (nfds > current->files->max_fdset && nfds > OPEN_MAX)        return -EINVAL;    if (timeout) {        if ((unsigned long) timeout < MAX_SCHEDULE_TIMEOUT / HZ)            timeout = (unsigned long)(timeout*HZ+999)/1000+1;        else            timeout = MAX_SCHEDULE_TIMEOUT;    }    // 初始化poll_wqueues,设置了回调函数为__pollwait    poll_initwait(&table);        head = NULL;    walk = NULL;    i = nfds;    err = -ENOMEM;    // 每次都分配一个页面来保存传入的pollfd数组,下面的循环用来分割    while(i!=0) {        struct poll_list *pp;        pp = kmalloc(sizeof(struct poll_list)+ sizeof(struct pollfd)*(i>POLLFD_PER_PAGE?POLLFD_PER_PAGE:i), GFP_KERNEL);        if(pp==NULL)            goto out_fds;        pp->next=NULL;        pp->len = (i>POLLFD_PER_PAGE?POLLFD_PER_PAGE:i);        if (head == NULL)            head = pp;        else            walk->next = pp;                    walk = pp;        if (copy_from_user(pp->entries, ufds + nfds-i, sizeof(struct pollfd)*pp->len)) {            err = -EFAULT;            goto out_fds;        }        i -= pp->len;    }    // 真正的执行    fdcount = do_poll(nfds, head, &table, timeout);    walk = head;    err = -EFAULT;    // 如果发现事件发生了,这些仍然在内核空间,需要通知用户进程    while(walk != NULL) {        struct pollfd *fds = walk->entries;        int j;        for (j=0; j < walk->len; j++, ufds++) {            if(__put_user(fds[j].revents, &ufds->revents))                goto out_fds;        }        walk = walk->next;    }    err = fdcount;    if (!fdcount && signal_pending(current))        err = -EINTR;out_fds:    // 在离开之前释放掉分配的内存    walk = head;    while(walk!=NULL) {        struct poll_list *pp = walk->next;        kfree(walk);        walk = pp;    }    poll_freewait(&table);    return err;}

在poll_wqueues中我们看到了一个回调函数poll_queue_proc,在初始化poll_wqueues的时候(也就是poll_initwait函数中)会被初始化为__pollwait。

void __pollwait(struct file *filp, wait_queue_head_t *wait_address, poll_table *_p){    // 根据_p的位置就能找到所在的poll_wqueues所在的位置    struct poll_wqueues *p = container_of(_p, struct poll_wqueues, pt);    struct poll_table_page *table = p->table;    // 如果为空或者没有足够的空间    if (!table || POLL_TABLE_FULL(table)) {        struct poll_table_page *new_table;        new_table = (struct poll_table_page *) __get_free_page(GFP_KERNEL);        if (!new_table) {            p->error = -ENOMEM;            __set_current_state(TASK_RUNNING);            return;        }        new_table->entry = new_table->entries;        new_table->next = table;        p->table = new_table;        table = new_table;    }    {        struct poll_table_entry * entry = table->entry;        table->entry = entry+1;        get_file(filp);        entry->filp = filp;        entry->wait_address = wait_address;        // 初始化等待队列项,挂载当前线程        init_waitqueue_entry(&entry->wait, current);        add_wait_queue(wait_address,&entry->wait);    }}

接下来会调动do_poll函数,在这个函数里并没有做什么实际性的工作,循环处理poll_list下面的每个页面(每个页面上有用户进程感兴趣的事件),然后再do_pollfd函数中循环处理一个页面上所有的pollfd。这个函数中实际上还是循环调用file_operation结构中的poll函数。在返回的结果中判断我们感兴趣的事件有没有发生。具体的代码如下:

static void do_pollfd(unsigned int num, struct pollfd * fdpage, poll_table ** pwait, int *count){    int i;    // 循环遍历一个页面上保存的所有的pollfd    for (i = 0; i < num; i++) {        int fd;        unsigned int mask;        struct pollfd *fdp;        mask = 0;        fdp = fdpage+i;        fd = fdp->fd;        if (fd >= 0) {            struct file * file = fget(fd);            mask = POLLNVAL;            if (file != NULL) {                mask = DEFAULT_POLLMASK;                if (file->f_op && file->f_op->poll)                    mask = file->f_op->poll(file, *pwait);                // 用来判断是不是自己感兴趣的事件发生了                mask &= fdp->events | POLLERR | POLLHUP;                fput(file);            }            // 如果发生了,就将pwait清空,后面就不用再把当前线程挂入等待队列            if (mask) {                *pwait = NULL;                (*count)++;            }        }        // 把发生的事件记录下来,用来返回        fdp->revents = mask;    }}

到此为止,如果发现我们感兴趣的事件发生了,那就把回调函数设置为null,这样就算以后再发现有事件发生也不会做任何实质性的操作,而是记录一下数目而已。相应的在do_poll函数中发现对应的事件发生了就会中断循环返回。再底层的比如file_operation中的poll是怎么实现的话以后有事件再看。

转载于:https://www.cnblogs.com/ggzwtj/archive/2012/06/11/2544487.html

你可能感兴趣的文章
HDU 2063 过山车
查看>>
高精度1--加法
查看>>
String比较
查看>>
Django之Models
查看>>
CSS 透明度级别 及 背景透明
查看>>
Linux 的 date 日期的使用
查看>>
PHP zip压缩文件及解压
查看>>
SOAP web service用AFNetWorking实现请求
查看>>
Java变量类型,实例变量 与局部变量 静态变量
查看>>
mysql操作命令梳理(4)-中文乱码问题
查看>>
Python环境搭建(安装、验证与卸载)
查看>>
一个.NET通用JSON解析/构建类的实现(c#)
查看>>
Windows Phone开发(5):室内装修 转:http://blog.csdn.net/tcjiaan/article/details/7269014
查看>>
详谈js面向对象 javascript oop,持续更新
查看>>
关于这次软件以及pda终端的培训
查看>>
jQuery上传插件Uploadify 3.2在.NET下的详细例子
查看>>
如何辨别一个程序员的水平高低?是靠发量吗?
查看>>
新手村之循环!循环!循环!
查看>>
正则表达式的用法
查看>>
线程安全问题
查看>>