公开学习文档

公开学习文档


TCP内存

<h2>概述</h2> <p>1、客户端创建 socket 涉及以下几方面的内存:socket 对象(sock_inode_cache)、tcp_sock 对象(TCP)、dentry 对象(dentry)、file 对象(filp -&gt; kmalloc-256) 2、服务端是通过 accept 创建 sokcet,其对象和客户端类似,有一点不太一样的是:在第 3 次握手的时候就创建了 tcp_sock 放在全连接队列中,因此在 accept 时直接取出来就行 3、一条空的 socket 连接占用内存大概 3.3 kb(包含上面 4 个对象,以及 socket_wq、端口绑定关系 inet_bind_bucket(服务端不用),后 2 者都是在 kmalloc-64 中) 4、TIME_WAIT 状态下空的 TCP 连接占用内存大概 0.3-0.4 KB 左右 5、客户端发送的数据,即使服务端应用层不收取,但只要服务端协议栈回了 ACK,客户端就认为数据已经被收取,就会释放发送的数据空间 6、待分析发送、接收缓冲区</p> <h2>接收和发送缓冲区</h2> <p><img src="https://www.showdoc.com.cn/server/api/attachment/visitFile?sign=24c2c8cadac3f5f67ef205202125910a&amp;amp;file=file.png" alt="" /></p> <h2>客户端创建 socket</h2> <p>创建 socket 涉及的内存申请:</p> <pre><code class="language-c">// file: net/socket.c SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol) { retval = sock_create(family, type, protocol, &amp;amp;sock); // 涉及 socket 和 tcp_sock 对象分配 retval = sock_map_fd(sock, flags &amp;amp; (O_CLOEXEC | O_NONBLOCK)); // 涉及 dentry 和 file 申请 // ... }</code></pre> <h2>socket 对象</h2> <p>先看 socket 对象分配:</p> <pre><code class="language-c">int __sock_create(struct net *net, int family, int type, int protocol, struct socket **res, int kern) { sock = sock_alloc(); // 在 sock_inode_cachep(名称为 sock_inode_cache)中分配 struct socket_alloc err = pf-&amp;gt;create(net, sock, protocol, kern); // 分配 tcp_sock 对象 // ... }</code></pre> <p>对于 sock_alloc 分析如下:</p> <pre><code class="language-c">// file: net/socket.c struct socket_alloc { struct socket socket; struct inode vfs_inode; }; static int init_inodecache(void) { sock_inode_cachep = kmem_cache_create(&amp;quot;sock_inode_cache&amp;quot;, sizeof(struct socket_alloc), 0, (SLAB_HWCACHE_ALIGN | SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD), init_once); // 创建专用的 keme_cache,对象大小为 sizeof(struct socket_alloc) if (sock_inode_cachep == NULL) return -ENOMEM; return 0; } static const struct super_operations sockfs_ops = { .alloc_inode = sock_alloc_inode, // 创建 socket 对象时会用到 .destroy_inode = sock_destroy_inode, .statfs = simple_statfs, }; static struct dentry *sockfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return mount_pseudo(fs_type, &amp;quot;socket:&amp;quot;, &amp;amp;sockfs_ops, &amp;amp;sockfs_dentry_operations, SOCKFS_MAGIC); }</code></pre> <p>申请 struct socket 内核的调用树:</p> <pre><code class="language-c">sock_alloc new_inode_pseudo alloc_inode sb-&amp;gt;s_op-&amp;gt;alloc_inode // 即 sock_alloc_inode</code></pre> <p>继续:</p> <pre><code class="language-c">// file: net/socket.c static struct inode *sock_alloc_inode(struct super_block *sb) { struct socket_alloc *ei; struct socket_wq *wq; ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL); // socket_alloc 对象 if (!ei) return NULL; wq = kmalloc(sizeof(*wq), GFP_KERNEL); // 用来记录在 socket 上等待事件的等待项。据说很小,可忽略 if (!wq) { kmem_cache_free(sock_inode_cachep, ei); return NULL; } init_waitqueue_head(&amp;amp;wq-&amp;gt;wait); wq-&amp;gt;fasync_list = NULL; RCU_INIT_POINTER(ei-&amp;gt;socket.wq, wq); ei-&amp;gt;socket.state = SS_UNCONNECTED; ei-&amp;gt;socket.flags = 0; ei-&amp;gt;socket.ops = NULL; ei-&amp;gt;socket.sk = NULL; ei-&amp;gt;socket.file = NULL; return &amp;amp;ei-&amp;gt;vfs_inode; }</code></pre> <h2>tcp_sock 对象</h2> <p>再看 sk 分配。上文的 create 对于 IPV4 而言是 inet_create:</p> <pre><code class="language-c">// file: net/ipv4/af_inet.c static int inet_create(struct net *net, struct socket *sock, int protocol, int kern) { sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot); // 实际申请的是 struct tcp_sock,只是结构层层包含,但第一个字段就是 sk // ... } // file: net/core/sock.c /** * sk_alloc - All socket objects are allocated here * @net: the applicable net namespace * @family: protocol family * @priority: for allocation (%GFP_KERNEL, %GFP_ATOMIC, etc) * @prot: struct proto associated with this new sock instance */ struct sock *sk_alloc(struct net *net, int family, gfp_t priority, struct proto *prot) { struct sock *sk; sk = sk_prot_alloc(prot, priority | __GFP_ZERO, family); // 继续 if (sk) { sk-&amp;gt;sk_family = family; /* * See comment in struct sock definition to understand * why we need sk_prot_creator -acme */ sk-&amp;gt;sk_prot = sk-&amp;gt;sk_prot_creator = prot; sock_lock_init(sk); sock_net_set(sk, get_net(net)); atomic_set(&amp;amp;sk-&amp;gt;sk_wmem_alloc, 1); sock_update_classid(sk); sock_update_netprioidx(sk); } return sk; } EXPORT_SYMBOL(sk_alloc); static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority, int family) { slab = prot-&amp;gt;slab; if (slab != NULL) { sk = kmem_cache_alloc(slab, priority &amp;amp; ~__GFP_ZERO); // 在 tcp_prot-&amp;gt;slab 中申请对象,对象为 struct tcp_sock // ... } // ... }</code></pre> <p>申请的对象是 <code>struct tcp_sock</code>,其包含关系如下:</p> <p><img src="https://www.showdoc.com.cn/server/api/attachment/visitFile?sign=05e5d14db2826024b90827e8ec4a638c&amp;amp;file=file.png" alt="" /></p> <p>那么 tcp_prot-&gt;slab 是怎么初始化的?</p> <pre><code class="language-c">// file: net/ipv4/af_inet.c static int __init inet_init(void) { rc = proto_register(&amp;amp;tcp_prot, 1); rc = proto_register(&amp;amp;udp_prot, 1); // ... } // file: net/core/sock.c int proto_register(struct proto *prot, int alloc_slab) { if (alloc_slab) { prot-&amp;gt;slab = kmem_cache_create(prot-&amp;gt;name, prot-&amp;gt;obj_size, 0, SLAB_HWCACHE_ALIGN | prot-&amp;gt;slab_flags, NULL); // prot-&amp;gt;name = &amp;quot;TCP&amp;quot; // ... } // ... } // file: net/ipv4/tcp_ipv4.c struct proto tcp_prot = { .name = &amp;quot;TCP&amp;quot;, .owner = THIS_MODULE, .obj_size = sizeof(struct tcp_sock), .slab_flags = SLAB_DESTROY_BY_RCU, // ... }</code></pre> <h2>dentry 对象</h2> <p>再看 dentry 申请:</p> <pre><code class="language-c">// file: include/linux/dcache.h struct dentry { struct dentry *d_parent; /* parent directory */ struct qstr d_name; struct inode *d_inode; /* Where the name belongs to - NULL is * negative */ unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */ // ... };</code></pre> <p>内存初始化:</p> <pre><code class="language-c">// file: fs/dcache.c static void __init dcache_init(void) { dentry_cache = KMEM_CACHE(dentry, SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_MEM_SPREAD); // 名称就是 dentry,大小就是 sizeof(struct dentry) // ... } // file: include/linux/slab.h #define KMEM_CACHE(__struct, __flags) kmem_cache_create(#__struct,\ sizeof(struct __struct), __alignof__(struct __struct),\ (__flags), NULL)</code></pre> <p>继续看 <code>sock_map_fd</code>:</p> <pre><code class="language-c">// file: net/socket.c static int sock_map_fd(struct socket *sock, int flags) { struct file *newfile; int fd = get_unused_fd_flags(flags); if (unlikely(fd &amp;lt; 0)) return fd; newfile = sock_alloc_file(sock, flags, NULL); // 申请 dentry、file 内核对象 if (likely(!IS_ERR(newfile))) { fd_install(fd, newfile); return fd; } put_unused_fd(fd); return PTR_ERR(newfile); } struct file *sock_alloc_file(struct socket *sock, int flags, const char *dname) { path.dentry = d_alloc_pseudo(sock_mnt-&amp;gt;mnt_sb, &amp;amp;name); file = alloc_file(&amp;amp;path, FMODE_READ | FMODE_WRITE, &amp;amp;socket_file_ops); // ... } // file: fs/dcache.c struct dentry *d_alloc_pseudo(struct super_block *sb, const struct qstr *name) { struct dentry *dentry = __d_alloc(sb, name); if (dentry) dentry-&amp;gt;d_flags |= DCACHE_DISCONNECTED; return dentry; } EXPORT_SYMBOL(d_alloc_pseudo); struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name) { struct dentry *dentry; char *dname; dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL); // dentry_cache 就是上文申请的 kmem_cache,其中对象为 struct dentry // ... }</code></pre> <h2>file 对象</h2> <p>再看 filp 对象申请:</p> <pre><code class="language-c">// file: fs/file_table.c struct file *alloc_file(struct path *path, fmode_t mode, const struct file_operations *fop) { struct file *file; file = get_empty_filp(); // ... } struct file *get_empty_filp(void) { f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL); // 在 filp_cachep 上申请一个 struct file 对象 }</code></pre> <p>那么 filp_cachep 是在哪里初始化的?</p> <pre><code class="language-c">// file: fs/file_table.c void __init files_init(unsigned long mempages) { unsigned long n; filp_cachep = kmem_cache_create(&amp;quot;filp&amp;quot;, sizeof(struct file), 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); // ... }</code></pre>

页面列表

ITEM_HTML