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

相关文章
|
17天前
|
安全 Linux 虚拟化
网络名称空间在Linux虚拟化技术中的位置
网络名称空间(Network Namespaces)是Linux内核特性之一,提供了隔离网络环境的能力,使得每个网络名称空间都拥有独立的网络设备、IP地址、路由表、端口号范围以及iptables规则等。这一特性在Linux虚拟化技术中占据了核心位置🌟,它不仅为构建轻量级虚拟化解决方案(如容器📦)提供了基础支持,也在传统的虚拟机技术中发挥作用,实现资源隔离和网络虚拟化。
网络名称空间在Linux虚拟化技术中的位置
|
17天前
|
网络协议 安全 Linux
Linux网络名称空间之独立网络资源管理
Linux网络名称空间是一种强大的虚拟化技术🛠️,它允许用户创建隔离的网络环境🌐,每个环境拥有独立的网络资源和配置。这项技术对于云计算☁️、容器化应用📦和网络安全🔒等领域至关重要。本文将详细介绍在Linux网络名称空间中可以拥有的独立网络资源,并指出应用开发人员在使用时应注意的重点。
|
17天前
|
安全 网络协议 Linux
Linux网络名称空间概述
Linux网络名称空间是操作系统级别的一种虚拟化技术🔄,它允许创建隔离的网络环境🌐,使得每个环境拥有自己独立的网络资源,如IP地址📍、路由表🗺️、防火墙规则🔥等。这种技术是Linux内核功能的一部分,为不同的用户空间进程提供了一种创建和使用独立网络协议栈的方式。本文旨在全方面、多维度解释Linux网络名称空间的概念、必要性和作用。
Linux网络名称空间概述
|
15天前
|
存储 算法 Linux
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
39 6
|
5天前
|
机器学习/深度学习 缓存 监控
linux查看CPU、内存、网络、磁盘IO命令
`Linux`系统中,使用`top`命令查看CPU状态,要查看CPU详细信息,可利用`cat /proc/cpuinfo`相关命令。`free`命令用于查看内存使用情况。网络相关命令包括`ifconfig`(查看网卡状态)、`ifdown/ifup`(禁用/启用网卡)、`netstat`(列出网络连接,如`-tuln`组合)以及`nslookup`、`ping`、`telnet`、`traceroute`等。磁盘IO方面,`iostat`(如`-k -p ALL`)显示磁盘IO统计,`iotop`(如`-o -d 1`)则用于查看磁盘IO瓶颈。
|
17天前
|
网络协议 Linux
在Linux中,管理和配置网络接口
在Linux中管理网络接口涉及多个命令,如`ifconfig`(在新版本中被`ip`取代)、`ip`(用于网络设备配置)、`nmcli`(NetworkManager的CLI工具)、`nmtui`(文本界面配置)、`route/ip route`(处理路由表)、`netstat/ss`(显示网络状态)和`hostnamectl/systemctl`(主机名和服务管理)。这些命令帮助用户启动接口、设置IP地址、查看连接和路由信息。不同发行版可能有差异,建议参考相应文档。
19 4
|
3天前
|
网络协议 Linux Shell
【linux网络(一)】初识网络, 理解四层网络模型
【linux网络(一)】初识网络, 理解四层网络模型
|
3天前
|
安全 Ubuntu Linux
Linux 网络操作命令Telnet
Linux 网络操作命令Telnet
16 0
Linux 网络操作命令Telnet
|
3天前
|
Ubuntu Linux
Linux(22) Linux设置网络优先级顺序
Linux(22) Linux设置网络优先级顺序
6 0
|
4天前
|
Ubuntu 网络协议 Linux
Linux(20) Ubuntu 20.04 网络接口自动切换路由配置
Linux(20) Ubuntu 20.04 网络接口自动切换路由配置
27 0