1. 什么是IO multiplexing?
IO multiplexing是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。在Linux系统中,常用的IO multiplexing手段有 select、poll 和 epoll。
IO multiplexing 主要用于处理网络请求,例如可以把多个请求句柄添加到 select 中进行监听,当有请求可进行IO的时候就会告知进程,并且把就绪的请求句柄保存下来,进程只需要对这些就绪的请求进行IO操作即可。下面通过一幅图来展示 select 的使用方式
2.IO multiplexing的基本原理
进程中阻塞select的系统调用,而不是阻塞I/O调用。
阻塞select系统调用等待socket可读,当select返回该socket可读时,用户进程激活,便调用recvfrom将数据报复制到的应用程序缓区中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TNKVK2j5-1608088631639)(https://notes.shichao.io/unp/figure_6.3.png)]
3.Select process执行流
3.1 sys_selcet
下面为sys_select系统调用的代码
SYSCALL_DEFINE5(select, int, n, fd_set __user *, inp, fd_set __user *, outp,
fd_set __user *, exp, struct timeval __user *, tvp)
{
struct timespec end_time, *to = NULL;
struct timeval tv;
int ret;
if (tvp) { /*If there is a timeout value*/
if (copy_from_user(&tv, tvp, sizeof(tv)))
return -EFAULT;
to = &end_time;
/* set timeout, if Invalid time,then return -EINVAL*/
if (poll_select_set_timeout(to,
tv.tv_sec + (tv.tv_usec / USEC_PER_SEC),
(tv.tv_usec % USEC_PER_SEC) * NSEC_PER_USEC))
return -EINVAL;
}
ret = core_sys_select(n, inp, outp, exp, to);
ret = poll_select_copy_remaining(&end_time, tvp, 1, ret);
return ret;
}
3.2 core_sys_select
该函数的主要作用是将用户空间的参数拷贝到内核,及将系统调用的返回值拷贝到用户空间
int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
fd_set __user *exp, struct timespec *end_time)
{
fd_set_bits fds;
/*
计算max_fds,依据它使用kmalloc为fd_set_bits类型的变量分配内存:bits,该变量存储readfds、writefds、exceptfds共计6个集合
*/
......(Part omitted)
/ * get_fd_set just calls copy_from_user to copy FD_SET from user space * /
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); //clear
zero_fd_set(n, fds.res_out);
zero_fd_set(n, fds.res_ex);
ret = do_select(n, &fds, end_time);
if (ret < 0) // nagetive,error
goto out;
if (!ret) {
ret = -ERESTARTNOHAND;
if (signal_pending(current)) // 检测到有信号则系统调用退出,返回用户空间执行信号处理函数
goto out;
ret = 0;
}
/ *Copy the result set back to user space* /
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); /* corresponding to kmalloc above */
out_nofds:
return ret;
}
参考
Select system call source code analysis
Chapter 6. I/O Multiplexing: The select and poll Functions