IO复用——poll机制内核源代码剖析
来源:互联网 发布:淘宝如何设置限购 编辑:程序博客网 时间:2024/06/01 09:52
*select内核源代码已经剖析了,但是有个问题还没有解决。。。面对每一种文件描述符如何进行查询?这就和poll机制有关了。。。这篇就来看看poll机制内核源代码。。。→_→*
了解select底层实现请戳传送门——IO复用——select内核源代码剖析
每一个进程都会有一个与之对应的files_struct结构,files_struct结构中存储着该进程打开的文件的集合
struct files_struct { atomic_t count; rwlock_t file_lock; int max_fds; int max_fdset; int next_fd; struct file ** fd; /* current fd array */ //fd指向fd_array fd_set *close_on_exec; fd_set *open_fds; fd_set close_on_exec_init; fd_set open_fds_init; struct file * fd_array[NR_OPEN_DEFAULT]; //记录该进程打开文件的数组集合};
每一个文件都有与之对应的file_operations类型的文件操作方法
//linux-2.4.0\include\linux\Fs.hstruct file { struct list_head f_list; struct dentry *f_dentry; //dentry结构中的d_inode结构中记录着本文件的等待队列,即监听本文件的进程对应的wait_queue_t结构 struct vfsmount *f_vfsmnt; struct file_operations *f_op; //这是很关键的部分,它决定了poll机制是否可用 atomic_t f_count; unsigned int f_flags; mode_t f_mode; loff_t f_pos; unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin; struct fown_struct f_owner; unsigned int f_uid, f_gid; int f_error; unsigned long f_version; /* needed for tty driver, and maybe others */ void *private_data;};
对文件的操作方法有很多种,而每一种实现在机制上都使用回调函数的方法,其中就包括一种poll操作的回调函数
//linux-2.4.0\include\linux\Fs.hstruct 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 *); //poll操作所对应的回调函数,函数的具体实现和文件的类型有关,如果文件不支持poll操作也就无法在select中使用 int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);};
在每一种文件的poll操作回调函数中都会调用有poll_wait函数,目的是将监听本文件的进程的对应wait_queue_t结构,添加进本文件的等待队列中
//linux-2.4.0\arch\i386\math-emu\Poly.h//wait_address记录着本文件的等待队列队头的地址//p指向该进程的poll_table结构extern inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p){ if (p && wait_address) __pollwait(filp, wait_address, p);}
//linux-2.4.0\arch\i386\math-emu\Poly.hvoid __pollwait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p){ //table记录当前进程由poll_table_page结构组成的单链的第一个可用结点 struct poll_table_page *table = p->table; //如果单链不存在或所有poll_table_page结构的页面都被poll_table_entry结构使用,即没有空闲空间时 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; } //设置新poll_table_page结构页面的第一个可用poll_table_entry结构为poll_table_entry结构数组的第一个元素 new_table->entry = new_table->entries; //poll_table_page结构页面的单链表进行更新 new_table->next = table; //当前进程的poll_table结构成员进行更新 p->table = new_table; table = new_table; } /* Add a new entry */ { //获取当前进程的第一个空闲poll_table_entry结构 struct poll_table_entry * entry = table->entry; //更新第一个空闲poll_table_entry结构 table->entry = entry+1; //对该文件的引用计数加1 get_file(filp); //将此poll_table_entry结构的filp成员设置为该文件 entry->filp = filp; //将此poll_table_entry结构的wait_address成员,即等待队列的队头设置为该文件的等待队列的队头 entry->wait_address = wait_address; //将此poll_table_entry结构的wait成员,即每个进程对应的wait_queue_t结构,将其中的task_struck结构设置为当前进程的task_struck //init_waitqueue_entry定义在下面 init_waitqueue_entry(&entry->wait, current); //将该进程对应的wait_queue_t结构链入该文件的等待队列中 //add_wait_queue定义在下面 add_wait_queue(wait_address,&entry->wait); }}
//linux-2.4.0\include\linux\Wait.h//在init_waitqueue_entry中,将wait_queue_t结构中p设置为指向当前进程task_struck结构,所以当驱动设备唤醒该文件的等待队列中每一个wait_queue_t结构对应的进程时,就可以从wait_queue_t结构中的p成员找到进程的task_struck结构static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p){#if WAITQUEUE_DEBUG if (!q || !p) WQ_BUG();#endif q->flags = 0; //wait_queue_t结构的flags置为0 q->task = p;//wait_queue_t结构的p设置为指向当前进程的task_struck结构#if WAITQUEUE_DEBUG q->__magic = (long)&q->__magic;#endif}
//linux-2.4.0\kernel\Fork.cvoid add_wait_queue(wait_queue_head_t *q, wait_queue_t * wait){ unsigned long flags; wq_write_lock_irqsave(&q->lock, flags); wait->flags = 0; //wait_queue_t结构的flags置为0 __add_wait_queue(q, wait);//将该进程对应的wait_queue_t结构链入该文件的等待队列中 wq_write_unlock_irqrestore(&q->lock, flags);}
通过对Linux内核源代码的剖析,我们对poll机制已经有了深入地了解,现在我们再回到select中,解决最后的问题
//linux-2.4.0\fs\Select.c(部分截取) ...... for (;;) { set_current_state(TASK_INTERRUPTIBLE); for (i = 0 ; i < n; i++) { unsigned long bit = BIT(i); unsigned long mask; struct file *file; //记录文件结构体 off = i / __NFDBITS; if (!(bit & BITS(fds, off))) continue; file = fget(i); //利用文件描述符从该进程打开的文件描述符数组中,获取对应的文件结构体 mask = POLLNVAL; if (file) { mask = DEFAULT_POLLMASK; //此时需要判断该文件是否支持操作和poll操作 if (file->f_op && file->f_op->poll) //如果支持,就调用该类型文件的poll操作所对应的回调函数,并传入该文件结构体和该进程所对应的wait_queue_t结构,mask记录返回值 //这一操作就是在剖析select时提到的查询,所以真正的查询由poll回调函数完成 mask = file->f_op->poll(file, wait); fput(file); } if ((mask & POLLIN_SET) && ISSET(bit, __IN(fds,off))) { SET(bit, __RES_IN(fds,off)); retval++; wait = NULL; } if ((mask & POLLOUT_SET) && ISSET(bit, __OUT(fds,off))) { SET(bit, __RES_OUT(fds,off)); retval++; wait = NULL; } if ((mask & POLLEX_SET) && ISSET(bit, __EX(fds,off))) { SET(bit, __RES_EX(fds,off)); retval++; wait = NULL; } } wait = NULL; if (retval || !__timeout || signal_pending(current)) break; if(table.error) { retval = table.error; break; } __timeout = schedule_timeout(__timeout); } ......
*下一篇就来总结epoll喽。。。睡觉。。。→_→*
阅读全文
0 0
- IO复用——poll机制内核源代码剖析
- poll内核源代码剖析
- IO复用——select内核源代码剖析
- IO复用——epoll内核源代码剖析
- linux下poll和epoll内核源代码剖析
- select,poll,epoll实现分析—结合内核源代码
- select,poll,epoll实现分析—结合内核源代码
- select,poll,epoll实现分析—结合内核源代码
- Linux内核:poll机制
- Linux内核:poll机制
- epoll内核源代码剖析
- 内核驱动之poll机制
- Linux内核:Poll机制理解
- (五十四)高并发服务器——多路IO转接机制poll模型
- linux 内核poll/select/epoll实现剖析
- linux 内核poll/select/epoll实现剖析
- linux 内核poll/select/epoll实现剖析
- IO复用,linux poll
- display:inline、block、inline-block的区别
- 八大排序算法(Python实现)
- 开始写博客之旅
- Oracle入门基础
- HDU-1166-敌兵布阵
- IO复用——poll机制内核源代码剖析
- SQL 单表查询的一些常用语句和函数及单表的插入修改删除操作
- 关于字符串的运用(去除空格,反转,统计次数,最长公共子串)
- 1.1.5字典
- [leetcode]552. Student Attendance Record II
- Xlistview(异步、Imagerloader、POST)
- Java成员变量初始化顺序
- CodeForces
- jsp web上传文件