套接字之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!\n", __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系统调用的实现分析>;

原文地址:https://www.cnblogs.com/wanpengcoder/p/11749264.html

时间: 2024-08-30 07:12:59

套接字之close系统调用的相关文章

套接字之recv系统调用

recv系统调用对sys_recvfrom进行了简单的封装,只是其中不包含地址信息,其只需要从建立连接的另一端接收信息: 1 /* 2 * Receive a datagram from a socket. 3 */ 4 5 SYSCALL_DEFINE4(recv, int, fd, void __user *, ubuf, size_t, size, 6 unsigned int, flags) 7 { 8 return sys_recvfrom(fd, ubuf, size, flags,

套接字之send系统调用

send系统调用只是对sendto系统调用进行了封装,传递的参数不包含目的地址信息,数据会发送到已经建立连接的另一端的地址: 1 /* 2 * Send a datagram down a socket. 3 */ 4 5 SYSCALL_DEFINE4(send, int, fd, void __user *, buff, size_t, len, 6 unsigned int, flags) 7 { 8 return sys_sendto(fd, buff, len, flags, NULL

套接字之sendmsg系统调用

sendmsg系统调用允许在用户空间构造消息头和控制信息,用此函数可以发送多个数据缓冲区的数据,并支持控制信息:当调用进入内核后,会将用户端的user_msghdr对应拷贝到内核的msghdr中,然后进行数据发送: 1 SYSCALL_DEFINE3(sendmsg, int, fd, struct user_msghdr __user *, msg, unsigned int, flags) 2 { 3 /* 不支持64位采用32位兼容标记 */ 4 if (flags & MSG_CMSG_

套接字之sendto系统调用

sendto系统调用用于向指定的目的地址发送数据,其系统调用的流程比较容易理解,如下面所示,其主要完成 (1)将用户数据组织成msghdr,(2)而后调用socket操作的sendmsg:ipv4对应的sendmsg实现为inet_sendmsg,该函数进行端口自动绑定检查和绑定后,调用传输层的sendmsg函数发送数据,比如tcp会调用tcp_sendmsg函数,这里不详细介绍,读到tcp模块在进行补充: 1 /* 2 * Send a datagram to a given address.

套接字之recvmsg系统调用

recvmsg系统调用允许用户指定msghdr结构来接收数据,可以将数据接收到多个缓冲区中,并且可以接收控制信息:接收信息过程与其他接收系统调用核心一致,都是调用传输层的接收函数进行数据接收: 1 SYSCALL_DEFINE3(recvmsg, int, fd, struct user_msghdr __user *, msg, 2 unsigned int, flags) 3 { 4 /* 不支持64位采用32位兼容标记 */ 5 if (flags & MSG_CMSG_COMPAT) 6

套接字之recvfrom系统调用

recvfrom系统调用通过用户传入的接收空间构造msghdr,并且调用sock_recvmsg,该函数调用socket操作的recvmsg函数sock->ops->recvmsg,ipv4对应的是inet_recvmsg,该函数调用传输层的sk->sk_prot->recvmsg来接收数据,如tcp则调用tcp_recvmsg,接收完成之后记录地址结构长度信息: 1 /* 2 * Receive a frame from the socket and optionally rec

套接字之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 r

套接字之msghdr结构

用户端在使用sendmsg/recvmsg发送或者接收数据时,会使用msghdr来构造消息,其对应的内核结构为user_msghdr:其中msg_iov向量指向了多个数据区,msg_iovlen标识了数据区个数:在通过系统调用进入内核后,该结构中的信息会拷贝给内核的msghdr结构: 1 /* 用户空间的消息头 */ 2 struct user_msghdr { 3 /* 指向地址结构 */ 4 void __user *msg_name; /* ptr to socket address st

sockets: 套接字选项相关的系统调用

########################################################### 套接字选项相关的系统调用: ########################################################### 将optval指向的单元中的值设置给optname选项: int setsockopt(int sockfd, int level, int optname, const void *optval,socklen_t optlen)