select 源码学习记录

先记录一下学习的成果,慢慢完善
四个相关函数
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(&current->files->file_lock);//保证操作,上锁
    retval = max_select_fd(n, fds);//这里解释了为什么n要设置为最大的fd+1,他可以计算需要轮询几个字节,如果不这样,那么岂不是每次都要轮询1024个描述符,效率太低
    spin_unlock(&current->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要那么用
如有不对,请指出,谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值