套接字之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 cant 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系统调用的主要内容,如果未能解决你的问题,请参考以下文章

套接字之sendto系统调用

套接字之recvfrom系统调用

套接字之recvmsg系统调用

套接字之send系统调用

套接字之recv系统调用

套接字之select系统调用