• 套接字之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系统调用的实现分析>;

  • 相关阅读:
    Linux 线程池的简单实现
    m3u8(HLS) 抓包
    一个面试问题的思考
    简单实现无需密码 sudo
    转: NAT 穿透
    一个平均分配算法
    raft 算法扫盲
    20210615 JVM 优化
    20210614. 并发编程
    20210606 Java 并发编程之美
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/11749264.html
Copyright © 2020-2023  润新知