《2022年linux..poll系统调用源码分析 .pdf》由会员分享,可在线阅读,更多相关《2022年linux..poll系统调用源码分析 .pdf(8页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、linux2.6.9 poll 系统调用源码分析(2011-03-27 21:27:22)转载标签:杂谈分类:EPOLL 1、涉及到的数据结构typedef void(*poll_queue_proc)(struct file*,wait_queue_head_t*,struct poll_table_struct*);/声明回调函数类型,内核中不同的模块实现不同的回调函数(但接口统一),以提供给驱动程序调用。typedef struct poll_table_struct poll_queue_proc qproc;poll_table;/对上述类型的封装,便于以后的扩充struct pol
2、l_table_page struct poll_table_page*next;/指向下一个元素struct poll_table_entry*entry;/保存 entries 中的当前元素struct poll_table_entry entries0;/保存 poll_table_entry 元素;/内核中用来保存套接字的数据结构struct poll_table_entry struct file*filp;wait_queue_t wait;wait_queue_head_t*wait_address;/一个句柄对应的内核结构struct poll_wqueues poll_tab
3、le pt;struct poll_table_page*table;int error;/用于等待函数的结构struct pollfd int fd;short events;short revents;/用户态的句柄struct poll_list 名师资料总结-精品资料欢迎下载-名师精心整理-第 1 页,共 8 页 -struct poll_list*next;int len;struct pollfd entries0;/将用户态的句柄拷贝到内核时使用的结构2、对应函数源码分析1、sys_poll asmlinkage long sys_poll(struct pollfd _user
4、*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-EINV AL;if(timeout)if(unsigned long)timeout POLLFD_PER_PAGE?POLLFD_PER_PAGE:i)
5、,GFP_KERNEL);if(pp=NULL)goto out_fds;pp-next=NULL;pp-len=(iPOLLFD_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;调用 do_poll,检测是否有句柄就绪fdcount=do_poll(nfds,head,&table,t
6、imeout);将 revents 字段拷贝到用户空间下面的 while 循环就是将do_poll 之后的 revents 字段拷贝到用户空间,当句柄数很多时,这也是 poll 系统调用的性能瓶颈。walk=head;err=-EFAULT;while(walk!=NULL)struct pollfd*fds=walk-entries;int j;for(j=0;j len;j+,ufds+)if(_put_user(fdsj.revents,&ufds-revents)goto out_fds;walk=walk-next;名师资料总结-精品资料欢迎下载-名师精心整理-第 3 页,共 8 页
7、 -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;2、poll_initwait 设定 poll_table 对应回调函数很明显,poll_initwait的主要动作就是把table 变量的成员poll_table 对应的回调函数置为_pollwait。这个 _pollwait不
8、仅是poll 系统调用需要,select 系统调用也一样是用这个_pollwait,说白了,这(_pollwait)是个操作系统的异步操作的“御用”回调函数。void poll_initwait(struct poll_wqueues*pwq)init_poll_funcptr(&pwq-pt,_pollwait);/&(pwq-pt)-qproc=_pollwait;pwq-error=0;pwq-table=NULL;3、do_poll static int do_poll(unsigned int nfds,struct poll_list*list,struct poll_wqueue
9、s*wait,long timeout)int count=0;poll_table*pt=&wait-pt;if(!timeout)pt=NULL;for(;)struct poll_list*walk;set_current_state(TASK_INTERRUPTIBLE);walk=list;while(walk!=NULL)DO_POLLFD 调用名师资料总结-精品资料欢迎下载-名师精心整理-第 4 页,共 8 页 -对用户传入的每一个句柄,调用 do_pollfd 设定在本句柄上等待的事件发生时需要执行的回调函数,同时检测本句柄上等待的事件是否就绪,如果就绪则递增count。当用户
10、传入的fd 很多时(比如1000 个),对 do_pollfd 就会调用很多次,poll 效率瓶颈的另一原因就在这里do_pollfd(walk-len,walk-entries,&pt,&count);walk=walk-next;pt=NULL;返回当有等待的时间就绪,设定的时间超时或者本进程收到信号则跳出循环,返回。if(count|!timeout|signal_pending(current)break;count=wait-error;if(count)break;休眠让 current 挂起,别的进程跑,timeout 到了以后再回来运行current,或者是在current 等
11、待的事件发生时返回,然后回到for 循环的其实处,重新扫描所有句柄。timeout=schedule_timeout(timeout);_set_current_state(TASK_RUNNING);return count;4、do_pollfd static void do_pollfd(unsigned int num,struct pollfd*fdpage,poll_table*pwait,int*count)int i;for(i=0;i fd;if(fd=0)struct file*file=fget(fd);mask=POLLNV AL;if(file!=NULL)mask=
12、DEFAULT_POLLMASK;if(file-f_op&file-f_op-poll)名师资料总结-精品资料欢迎下载-名师精心整理-第 5 页,共 8 页 -调用设备驱动程序的poll 函数如果 fd 对应的是某个socket,do_pollfd 调用的就是网络设备驱动实现的poll;如果 fd 对应的是某个ext3 文件系统上的一个打开文件,那 do_pollfd 调用的就是ext3 文件系统驱动实现的 poll。一句话,这个file-f_op-poll是设备驱动程序实现的,那设备驱动程序的poll 实现通常又是什么样子呢?其实,设备驱动程序的标准实现是:调用 poll_wait,即以设
13、备自己的等待队列为参数(通常设备都有自己的等待队列,不然一个不支持异步操作的设备会让人很郁闷)调用struct poll_table 的回调函数。mask=file-f_op-poll(file,*pwait);mask&=fdp-events|POLLERR|POLLHUP;fput(file);if(mask)*pwait=NULL;(*count)+;fdp-revents=mask;5、tcp_poll unsigned int tcp_poll(struct file*file,struct socket*sock,poll_table*wait)unsigned int mask;
14、struct sock*sk=sock-sk;struct tcp_opt*tp=tcp_sk(sk);调用 _poll_wait,设定回调函数tcp_poll 的核心实现就是poll_wait,而 poll_wait 就是调用struct poll_table 对应的回调函数,那 poll 系统调用对应的回调函数就是_poll_wait,所以这里几乎就可以把tcp_poll 理解为一个语句:_poll_wait(file,sk-sk_sleep,wait);由此也可以看出,每个socket 自己都带有一个等待队列sk_sleep,所以上面我们所说的“设备的等待队列”其实不止一个。poll_w
15、ait(file,sk-sk_sleep,wait);/_poll_wait(file,sk-sk_sleep,wait);if(sk-sk_state=TCP_LISTEN)return tcp_listen_poll(sk,wait);测试套接字的状态,返回给调用者mask=0;if(sk-sk_err)mask=POLLERR;名师资料总结-精品资料欢迎下载-名师精心整理-第 6 页,共 8 页 -if(sk-sk_shutdown=SHUTDOWN_MASK|sk-sk_state=TCP_CLOSE)mask|=POLLHUP;if(sk-sk_shutdown&RCV_SHUTDO
16、WN)mask|=POLLIN|POLLRDNORM;if(1 sk_state)&(TCPF_SYN_SENT|TCPF_SYN_RECV)if(tp-rcv_nxt!=tp-copied_seq)&(tp-urg_seq!=tp-copied_seq|tp-rcv_nxt!=tp-copied_seq+1|sock_flag(sk,SOCK_URGINLINE)|!tp-urg_data)mask|=POLLIN|POLLRDNORM;if(!(sk-sk_shutdown&SEND_SHUTDOWN)if(sk_stream_wspace(sk)=sk_stream_min_wspace
17、(sk)mask|=POLLOUT|POLLWRNORM;else set_bit(SOCK_ASYNC_NOSPACE,&sk-sk_socket-flags);set_bit(SOCK_NOSPACE,&sk-sk_socket-flags);if(sk_stream_wspace(sk)=sk_stream_min_wspace(sk)mask|=POLLOUT|POLLWRNORM;if(tp-urg_data&TCP_URG_V ALID)mask|=POLLPRI;return mask;6、_pollwait void _pollwait(struct file*filp,wai
18、t_queue_head_t*wait_address,poll_table*_p)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;名师资料总结-精品资料欢迎下载-名师精心整理-第 7 页,共 8 页 -new_table=(struct poll_table_page*)_get_free_page
19、(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=
20、wait_address;init_waitqueue_entry(&entry-wait,current);add_wait_queue(wait_address,&entry-wait);_poll_wait 的作用就是创建了下图所示的数据结构(一次_poll_wait 即一次设备poll 调用只创建一个poll_table_entry),并通过struct poll_table_entry 的 wait 成员,把current 挂在了设备的等待队列上,此处的等待队列是wait_address,对应 tcp_poll 里的 sk-sk_sleep。名师资料总结-精品资料欢迎下载-名师精心整理-第 8 页,共 8 页 -