select 和 poll的用法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010318270/article/details/88561107

## select  ##

一、用户态select用法

#include <sys/select.h>
#include <sys/time.h>

int select(int maxfdp1, 
           fd_set *readset, 
           fd_set *writeset, 
           fd_set *exceptset, 
           const struct timeval *timeout);
返回值:若有就绪描述符则为其数目,若超时则为0,若出错则为-1。

参数:
maxfdp1:指定待测试的描述符个数,它的值是待测试的最大描述符加1,描述符0,1,2,...,一直到maxfdp1-1均将被测试。
readset、writeset、exceptset:指定我们要让内核测试读、写和异常条件的描述符集。如果对某一个的条件不感兴趣,就可以把它设为空指针。
timeout:告诉内核等待所指定描述符中的任何一个就绪可花多长时间。
(1)永远等待下去:参数置为空指针NULL,仅在有一个描述字准备好IO时才返回。
(2)等待一段固定时间
(3)根本不等待:设置为0,检查描述字后立即返回,称为轮询(polling)。
	struct timeval
	{
	  long tv_sec;     /* Seconds.  */
	  long tv_usec;    /* Microseconds.  */
	};

struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符,可通过以下四个宏进行设置:           
void FD_ZERO(fd_set *fdset);  // 清空集合
void FD_SET(int fd, fd_set *fdset);    // 将一个给定的文件描述符加入集合之中
void FD_CLR(int fd, fd_set *fdset);    // 将一个给定的文件描述符从集合中删除
void FD_ISSET(int fd, fd_set *fdset);  // 检查集合中指定的文件描述符是否可以读写

其中FD_为前缀的函数并非系统调用,而是几个对fd_set进行相关位操作的宏,对应原型定义在
sys\select.h和bits\select.h
typedef struct
{
    __fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
}fd_set

fd_set结构体的定义实际包含的是fds_bits位数组,其大小是固定的,由FD_SETSIZE指定(bits\typesizes.h中),当前内核中数值为1024。可见每次select系统调用可监听处理的文件描述符最大数量为1024。

二、内核态下select系统调用入口
sys_select()->core_sys_select()->do_select()

1、sys_select()
(1)从用户态拷贝超时时间到内核态,将超时时间换算成jiffies
(2)调用core_sys_select()
(3)将剩余的时间拷贝到用户空间

2、core_sys_select()
(1)定义long stack_fds[SELECT_STACK_ALLOC/sizeof(long)],定义一个栈上数组,用于高效处理传入以及待传出的可读、可写及异常文件描述符集合,
如果空间不够使用,则调用kmalloc/kfree申请内存。
(2)定义fd_set_bits fds,用来保存指向描述符集合,可读、可写、异常的描述符集合,每份有一个输入和一个输出(用于结果返回)

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

(3)依次使用get_fd_set(copy_from_user)拷贝待监听的可读、可写及异常事件对应的文件描述符集合 [从用户态拷贝到内核态]
(4)调用do_select()
(5)依次使用set_fd_set(copy_to_user)拷贝各事件处理结果集合到对应传入的三个事件集合 [从内核态拷贝到用户态] [每次select调用前都需要清空(FD_ZERO)传入的fd事件集合]

3、do_select(),每次select调用都要轮询完成所有fd的挂载等待队列及事件监测
(1)retval = max_select_fd(n, fds);  //根据设置的fd位图fds,检查确认所有被置位对应的fd确实被打开了,并返回最大的fd。
(2)poll_initwait(&table);  //初始化poll_wqueues.poll_table.poll_queue_proc函数指针,f_op->poll会用到
(3) for(;;){
  依次检查fds中每一个被置1的位对应的fd;
  调用这个fd对应的驱动程序提供的poll函数,socket对应是sock_poll;
  检查poll函数的返回值,若有关心的事件发生,则设置fds对应的结果变量。
}
(4)poll_freewait(&table);  //释放 poll_table

三、select相关问题
1、单个进程能够监视的文件描述符的数量最大为1024。
2、每次调用select都需要把fd集合从用户态拷贝到内核态,同时需要把结果从内核态拷贝到用户态
3、select返回的是含有整个句柄的数组,需要应用程序遍历整个数组才能发现哪些句柄发生了事件
4、select的触发方式是水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作,那么之后每次select调用还是会将这些文件描述符通知进程。
 

##  poll  ##

一、用户态poll用法

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

参数:
fds:指向一个结构体数组的第0个元素的指针,每个数组元素都是一个struct pollfd结构,用于指定测试某个给定的fd的条件。
nfds:表示fds结构体数组的长度
timeout:表示poll函数的超时时间,单位是毫秒

返回值:
返回值小于0,表示出错
返回值等于0,表示poll函数等待超时
返回值大于0,表示poll由于监听的文件描述符就绪返回,并且返回结果就是就绪的文件描述符的个数。
struct pollfd {
	int fd;  //每个pollfd结构体指定了一个被监视的文件描述符
	short events;  //表示要告诉操作系统需要监控的fd事件(in, out, err),每一个事件有多个取值
	short revents;  //文件描述符的操作结果事件,内核在返回时设置这个域,events域中请求的任何事件都可能在revents域中返回
};

struct poll_list {
	struct poll_list *next;
	int len;
	struct pollfd entries[0];
};

二、poll解决的问题
poll改变了fds集合的描述方式,使用了pollfd结构而不是select的fd_set结构,使得poll支持的fds集合限制远大于select的1024。

猜你喜欢

转载自blog.csdn.net/u010318270/article/details/88561107
今日推荐