标签:
先记录一下学习的成果,慢慢完善
四个相关函数
fd_set的结构在上一篇中有讲,同时解释了为什么最大1024.
-fd_set为1024/32的long型数组结构体。也就是结构体里面保存了long型数组
int FD_ZERO(int fd, fd_set *fdset);
int FD_CLR(int fd, fd_set *fdset);
int FD_SET(int fd, fd_set *fd_set);
int FD_ISSET(int fd, fd_set *fdset);
FD_ZERO,将整个fd_set全部置0;
FD_CLR 清楚某一位,会自动计算出在在那一个字节,那个bit位
后面的同理
当声明了一个文件描述符集后,必须用FD_ZERO将所有位置零。之后将我们所感兴趣的描述符所对应的位置位,操作如下:
fd_set rset;
int fd;
FD_ZERO(&rset);
FD_SET(fd, &rset);
FD_SET(stdin, &rset);
select返回后,用FD_ISSET测试给定位是否置位:
if(FD_ISSET(fd, &rset)
{ … }
http://blog.csdn.net/lingfengtengfei/article/details/12392449使用方法,参数含义
具体解释select的参数:
(1)intmaxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错。
说明:对于这个原理的解释可以看上边fd_set的详细解释,fd_set是以位图的形式来存储这些文件描述符。maxfdp也就是定义了位图中有效的位的个数。
(2)fd_set*readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读;如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。
(3)fd_set*writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。
(4)fd_set*errorfds同上面两个参数的意图,用来监视文件错误异常文件。
(5)structtimeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即 select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。
说明:
函数返回:
(1)当监视的相应的文件描述符集中满足条件时,比如说读文件描述符集中有数据到来时,内核(I/O)根据状态修改文件描述符集,并返回一个大于0的数。
(2)当没有满足条件的文件描述符,且设置的timeval监控时间超时时,select函数会返回一个为0的值。
(3)当select返回负值时,发生错误。
http://blog.csdn.net/lingfengtengfei/article/details/12392449
查看源码并分析
sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct timeval __user *tvp)
{
fd_set_bits fds;//这是等会要传入内核的用户数据,也就是轮询的这个结构体,他包含6个成员,包括了inp,outp,exp,后面三个表示要传出的结果
char *bits;
long timeout;
int ret, size, max_fdset;
timeout = MAX_SCHEDULE_TIMEOUT;
if (tvp) {//如果timeval 结构体传入非NULL
time_t sec, usec;
if ((ret = verify_area(VERIFY_READ, tvp, sizeof(*tvp)))
|| (ret = __get_user(sec, &tvp->tv_sec))
|| (ret = __get_user(usec, &tvp->tv_usec)))//将时间参数copy到内核空间
goto out_nofds;//数据无效??
ret = -EINVAL;
if (sec < 0 || usec < 0)//参数检查
goto out_nofds;
if ((unsigned long)sec < MAX_SELECT_SECONDS) {
timeout = ROUND_UP(usec, 1000000 / HZ);
timeout += sec * (unsigned long)HZ;
}
}
ret = -EINVAL;//注意无论timeval 是否非NULL都会到这里
if (n < 0)
goto out_nofds;
/* max_fdset can increase, so grab it once to avoid race */
max_fdset = current->files->max_fdset;
if (n > max_fdset)
n = max_fdset;
/*
* 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.
*/
ret = -ENOMEM;
size = FDS_BYTES(n);
bits = select_bits_alloc(size);
if (!bits)
goto out_nofds;
fds.in = (unsigned long *)bits;
fds.out = (unsigned long *)(bits + size);
fds.ex = (unsigned long *)(bits + 2 * size);
fds.res_in = (unsigned long *)(bits + 3 * size);
fds.res_out = (unsigned long *)(bits + 4 * size);
fds.res_ex = (unsigned long *)(bits + 5 * size);
if ((ret = get_fd_set(n, inp, fds.in)) ||
(ret = get_fd_set(n, outp, fds.out)) ||//复制到fds,下面会将结果情况,用于返回
goto out;
zero_fd_set(n, fds.res_in);
zero_fd_set(n, fds.res_out);
zero_fd_set(n, fds.res_ex);
ret = do_select(n, &fds, &timeout);//这里进入真正轮询的核心函数
if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
time_t sec = 0, usec = 0;
if (timeout) {
sec = timeout / HZ;
usec = timeout % HZ;
usec *= (1000000 / HZ);
}
put_user(sec, &tvp->tv_sec);
put_user(usec, &tvp->tv_usec);//还剩多少时间,计算并复制到用户空间
}
if (ret < 0)
goto out;
if (!ret) {
ret = -ERESTARTNOHAND;
if (signal_pending(current))//在这里等待可读可写?如果time结构体为nuLL那么无限等待。
goto out;
ret = 0;//超时放回
}
if (set_fd_set(n, inp, fds.res_in) ||//计算的结果复制到inp,outp,exp,因此,如果没有数据可用,那么全都是0,这也就是每次调用select之前都要重新设置fd-set。不然全都是0
set_fd_set(n, outp, fds.res_out) ||
set_fd_set(n, exp, fds.res_ex))
ret = -EFAULT;
out:
select_bits_free(bits, size);
out_nofds:
return ret;
}
int do_select(int n, fd_set_bits *fds, long *timeout)
{
struct poll_wqueues table;
poll
_table *wait;
int retval, i;
long __timeout = *timeout;
spin_lock(¤t->files->file_lock);//保证操作,上锁
retval = max_select_fd(n, fds);//这里解释了为什么n要设置为最大的fd+1,他可以计算需要轮询几个字节,如果不这样,那么岂不是每次都要轮询1024个描述符,效率太低
spin_unlock(¤t->files->file_lock);
if (retval < 0)
return retval;
n = retval;
poll_initwait(&table);
wait = &table.pt;
if (!__timeout)
wait = NULL;
retval = 0;
for (;;) {
unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;
set_current_state(TASK_INTERRUPTIBLE);//select是可中断的
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) {
unsigned long in, out, ex, all_bits, bit = 1, mask, j;
unsigned long res_in = 0, res_out = 0, res_ex = 0;
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) {
i += __NFDBITS;
continue;
}
for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) {
if (i >= n)
break;
if (!(bit & all_bits))
continue;
file = fget(i);
if (file) {
f_op = file->f_op;
mask = DEFAULT_POLLMASK;
if (f_op && f_op->poll)
mask = (*f_op->poll)(file, retval ? NULL : wait);//驱动的poll函数,写过驱动的都知道
fput(file);
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++;
}
}
cond_resched();
}
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))
break;
if (table.error) {
retval = table.error;
break;
}
__timeout = schedule_timeout(__timeout);
}
__set_current_state(TASK_RUNNING);
poll_freewait(&table);
/*
* Up-to-date the caller timeout.
*/
*timeout = __timeout;
return retval;
}
在上面的源码中清晰的说明了为什么select要那么用
如有不对,请指出,谢谢
标签:
原文地址:http://blog.csdn.net/wejoncy/article/details/51334668