- 浏览: 261683 次
- 性别:
- 来自: 武汉
文章分类
最新评论
-
daknife:
谢谢你的这篇文章,让我大概了解了select的一部分底层原理。 ...
Linux-2.6.25 select系统调用源码分析 -
gjlzjb:
非常有用,谢谢哈。另外问下,您是否用过Pheonix Syst ...
Why Map/Reduce? -
zhangyafei_kimi:
canbo 写道请问,我怎么生成安装包,提供给其它用户安装呢? ...
下载最新的Google Chrome源码并编译 -
canbo:
请问,我怎么生成安装包,提供给其它用户安装呢?
下载最新的Google Chrome源码并编译
Linux 2.6.25中的select系统调用
主要有4个函数:
sys_select:处理时间参数,调用core_sys_select。
core_sys_select:处理三个fd_set参数,调用do_select。
do_select:做select/poll的工作。在合适的时机把自己挂起等待,调用sock_poll。
sock_poll:用函数指针分派到具体的协议层函数tcp_poll、udp_poll、datagram_poll。
层层分工明确,我也要多学习这种方式啊。
其他重要函数一览
static int max_select_fd(unsigned long n, fd_set_bits *fds)
返回在fd_set中已经打开的,并且小于用户指定最大值,的fd
static inline int get_fd_set(unsigned long nr, void __user *ufdset, unsigned long *fdset)
从用户空间拷贝fd_set到内核
static inline void zero_fd_set(unsigned long nr, unsigned long *fdset)
把fd_set清零
static inline unsigned long __must_check set_fd_set(unsigned long nr, void __user *ufdset, unsigned long *fdset)
把fd_set拷贝回用户空间
static inline int signal_pending(struct task_struct *p)
目前进程有信号需要处理
struct file *fget_light(unsigned int fd, int *fput_needed)
由fd得到其对应的file结构指针,并增加其引用计数
static inline void fput_light(struct file *file, int fput_needed)
释放由fget_light得到的file结构指针,减少其引用计数
set_current_state
设置当前进程的状态
static inline int cond_resched(void)
判断是否有进程需要抢占当前进程,如果是将立即发生调度。就是额外增加一个抢占点。
signed long __sched schedule_timeout(signed long timeout)
当前进程睡眠timeout个jiffies
rcu_read_lock
rcu_read_unlock
Linux 2.6新加入的rcu锁。读锁的加锁、解锁函数
参考http://www.ibm.com/developerworks/cn/linux/l-rcu
poll_freewait
poll_initwait
poll_wait
...
和文件IO,poll机制有关的几个函数,参考《Linux设备驱动(第三版)》6.3
tcp_poll
udp_poll
datagram_poll
协议层的poll函数
主要有4个函数:
sys_select:处理时间参数,调用core_sys_select。
core_sys_select:处理三个fd_set参数,调用do_select。
do_select:做select/poll的工作。在合适的时机把自己挂起等待,调用sock_poll。
sock_poll:用函数指针分派到具体的协议层函数tcp_poll、udp_poll、datagram_poll。
层层分工明确,我也要多学习这种方式啊。
/* sys_select(fs/select.c) 处理了超时值(如果有),将struct timeval转换成了时钟周期数,调用core_sys_select,然后检查剩余时间,处理时间 */ asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct timeval __user *tvp) { s64 timeout = -1; struct timeval tv; int ret; if (tvp) {/*如果有超时值*/ if (copy_from_user(&tv, tvp, sizeof(tv))) return -EFAULT; if (tv.tv_sec < 0 || tv.tv_usec < 0)/*时间无效*/ return -EINVAL; /* Cast to u64 to make GCC stop complaining */ if ((u64)tv.tv_sec >= (u64)MAX_INT64_SECONDS) timeout = -1; /* 无限等待*/ else { timeout = DIV_ROUND_UP(tv.tv_usec, USEC_PER_SEC/HZ); timeout += tv.tv_sec * HZ;/*计算出超时的相对时间,单位为时钟周期数*/ } } /*主要工作都在core_sys_select中做了*/ ret = core_sys_select(n, inp, outp, exp, &timeout); if (tvp) {/*如果有超时值*/ struct timeval rtv; if (current->personality & STICKY_TIMEOUTS)/*模拟bug的一个机制,不详细描述*/ goto sticky; /*rtv中是剩余的时间*/ rtv.tv_usec = jiffies_to_usecs(do_div((*(u64*)&timeout), HZ)); rtv.tv_sec = timeout; if (timeval_compare(&rtv, &tv) >= 0)/*如果core_sys_select超时返回,更新时间*/ rtv = tv; /*拷贝更新后的时间到用户空间*/ if (copy_to_user(tvp, &rtv, sizeof(rtv))) { sticky: /* * If an application puts its timeval in read-only * memory, we don't want the Linux-specific update to * the timeval to cause a fault after the select has * completed successfully. However, because we're not * updating the timeval, we can't restart the system * call. */ if (ret == -ERESTARTNOHAND)/*ERESTARTNOHAND表明,被中断的系统调用*/ ret = -EINTR; } } return ret; } /*core_sys_select 为do_select准备好了位图,然后调用do_select,将返回的结果集,返回到用户空间 */ static int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, s64 *timeout) { fd_set_bits fds; void *bits; int ret, max_fds; unsigned int size; struct fdtable *fdt; /* Allocate small arguments on the stack to save memory and be faster */ /*SELECT_STACK_ALLOC 定义为256*/ long stack_fds[SELECT_STACK_ALLOC/sizeof(long)]; ret = -EINVAL; if (n < 0) goto out_nofds; /* max_fds can increase, so grab it once to avoid race */ rcu_read_lock(); fdt = files_fdtable(current->files);/*获取当前进程的文件描述符表*/ max_fds = fdt->max_fds; rcu_read_unlock(); if (n > max_fds)/*修正用户传入的第一个参数:fd_set中文件描述符的最大值*/ n = max_fds; /* * We need 6 bitmaps (in/out/ex for both incoming and outgoing), * since we used fdset we need to allocate memory in units of * long-words. */ /* 如果stack_fds数组的大小不能容纳下所有的fd_set,就用kmalloc重新分配一个大数组。 然后将位图平均分成份,并初始化fds结构 */ size = FDS_BYTES(n); bits = stack_fds; if (size > sizeof(stack_fds) / 6) { /* Not enough space in on-stack array; must use kmalloc */ ret = -ENOMEM; bits = kmalloc(6 * size, GFP_KERNEL); if (!bits) goto out_nofds; } fds.in = bits; fds.out = bits + size; fds.ex = bits + 2*size; fds.res_in = bits + 3*size; fds.res_out = bits + 4*size; fds.res_ex = bits + 5*size; /*get_fd_set仅仅调用copy_from_user从用户空间拷贝了fd_set*/ 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))) 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, timeout); if (ret < 0) goto out; /*do_select返回,是一种异常状态*/ if (!ret) { /*记得上面的sys_select不?将ERESTARTNOHAND转换成了EINTR并返回。EINTR表明系统调用被中断*/ ret = -ERESTARTNOHAND; if (signal_pending(current))/*当当前进程有信号要处理时,signal_pending返回真,这符合了EINTR的语义*/ goto out; ret = 0; } /*把结果集,拷贝回用户空间*/ 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; out: if (bits != stack_fds) kfree(bits);/*对应上面的kmalloc*/ out_nofds: return ret; } /*do_select 真正的select在此,遍历了所有的fd,调用对应的xxx_poll函数 */ int do_select(int n, fd_set_bits *fds, s64 *timeout) { struct poll_wqueues table; poll_table *wait; int retval, i; rcu_read_lock(); /*根据已经打开fd的位图检查用户打开的fd, 要求对应fd必须打开, 并且返回最大的fd*/ retval = max_select_fd(n, fds); rcu_read_unlock(); if (retval < 0) return retval; n = retval; /*将当前进程放入自已的等待队列table, 并将该等待队列加入到该测试表wait*/ poll_initwait(&table); wait = &table.pt; if (!*timeout) wait = NULL; retval = 0; for (;;) {/*死循环*/ unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp; long __timeout; /*注意:可中断的睡眠状态*/ set_current_state(TASK_INTERRUPTIBLE); inp = fds->in; outp = fds->out; exp = fds->ex; rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex; for (i = 0; i < n; ++rinp, ++routp, ++rexp) {/*遍历所有fd*/ unsigned long in, out, ex, all_bits, bit = 1, mask, j; unsigned long res_in = 0, res_out = 0, res_ex = 0; const struct file_operations *f_op = NULL; struct file *file = NULL; in = *inp++; out = *outp++; ex = *exp++; all_bits = in | out | ex; if (all_bits == 0) { /* __NFDBITS定义为(8 * sizeof(unsigned long)),即long的位数。 因为一个long代表了__NFDBITS位,所以跳到下一个位图i要增加__NFDBITS */ i += __NFDBITS; continue; } for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) { int fput_needed; if (i >= n) break; /*测试每一位*/ if (!(bit & all_bits)) continue; /*得到file结构指针,并增加引用计数字段f_count*/ file = fget_light(i, &fput_needed); if (file) { f_op = file->f_op; mask = DEFAULT_POLLMASK; /*对于socket描述符,f_op->poll对应的函数是sock_poll 注意第三个参数是等待队列,在poll成功后会将本进程唤醒执行*/ if (f_op && f_op->poll) mask = (*f_op->poll)(file, retval ? NULL : wait); /*释放file结构指针,实际就是减小他的一个引用计数字段f_count*/ fput_light(file, fput_needed); /*根据poll的结果设置状态,要返回select出来的fd数目,所以retval++。 注意:retval是in out ex三个集合的总和*/ if ((mask & POLLIN_SET) && (in & bit)) { res_in |= bit; retval++; } if ((mask & POLLOUT_SET) && (out & bit)) { res_out |= bit; retval++; } if ((mask & POLLEX_SET) && (ex & bit)) { res_ex |= bit; retval++; } } /* 注意前面的set_current_state(TASK_INTERRUPTIBLE); 因为已经进入TASK_INTERRUPTIBLE状态,所以cond_resched回调度其他进程来运行, 这里的目的纯粹是为了增加一个抢占点。被抢占后,由等待队列机制唤醒。 在支持抢占式调度的内核中(定义了CONFIG_PREEMPT),cond_resched是空操作 */ cond_resched(); } /*根据poll的结果写回到输出位图里*/ if (res_in) *rinp = res_in; if (res_out) *routp = res_out; if (res_ex) *rexp = res_ex; } wait = NULL; if (retval || !*timeout || signal_pending(current))/*signal_pending前面说过了*/ break; if(table.error) { retval = table.error; break; } if (*timeout < 0) { /*无限等待*/ __timeout = MAX_SCHEDULE_TIMEOUT; } else if (unlikely(*timeout >= (s64)MAX_SCHEDULE_TIMEOUT - 1)) { /* 时间超过MAX_SCHEDULE_TIMEOUT,即schedule_timeout允许的最大值,用一个循环来不断减少超时值*/ __timeout = MAX_SCHEDULE_TIMEOUT - 1; *timeout -= __timeout; } else { /*等待一段时间*/ __timeout = *timeout; *timeout = 0; } /*TASK_INTERRUPTIBLE状态下,调用schedule_timeout的进程会在收到信号后重新得到调度的机会, 即schedule_timeout返回,并返回剩余的时钟周期数 */ __timeout = schedule_timeout(__timeout); if (*timeout >= 0) *timeout += __timeout; } /*设置为运行状态*/ __set_current_state(TASK_RUNNING); /*清理等待队列*/ poll_freewait(&table); return retval; } static unsigned int sock_poll(struct file *file, poll_table *wait) { struct socket *sock; /*约定socket的file->private_data字段放着对应的socket结构指针*/ sock = file->private_data; /*对应了三个协议的函数tcp_poll,udp_poll,datagram_poll,其中udp_poll几乎直接调用了datagram_poll 累了,先休息一下,这三个函数以后分析*/ return sock->ops->poll(file, sock, wait); }
其他重要函数一览
static int max_select_fd(unsigned long n, fd_set_bits *fds)
返回在fd_set中已经打开的,并且小于用户指定最大值,的fd
static inline int get_fd_set(unsigned long nr, void __user *ufdset, unsigned long *fdset)
从用户空间拷贝fd_set到内核
static inline void zero_fd_set(unsigned long nr, unsigned long *fdset)
把fd_set清零
static inline unsigned long __must_check set_fd_set(unsigned long nr, void __user *ufdset, unsigned long *fdset)
把fd_set拷贝回用户空间
static inline int signal_pending(struct task_struct *p)
目前进程有信号需要处理
struct file *fget_light(unsigned int fd, int *fput_needed)
由fd得到其对应的file结构指针,并增加其引用计数
static inline void fput_light(struct file *file, int fput_needed)
释放由fget_light得到的file结构指针,减少其引用计数
set_current_state
设置当前进程的状态
static inline int cond_resched(void)
判断是否有进程需要抢占当前进程,如果是将立即发生调度。就是额外增加一个抢占点。
signed long __sched schedule_timeout(signed long timeout)
当前进程睡眠timeout个jiffies
rcu_read_lock
rcu_read_unlock
Linux 2.6新加入的rcu锁。读锁的加锁、解锁函数
参考http://www.ibm.com/developerworks/cn/linux/l-rcu
poll_freewait
poll_initwait
poll_wait
...
和文件IO,poll机制有关的几个函数,参考《Linux设备驱动(第三版)》6.3
tcp_poll
udp_poll
datagram_poll
协议层的poll函数
发表评论
-
HTTP客户请求的数据格式说明
2008-12-27 18:51 1365HTTP客户请求的数据格式说明 HTTP请求包括三部分:请 ... -
Linux和WIndows都不支持自连接
2008-12-09 09:29 1709Cygwin下结果: connect() 127 Transp ... -
SO_RCVBUF选项的大小和接受窗口字节数关系
2008-12-08 20:08 7751运行下面程序的同时打开任何一个抓包工具分析。 实验表明Win ... -
TCPIP点点滴滴
2008-12-06 23:32 1267之前学到的TCPIP里面很多点点滴滴都没有做笔记,可以了。 ... -
UDP的ICMP差错的测试程序
2008-12-03 10:07 2177/* 作者:张亚霏 2008-12-03早 */ ... -
epoll的一个demo,备忘
2008-12-02 21:12 4380/** 张亚霏修改 文件名:epoll_demo. ... -
用asio的定时器实现带超时的connect,备忘
2008-11-30 19:39 7409// test.cpp : 定义控制台应用程序的入口点。 ... -
自己写的IOCP的程序,备忘(2009-02-05更新)
2008-11-30 19:10 3003#include <winsock2.h> ... -
TCP连接非正常断开的检测
2008-10-24 14:58 4537如果主机崩溃,write是 ... -
exit和_exit的区别
2008-10-23 19:38 1888exit()在结束调用它的进程之前,要进行如下步骤: 1.cl ... -
Linux-2.6.25 TCPIP函数调用大致流程
2008-10-08 21:53 6559Linux-2.6.25 TCPIP函数调用大致流程 学习目的 ... -
UDP数据包截断
2008-10-04 11:20 3304Posix系列的recv、recvfrom、read函数均无法 ... -
TCP协议里的时间
2008-10-04 11:18 3039一、建立连接 在发送SYN报文段后,如果在75秒没有收到相应 ...
相关推荐
博文链接:https://zhangyafeikimi.iteye.com/blog/250511
linux-2.6.25.tar.bz2 正版 编译很成功
android开发的使用的linux内核,2.6.25版本。
天嵌科技出品linux2.6.25超详细的移植手册 专为 tq2440 sky2440用户精心打造的学习开发使用手册
虚拟机vm下fedora系统下安装vmtools时用到的一个软件。为了方便虚拟机系统读取该文件。已经把他转成了.iso文件。只需加载到光驱就可以在虚拟机里读取了。 安装过程参考:...
在安装vmware tools时需要,这是.iso文件,直接在虚拟机中光盘中打开并拷贝到根目录下,在终端中运行rpm -ivh kernel-devel-2.6.25-14.fc9.i686.rpm即可
kernel-devel-2.6.25-14.fc9.i686 FEDORA9内核头文件
kernel-2.6.25-14.fc9.src.rpm 这个更详细点 带各种配置文件
libxml2-2.6.25.tar.gz for linux
资源来自pypi官网。 资源全名:efel-2.6.25.tar.gz
其中主机操作系统采用 Fedora 9( Linux-2.6.25),目标机采用友善之臂mini2440 ,Android OS内核采用 Linux-2.6.25-android-1.0,编译工具采用 arm-linux-gcc-4.3.2。最后,成功将Android OS移植到开发板上。
Smarty-2.6.25 为php的一个模板。
Smarty模板引擎是PHP官方的模板引擎,它分开了逻辑程序和外在的内容,提供了一种易于管理的方法。
Smarty是一个使用PHP写出来的模板引擎,是目前业界最著名的PHP模板引擎之一。它分离了逻辑代码和外在的内容,提供了一种易于管理和使用的方法,用来将原本与HTML代码混杂在一起PHP代码逻辑分离。...
NULL 博文链接:https://zongyukai20070419095606.iteye.com/blog/655794
你是不是也在安装Fedora的VMware-Tools遇到麻烦呢?当你找到答案的时候,你就需要这个软件包了!
HID是Human Interface Devices 的缩写.翻译成中文即为人机交互设备.这里的人机交互设备是一个宏观上面的概念,任何设备,只要符合HID spec,都可以称之为HID 设备...分析的代码主要集中在linux-2.6.25/drivers/hid 目录下.
PEAK-System公司的PCAN接口卡驱动程序,适用于Linux2.6.25。解压后在documentation目录下有PDF的说明手册。
Linux2.6.25平台下的I2C驱动架构分析
kernel-devel-2.6.25.14-108.fc9.i686.rpm 内核源码 解压过后 usr/src/kernels/2.6.25.14-108.fc9.i686/