套接字之close系统调用
Posted wanpengcoder
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了套接字之close系统调用相关的知识,希望对你有一定的参考价值。
close系统调用用于关闭文件描述符,其系统调用实现如下所示;
1 / 2 * Careful here! We test whether the file pointer is NULL before 3 * releasing the fd. This ensures that one clone task can‘t release 4 * an fd while another clone is opening it. 5 */ 6 SYSCALL_DEFINE1(close, unsigned int, fd) 7 { 8 int retval = __close_fd(current->files, fd); 9 10 /* can‘t restart close syscall because file table entry was cleared */ 11 if (unlikely(retval == -ERESTARTSYS || 12 retval == -ERESTARTNOINTR || 13 retval == -ERESTARTNOHAND || 14 retval == -ERESTART_RESTARTBLOCK)) 15 retval = -EINTR; 16 17 return retval; 18 } 19 EXPORT_SYMBOL(sys_close);
本文重点在于分析套接字的的close部分,所以简要列出close系统调用通用流程的函数调用关系,如下;
补充:其中重点注意下fput函数,该函数会先现将文件的引用计数-1,然后判断是否为0,为0的时候才会进行继续的流程,也就是说当socket存在多个引用的时候,只有最后一个close才会触发后面的调度销毁流程,也是close与shutdown不同的一个地方;
1 /** 2 * close系统调用函数调用关系 3 *__close_fd 4 * |-->filp_close 5 * |-->fput 将file加入到delayed_fput_list链表 6 * |-->schedule_delayed_work 调度延迟执行队列处理链表 7 * |-->delayed_fput 8 * |-->__fput 9 * |-->file->f_op->release 调用文件的release操作 10 */
可见,在close系统调用中会调用文件的release操作,所以我们本文重点分析socket的release操作实现;
socket实现的文件操作结构如下所示,其中本文讨论的release函数实现为sock_close;
1 /* socket文件操作函数 */ 2 static const struct file_operations socket_file_ops = { 3 .owner = THIS_MODULE, 4 .llseek = no_llseek, 5 .read_iter = sock_read_iter, 6 .write_iter = sock_write_iter, 7 .poll = sock_poll, 8 .unlocked_ioctl = sock_ioctl, 9 #ifdef CONFIG_COMPAT 10 .compat_ioctl = compat_sock_ioctl, 11 #endif 12 .mmap = sock_mmap, 13 .release = sock_close, 14 .fasync = sock_fasync, 15 .sendpage = sock_sendpage, 16 .splice_write = generic_splice_sendpage, 17 .splice_read = sock_splice_read, 18 };
在socket_release函数中,会调用socket操作函数release,ipv4对应inet_release;
1 /* 关闭socket */ 2 static int sock_close(struct inode *inode, struct file *filp) 3 { 4 /* socket关闭 */ 5 sock_release(SOCKET_I(inode)); 6 return 0; 7 }
1 /** 2 * sock_release - close a socket 3 * @sock: socket to close 4 * 5 * The socket is released from the protocol stack if it has a release 6 * callback, and the inode is then released if the socket is bound to 7 * an inode not a file. 8 */ 9 10 void sock_release(struct socket *sock) 11 { 12 if (sock->ops) { 13 struct module *owner = sock->ops->owner; 14 15 /* 调用socket操作中的release */ 16 sock->ops->release(sock); 17 sock->ops = NULL; 18 module_put(owner); 19 } 20 21 if (rcu_dereference_protected(sock->wq, 1)->fasync_list) 22 pr_err("%s: fasync list not empty! ", __func__); 23 24 /* 减少cpu的套接口数量 */ 25 this_cpu_sub(sockets_in_use, 1); 26 27 /* 容错处理 */ 28 if (!sock->file) { 29 iput(SOCK_INODE(sock)); 30 return; 31 } 32 33 /* 套接口完成关闭,继续执行close系统调用其他流程 */ 34 sock->file = NULL; 35 }
inet_release负责退出组播组,根据是否开启linger标记来设置延迟关闭时间,并且调用传输层的close函数,对于tcp来说,其调用的为tcp_close;
1 /* 2 * The peer socket should always be NULL (or else). When we call this 3 * function we are destroying the object and from then on nobody 4 * should refer to it. 5 */ 6 int inet_release(struct socket *sock) 7 { 8 struct sock *sk = sock->sk; 9 10 if (sk) { 11 long timeout; 12 13 /* Applications forget to leave groups before exiting */ 14 /* 退出组播组 */ 15 ip_mc_drop_socket(sk); 16 17 /* If linger is set, we don‘t return until the close 18 * is complete. Otherwise we return immediately. The 19 * actually closing is done the same either way. 20 * 21 * If the close is due to the process exiting, we never 22 * linger.. 23 */ 24 timeout = 0; 25 26 /* 27 设置了linger标记,进程未在退出, 28 则设置lingertime延迟关闭时间 29 */ 30 if (sock_flag(sk, SOCK_LINGER) && 31 !(current->flags & PF_EXITING)) 32 timeout = sk->sk_lingertime; 33 sock->sk = NULL; 34 35 /* 调用传输层的close函数 */ 36 sk->sk_prot->close(sk, timeout); 37 } 38 return 0; 39 }
tcp_close分析请移步另外一篇文章<TCP层close系统调用的实现分析>;
以上是关于套接字之close系统调用的主要内容,如果未能解决你的问题,请参考以下文章