Linux网络解读(2) - 套接字的初始化

简介: 套接字的初始化

socket的创建

int socket(int domain, int type, int protocol);

socket返回了一个fd的值。

当在用户态调用socket创建套接字时发生了什么呢?

fd和socket之间的关系

image.png

socket结构体

struct socket {
    socket_state state;
    unsigned long flags;
    const struct proto_ops *ops;
    struct fasync_struct *fasync_list;
    struct file *file;
    struct sock *sk;
    wait_queue_head_t wait;
    short type;
};

sys_socket

socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

主要看一下创建一个套接字过程中都分配了哪些资源。

// net/socket.c
asmlinkage long sys_socket(int family, int type, int protocol)
{
        int retval;
        struct socket *sock;
        retval = sock_create(family, type, protocol, &sock);
        if (retval < 0)
                goto out;
        retval = sock_map_fd(sock);
}

可以看出,创建一个套接字需要从两方面入手:

1) 根据family, type, protocol分配socket资源

2) 把socket和VFS关联起来。

先看如何分配socket相关的资源。

1) 分配套接字资源sock_create

分配的过程是:从上层开始,分配上层对应的数据结构,然后根据参数设置函数。然后再分配下层对应的数据结构。
static int __sock_create(int family, int type, int protocol, struct socket **res, int kern)
{
    sock = sock_alloc();
    sock->type  = type;
    net_families[family]->create(sock, protocol)    
}

1) sock = sock_alloc();

分配最上层的代表:socket结构体。

这个结构体和具体的协议无关。

有了这个结构体就可以设置相关的协议选项和buffer了。

另外,这个sock_alloc()同时分配了file的结构体,在内存布局上是相邻的。

image.png

2) net_families[family]->create(sock, protocol)

net_families[family]通过套接字domain的值family,选择出一个协议族,然后调用其create函数,同时把socket的结构体和下层的参数protocol传进入。

这里的family为AF_INET,根据net_families的初始化,可以知道net_families[family]->create()对应inet_create()函数。

inet层的构造:inet_create

// net/ipv4/af_inet.c
static int inet_create(struct socket *sock, int protocol)
{
        sock->state = SS_UNCONNECTED;
        // 根据type找到inetsw,这里的type是SOCK_RAW
        list_for_each_rcu(p, &inetsw[sock->type]) {
                answer = list_entry(p, struct inet_protosw, list);
                /* Check the non-wild match. */
                if (protocol == answer->protocol) {
                        if (protocol != IPPROTO_IP)
                                break;
                } else {
                        /* Check for the two wild cases. */
                        if (IPPROTO_IP == protocol) {
                                protocol = answer->protocol;
                                break;
                        }
                        if (IPPROTO_IP == answer->protocol)
                                break;
                }
                answer = NULL;
        }
        // 通过上面的循环,此处的answer是raw_proto,把协议的ops设置给socket层
        sock->ops = answer->ops;
        answer_prot = answer->prot;
        answer_no_check = answer->no_check;
        answer_flags = answer->flags;
        // 分配sock结构体
        sk = sk_alloc(PF_INET, GFP_KERNEL,
                      answer_prot->slab_obj_size,
                      answer_prot->slab);
       // 把协议的结构体设置给sock                      
       sk->sk_prot = answer_prot;
       // 把sock结构体转换成inet_sock,因为此时的上下文正是inet的create方法
       inet = inet_sk(sk);
       // 如果是协议是raw,则把protocol赋值给inet->num,这个值本身应该是端口的数值,但raw没有端口。
       if (SOCK_RAW == sock->type) {
                inet->num = protocol;
                if (IPPROTO_RAW == protocol)
                        inet->hdrincl = 1;
        }
        // 初始化socket,sock结构体
        // 初始化sock结构体中的receive,write,error queue
        // 初始化sock结构体中的revbuf,sendbuf大小
        // 初始化sock中其他的回调:sk_state_change(), sk_data_ready()等
        sock_init_data(sock, sk);
        // 把sock结构体添加到raw协议中的hash中?
        if (inet->num) {
                inet->sport = htons(inet->num);
                /* Add to protocol hash chains. */
                sk->sk_prot->hash(sk);
        }
}

sock结构体是用来管理具体协议的结构体,协议不同这个结构体的意义也不同,sock在不同协议的数据块:

image.png

在sk_alloc中会根据具体的协议proto的obj_size大小分配一片内存:

struct sock *sk_alloc(int family, gfp_t priority,
              struct proto *prot, int zero_it)
{
    struct sock *sk = NULL;
    kmem_cache_t *slab = prot->slab;
    if (slab != NULL)
        sk = kmem_cache_alloc(slab, priority);
    else
               // 根据协议proto的obj_size分配一片内存!!!proto是在inet_init中初始化的
        sk = kmalloc(prot->obj_size, priority);
    if (sk) {
        if (zero_it) {
            memset(sk, 0, prot->obj_size);
            sk->sk_family = family;
            /*
             * See comment in struct sock definition to understand
             * why we need sk_prot_creator -acme
             */
            sk->sk_prot = sk->sk_prot_creator = prot;
            sock_lock_init(sk);
        }
        if (security_sk_alloc(sk, family, priority))
            goto out_free;
        if (!try_module_get(prot->owner))
            goto out_free;
    }
    return sk;
out_free:
    if (slab != NULL)
        kmem_cache_free(slab, sk);
    else
        kfree(sk);
    return NULL;
}

