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

相关文章
|
4天前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
16 2
|
16天前
|
域名解析 网络协议 安全
|
22天前
|
运维 监控 网络协议
|
17天前
|
存储 Ubuntu Linux
2024全网最全面及最新且最为详细的网络安全技巧 (三) 之 linux提权各类技巧 上集
在本节实验中,我们学习了 Linux 系统登录认证的过程,文件的意义,并通过做实验的方式对 Linux 系统 passwd 文件提权方法有了深入的理解。祝你在接下来的技巧课程中学习愉快,学有所获~和文件是 Linux 系统登录认证的关键文件,如果系统运维人员对shadow或shadow文件的内容或权限配置有误,则可以被利用来进行系统提权。上一章中,我们已经学习了文件的提权方法, 在本章节中,我们将学习如何利用来完成系统提权。在本节实验中,我们学习了。
|
23天前
|
Java
[Java]Socket套接字(网络编程入门)
本文介绍了基于Java Socket实现的一对一和多对多聊天模式。一对一模式通过Server和Client类实现简单的消息收发;多对多模式则通过Server类维护客户端集合,并使用多线程实现实时消息广播。文章旨在帮助读者理解Socket的基本原理和应用。
19 1
|
26天前
|
Ubuntu Linux 虚拟化
Linux虚拟机网络配置
【10月更文挑战第25天】在 Linux 虚拟机中,网络配置是实现虚拟机与外部网络通信的关键步骤。本文介绍了四种常见的网络配置方式:桥接模式、NAT 模式、仅主机模式和自定义网络模式,每种模式都详细说明了其原理和配置步骤。通过这些配置,用户可以根据实际需求选择合适的网络模式,确保虚拟机能够顺利地进行网络通信。
|
1月前
|
网络协议 安全 Ubuntu
Linux中网络连接问题
【10月更文挑战第3天】
32 1
|
1月前
|
监控 Linux 测试技术
Linux系统命令与网络,磁盘和日志监控总结
Linux系统命令与网络,磁盘和日志监控总结
55 0
|
1月前
|
监控 Linux 测试技术
Linux系统命令与网络,磁盘和日志监控三
Linux系统命令与网络,磁盘和日志监控三
38 0
|
Linux
Linux协议栈(6)——初始化及链路层实现
这篇主要学习链路层在内核协议栈的实现,包括初始化、注册以及接收发送,会涉及相关函数和代码所在位置。 我们知道以太网不仅可以传输IP分组,还可以传输其他协议的分组,接收系统必须能够区分不同的协议类型,以便将数据转发到正确的例程进一步处理。
2881 0