套接字之select系统调用
select是IO多路复用的一种方式,用来等待一个列表中的多个描述符的可读可写状态;
1 SYSCALL_DEFINE5(select, int, n, fd_set __user *, inp, fd_set __user *, outp, 2 fd_set __user *, exp, struct timeval __user *, tvp) 3 { 4 struct timespec64 end_time, *to = NULL; 5 struct timeval tv; 6 int ret; 7 8 if (tvp) { 9 if (copy_from_user(&tv, tvp, sizeof(tv))) 10 return -EFAULT; 11 12 to = &end_time; 13 if (poll_select_set_timeout(to, 14 tv.tv_sec + (tv.tv_usec / USEC_PER_SEC), 15 (tv.tv_usec % USEC_PER_SEC) * NSEC_PER_USEC)) 16 return -EINVAL; 17 } 18 19 ret = core_sys_select(n, inp, outp, exp, to); 20 ret = poll_select_copy_remaining(&end_time, tvp, 1, ret); 21 22 return ret; 23 }
本文主要分析socket的select操作,所以对于select系统调用前面通用的部分,我们只分析其调用关系,如下,可见在do_select函数中,会调用文件操作的poll函数;
1 /** 2 * select系统调用函数调用关系 3 * sys_select 4 * |-->core_sys_select 5 * |-->do_select 6 * |-->f_op->poll 调用文件的poll操作 7 */
socket文件操作结构实现如下,我们本文重点分析poll操作,即sock_poll函数;
1 /* 2 * Socket files have a set of 'special' operations as well as the generic file ones. These don't appear 3 * in the operation structures but are done directly via the socketcall() multiplexor. 4 */ 5 /* socket文件操作函数 */ 6 static const struct file_operations socket_file_ops = { 7 .owner = THIS_MODULE, 8 .llseek = no_llseek, 9 .read_iter = sock_read_iter, 10 .write_iter = sock_write_iter, 11 .poll = sock_poll, 12 .unlocked_ioctl = sock_ioctl, 13 #ifdef CONFIG_COMPAT 14 .compat_ioctl = compat_sock_ioctl, 15 #endif 16 .mmap = sock_mmap, 17 .release = sock_close, 18 .fasync = sock_fasync, 19 .sendpage = sock_sendpage, 20 .splice_write = generic_splice_sendpage, 21 .splice_read = sock_splice_read, 22 };
sock_poll函数在获取到socket之后,会调用其操作中的poll函数,其中tcp为tcp_poll,udp为udp_poll;
1 /* No kernel lock held - perfect */ 2 static unsigned int sock_poll(struct file *file, poll_table *wait) 3 { 4 unsigned int busy_flag = 0; 5 struct socket *sock; 6 7 /* 8 * We can't return errors to poll, so it's either yes or no. 9 */ 10 /* 获取到socket */ 11 sock = file->private_data; 12 13 if (sk_can_busy_loop(sock->sk)) { 14 /* this socket can poll_ll so tell the system call */ 15 busy_flag = POLL_BUSY_LOOP; 16 17 /* once, only if requested by syscall */ 18 if (wait && (wait->_key & POLL_BUSY_LOOP)) 19 sk_busy_loop(sock->sk, 1); 20 } 21 22 /* 执行socket操作中的poll,tcp为tcp_poll,udp为udp_poll */ 23 return busy_flag | sock->ops->poll(file, sock, wait); 24 }
tcp_poll的代码分析会在阅读tcp源码时补充;