2) 把socket和VFS关联起来:sock_map_fd()

int sock_map_fd(struct socket        *sock)
{
    fd = get_unused_fd();
    struct file        *file = get_empty_filp();
    file->f_dentry = d_alloc(sock_mnt->mnt_sb->s_root, &this);
    // 设置d_op
    file->f_dentry->d_op = &sockfs_dentry_operations;
    d_add(file->f_dentry, SOCK_INODE(sock));
    file->f_vfsmnt             = mntget(sock_mnt);
    file->f_mapping             = file->f_dentry->d_inode->i_mapping;
    // 设置socket结构体中file的指针,关联起socket和file
    sock->file    = file;
    // 设置f_op
    file->f_op    = SOCK_INODE(sock)->i_fop = &socket_file_ops;
    file->f_mode  = FMODE_READ | FMODE_WRITE;
    file->f_flags = O_RDWR;
    file->f_pos   = 0;
    fd_install(fd, file);
}

fd,socket,sock之间的关系:

image.png

相关文章
|
5月前
|
安全 Linux 网络安全
Nipper 3.9.0 for Windows & Linux - 网络设备漏洞评估
Nipper 3.9.0 for Windows & Linux - 网络设备漏洞评估
162 0
Nipper 3.9.0 for Windows & Linux - 网络设备漏洞评估
|
6月前
|
运维 Linux 开发者
Linux系统中使用Python的ping3库进行网络连通性测试
以上步骤展示了如何利用 Python 的 `ping3` 库来检测网络连通性,并且提供了基本错误处理方法以确保程序能够优雅地处理各种意外情形。通过简洁明快、易读易懂、实操性强等特点使得该方法非常适合开发者或系统管理员快速集成至自动化工具链之内进行日常运维任务之需求满足。
419 18
|
6月前
|
网络协议 关系型数据库 Linux
【App Service Linux】在Linux App Service中安装 tcpdump 并抓取网络包
在App Service for Linux环境中,无法像Windows一样直接使用网络排查工具抓包。本文介绍了如何通过TCPDUMP在Linux环境下抓取网络包,包括SSH进入容器、安装tcpdump、执行抓包命令及下载分析文件的完整操作步骤。
316 5
|
7月前
|
Web App开发 网络协议 Linux
【Linux】网络基础
TCP/IP五层模型是网络通信的基础框架,将复杂的数据传输过程分为物理层、数据链路层、网络层、传输层和应用层,每层各司其职,协同完成远程通信。该模型确保了不同设备和网络之间的互联互通,是现代互联网运行的核心机制。
590 5
|
7月前
|
网络协议 Linux 开发者
深入Linux中UDP网络通信机制编程探索
以上步骤概述了Linux中UDP网络通信的编程机制。在实现时,因关注细节和上下文环境可能有所调整,但大致流程是一致的。这些知识片段旨在帮助开发者快速上手Linux下的UDP编程,并提供可靠的信息作为编程的基础。在编程实践中,应结合实际业务需求,设计合适的数据传输协议,确保数据的正确性和实时性。
186 0
|
9月前
|
安全 网络协议 Linux
Linux网络应用层协议展示:HTTP与HTTPS
此外,必须注意,从HTTP迁移到HTTPS是一项重要且必要的任务,因为这不仅关乎用户信息的安全,也有利于你的网站评级和粉丝的信心。在网络世界中,信息的安全就是一切,选择HTTPS,让您的网站更加安全,使您的用户满意,也使您感到满意。
261 18
|
9月前
|
Linux 数据安全/隐私保护
使用Linux命令行接入无线网络Wi-Fi的示例。
现在,你已经使用命令行成功地连接到 Wi-Fi 网络了。这两个示例涵盖了用 `nmcli` 和 `wpa_supplicant` 连接无线网络的常见场景,让你能够不依赖图形化界面来完成这个任务。在日常使用中熟练掌握这些基本操作能增强你对 Linux 系统的理解,帮助你更有效地处理各种问题。
756 12
|
9月前
|
安全 Ubuntu Linux
Nipper 3.8.0 for Windows & Linux - 网络设备漏洞评估
Nipper 3.8.0 for Windows & Linux - 网络设备漏洞评估
351 0
Nipper 3.8.0 for Windows & Linux - 网络设备漏洞评估
|
5月前
|
Linux 应用服务中间件 Shell
二、Linux文本处理与文件操作核心命令
熟悉了Linux的基本“行走”后,就该拿起真正的“工具”干活了。用grep这个“放大镜”在文件里搜索内容,用find这个“探测器”在系统中寻找文件,再用tar把东西打包带走。最关键的是要学会使用管道符|,它像一条流水线,能把这些命令串联起来,让简单工具组合出强大的功能,比如 ps -ef | grep 'nginx' 就能快速找出nginx进程。
627 1
二、Linux文本处理与文件操作核心命令