Just a journey

套接字中select函数的理解

套接字中select函数的理解

问题

  1. 为什么select函数的时间复杂度是O(n)?
  2. select函数如何判断文件描述符是ready状态?

select函数

select函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或 多个事件发生或经历一段指定的时间后才唤醒它。其定义如下:   

#include <sys/time.h> /* For portability */
#include <sys/select.h>

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

	Returns number of ready file descriptors, 0 on timeout, or –1 on error

nfds参数指定待测试的描述符个数,它的值是待测试的最大描述符加1,描 述符0,1,2,…..一直到nfds-1均被测试。参数readfds、writefds和exceptfds
用于监控读、写和异常的文件描述符集合。最后的参数timeout用于超时时间。   

select函数的内核实现   

标准库中的select函数是由系统调用sys_select实现。其调用关系如下:

sys_select==>   
	core_sys_select==>   
		do_select

数据结构

#undef __NFDBITS
#define __NFDBITS   (8 * sizeof(unsigned long))

/* __FD_SETSIZE 代表select可以监控的fd最大数目。*/
#undef __FD_SETSIZE
#define __FD_SETSIZE    1024	

#undef __FDSET_LONGS
#define __FDSET_LONGS   (__FD_SETSIZE/__NFDBITS)

typedef struct {
    unsigned long fds_bits [__FDSET_LONGS];
} __kernel_fd_set;

typedef __kernel_fd_set     fd_set;

typedef struct {
    unsigned long *in, *out, *ex;
    unsigned long *res_in, *res_out, *res_ex;
} fd_set_bits;

/*
 * How many longwords for "nr" bits? nr需要的字节
 */
#define FDS_BITPERLONG  (8*sizeof(long))
#define FDS_LONGS(nr)   (((nr)+FDS_BITPERLONG-1)/FDS_BITPERLONG)
#define FDS_BYTES(nr)   (FDS_LONGS(nr)*sizeof(long))

主要代码流程   

在函数do_select中,将执行一个for循环。

    		for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) {
            	int fput_needed;
                if (i >= n)
                    break;
                if (!(bit & all_bits))
                    continue;
                /* TODO: 这个file 如何和tcp产生联想的? */
                /*
                 struct socket_alloc {
                    struct socket socket;
                    struct inode vfs_inode;
                 };
            
                 TODO: file 的f_op 和 inode 的 i_fop 有什么关系。
                 */
                file = fget_light(i, &fput_needed);
                if (file) {
                    f_op = file->f_op;
                    mask = DEFAULT_POLLMASK;
                    /*  
                     这里应该是tcp_poll/udp_poll 
                     udp的接收应该会调用skb_recv_datagram 
                    */
                    if (f_op && f_op->poll)
                        mask = (*f_op->poll)(file, retval ? NULL : wait);
                    fput_light(file, fput_needed);
                    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++;
                    }
                }
                /* perform a process switch */
         		cond_resched();
            }
            if (res_in)
                *rinp = res_in;
            if (res_out)
                *routp = res_out;
            if (res_ex)
                *rexp = res_ex;
        }
        wait = NULL;
        /*
         signal_pending
         Returns the value 1 (true) if the process identified by the *p process descriptor has
         nonblocked pending signals, and returns the value 0 (false) if it doesn't. 
         */
        if (retval || !*timeout || signal_pending(current))
            break;
comments powered by Disqus