Select函数实现
int select(int nfds,fd_set *restrict readfds,fd_set *restrict writefds,fd_set *restrict errorfds,struct timeval *restrict timeout);
SYSCALL_DEFINE5(select, int, n,fd_set __user *, inp,fd_set __user *, outp,fd_set __user *, exp,struct timeval __user *, tvp){ret = core_sys_select(n, inp, outp, exp, to);ret = poll_select_copy_remaining(&end_time, tvp, 1, ret);return ret;}
core_sys_select 主要工作:
- 初始化读写还有异常的bitmap
- 调用
do_select实现核心的轮询工作。 - 把结果拷贝会用户空间
int core_sys_select(int n,fd_set __user *inp,fd_set __user *outp,fd_set __user *exp,struct timespec *end_time){fd_set_bits fds;// …if ((ret = get_fd_set(n, inp, fds.in)) ||(ret = get_fd_set(n, outp, fds.out)) ||(ret = get_fd_set(n, exp, fds.ex))) //*get_fd_set仅仅调用copy_from_user从用户空间拷贝了fd_set*/goto out;zero_fd_set(n, fds.res_in);zero_fd_set(n, fds.res_out);zero_fd_set(n, fds.res_ex);//发现do_select函数ret = do_select(n, &fds, end_time);/*把结果集,拷贝回用户空间*/- if (set_fd_set(n, inp, fds.res_in) ||
set_fd_set(n, outp, fds.res_out) ||set_fd_set(n, exp, fds.res_ex))ret = -EFAULT;}
int do_select(int n, fd_set_bits *fds, struct timespec *end_time){struct poll_wqueues table;poll_table *wait;poll_initwait(&table);//这个函数实现很关键,其内部的init_poll_funcptr初始化回调函数为__pollwait, 后面轮询会回调这个函数,然后通过这个函数把进程添加到对应的监听文件等待队列,当有事件到来时,就会唤醒这个进程。for (;;) {//一次大循环for (i = 0; i < n; ++rinp, ++routp, ++rexp) {// …struct fd f;f = fdget(i);if (f.file) {const struct file_operations *f_op; //每个设备拥有一个struct file_operations结构体f_op = f.file->f_op;mask = DEFAULT_POLLMASK;if (f_op->poll) { //轮询函数不为空,每当设备模块加载就自动会加载设备轮询函数,等于将轮回函数统一付给poll这个指针,以便调用wait_key_set(wait, in, out,bit, busy_flag);//检查集合// 对每个fd进行I/O事件检测 (*f_op->poll)返回当前设备fd的状态(可读可写)mask = (*f_op->poll)(f.file, wait);//将会调用poll_wait函数,检测文件设备的状态,并且将当前进程加入到设备等待队列中。并且返回掩码}fdput(f);}}// 退出循环体if (retval || timed_out || signal_pending(current))break;// 轮询一遍没有发现就绪。那就休眠if (!poll_schedule_timeout(&table, TASK_INTERRUPTIBLE,to, slack))timed_out = 1;}}
这个函数实现很关键,这里init_poll_funcptr初始化回调函数为__pollwait, 后面轮询会回调这个函数,然后通过这个函数把进程添加到对应的监听文件等待队列,当有事件到来时,就会唤醒这个进程。poll_initwait(&table);void poll_initwait(struct poll_wqueues *pwq){//这里p->_qproc实际就是__pollwait函数,因为p->qproc在init_poll_funcptr中被赋值为__pollwait函数指针init_poll_funcptr(&pwq->pt, __pollwait); //初始化函数指针,设置为__pollwaitpwq->error = 0;pwq->table = NULL;pwq->inline_index = 0;}static inline voidinit_poll_funcptr(poll_table *pt, poll_queue_proc qproc){pt->qproc = qproc;}
struct file {struct path f_path;//路径struct inode *f_inode; //inodeconst struct file_operations *f_op; //包含各种用于操作设备的函数指针- } __attribute__((aligned(4))); /* lest something weird decides that 2
struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);// select()轮询设备fd的操作函数,对应一个file 跟poll_table_struct *unsigned int (*poll) (struct file *, struct poll_table_struct *); //驱动加载。一般就挂到这个地方轮询函数};
struct scull_pipe {wait_queue_head_t inq, outq; //可读可写队列};
static unsigned int scull_p_poll(struct file *filp, poll_table *wait){struct scull_pipe *dev = filp->private_data;unsigned int mask = 0;mutex_lock(&dev->mutex);poll_wait(filp, &dev->inq, wait);//pollwait函数包含了__pollwait.这函数就是把当前进程添加到设备队列中poll_wait(filp, &dev->outq, wait);//等待if (dev->rp != dev->wp)mask |= POLLIN | POLLRDNORM; //可读if (spacefree(dev))mask |= POLLOUT | POLLWRNORM; //可写mutex_unlock(&dev->mutex);return mask;//返回该设备的掩码,是否就绪可读可写}
static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p){if (p && p->_qproc && wait_address)p->_qproc(filp, wait_address, p);//这里p->_qproc实际就是__pollwait函数,因为p->qproc在do_select中被赋值为__pollwait函数指针}
typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);
poll_table里的函数指针,是在do_select()初始化的。
int do_select(int n, fd_set_bits *fds, struct timespec *end_time){struct poll_wqueues table;poll_table *wait;poll_initwait(&table);//初始化}void poll_initwait(struct poll_wqueues *pwq){// 初始化poll_table里的函数指针init_poll_funcptr(&pwq->pt, __pollwait);}EXPORT_SYMBOL(poll_initwait);static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc){pt->_qproc = qproc;//将poll_table的函数指针设置为__pollwait完成初始化工作pt->_key = ~0UL; /* all events enabled */}
static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,poll_table *p){// 把当前进程装到设备的等待队列add_wait_queue(wait_address, &entry->wait);}
static ssize_t scull_p_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos){wake_up_interruptible(&dev->inq); //唤醒当前进程}
- select慢的原因
- 从上面看,在第一次所有监听都没有事件时,调用 select 都需要把进程挂到所有监听的文件描述符一次。
- 有事件到来时,不知道是哪些文件描述符有数据可以读写,需要把所有的文件描述符都轮询一遍才能知道。
- 通知事件到来给用户进程,需要把整个 bitmap 拷到用户空间,让用户空间去查询。
- select返回时,会将该进程从全部监听的fd的等待队列里移除掉,这样就需要select每次都要重新传入全部监听的fd,然后重现将本进程挂载到全部的监测fd的等待队列
Select函数实现的更多相关文章
- (十二)select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET
select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型,原型:int select(int maxfd,fd_set *rdset ...
- select 函数1
Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect.accept.recv或recvfrom这样的阻塞程序( ...
- select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET
http://hi.baidu.com/%B1%D5%C4%BF%B3%C9%B7%F0/blog/item/e7284ef16bcec3c70a46e05e.html select函数用于在非阻塞中 ...
- PHP Socket实现websocket(四)Select函数
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout); /* ...
- IO复用与select函数
socket select函数的详细讲解 select函数详细用法解析 http://blog.chinaunix.net/uid-21411227-id-1826874.html linu ...
- I/O多路复用——select函数与poll函数
1 区别 同:(1)机制类似,本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理.(2)包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就 ...
- select函数
select函数: http://baike.baidu.com/view/3421856.htm select函数 目录 概况 操作程序 宏解释 socket读写 概况 select()的机制中 ...
- select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET(转)
select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型, 原型: int select(int maxfd,fd_set *rds ...
- 阻塞、非阻塞的概念和select函数的阻塞功能
其它文档: http://www.cnitblog.com/zouzheng/archive/2010/11/25/71711.html (1)阻塞block 所谓阻塞方式block,顾名思义 ...
- select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET (转)
select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型,原型: #include <sys/time.h> ...
随机推荐
- 对象、句柄、ID之间的区别
对象是C++的概念,C++的类对象 句柄是Windows SDK的概念,指向某种资源的一种“指针”(有时候底层不一定是指针) 资源ID在MFC里仅仅是一个宏,也就是个整数. 其实,句柄是控件在数据结构 ...
- 【思维题 线段树】cf446C. DZY Loves Fibonacci Numbers
我这种maintain写法好zz.考试时获得了40pts的RE好成绩 In mathematical terms, the sequence Fn of Fibonacci numbers is de ...
- redis学习笔记(2)
redis学习笔记第二部分 --配置文件介绍 二,解析redis的配置文件redis.conf常见配置参数说明redis.conf 配置项说明如下:1. Redis默认不是以守护进程的方式运行,可以通 ...
- Job for docker.service failed because the control process exited with error code. See "systemctl status docker.service" and "journalctl -xe" for details.
文档:Docker 启动错误.note链接:http://note.youdao.com/noteshare?id=065111d506e1b132dc930dbe88f5d7b0&sub=A ...
- LOJ 101 最大流(ISAP 模板)
开long long的最大流 #include<bits/stdc++.h> using namespace std; ;//点数的最大值 ;//边数的最大值 ; struct Edge ...
- 2018 Multi-University Training Contest 1 Balanced Sequence(贪心)
题意: t组测试数据,每组数据有 n 个只由 '(' 和 ')' 构成的括号串. 要求把这 n 个串排序然后组成一个大的括号串,使得能够匹配的括号数最多. 如()()答案能够匹配的括号数是 4,(() ...
- redis--py操作redis【转】
Python操作redis 请给作者点赞--> 原文链接 python连接方式:点击 下面介绍详细使用 1.String 操作 redis中的String在在内存中按照一个name对应一个val ...
- Page-Object思想
为什么要使用page-object 集中管理元素对象 集中管理一个page内的公共方法 后期维护方便 集中管理元素对象 实现方法: 调用方法: WebElement element = dri ...
- #ifndef、#def、#endif说明
你所遇到的这几个宏是为了进行条件编译.一般情况下,源程序中所有的行都参加编译.但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一 部分内容指定编译的条件,这就是“条件编译”.有时,希望 ...
- SPOJ 375 树链剖分 QTREE - Query on a tree
人生第一道树链剖分的题目,其实树链剖分并不是特别难. 思想就是把树剖成一些轻链和重链,轻链比较少可以直接修改,重链比较长,用线段树去维护. 貌似大家都是从这篇博客上学的. #include <c ...