Linux网络连接内核

Linux网络连接内核

1. 连接是一个数据结构

连接的本质是内核中的一种数据结构(即结构体)。Linux 系统内核,会用 C 语言设计多态结构,其中套接字编程就是如此设计的。

我们使用的 socket() 函数,在内核中有一个 struct socket 结构体管理连接的基本信息,其中 short type 字段,就是决定创建的是UDP还是TCP的。而 struct sock *sk 就是网络 socket 的接口,它所指向的就是UDP或TCP的结构体。

c 复制代码
struct socket {
    socket_state          state;     /* 当前 socket 的状态 */
    short                 type;      /* socket 类型(SOCK_STREAM, SOCK_DGRAM 等) */
    unsigned long         flags;     /* socket 标志 */
    struct file           *file;     /* 与 socket 相关的文件描述符 */
    struct sock           *sk;       /* 指向内核中的 sock 结构体 */
    const struct proto_ops *ops;     /* socket 操作集合 */
};

在UDP和TCP的结构体中,它们都是由多种套接字结构体嵌套组成的,这种俄罗斯套娃式的结构体,就是c语言风格的多态结构。

以TCP为例,TCP的结构体是 struct tcp_sock,它的最上边是 struct sockstruct sock又在 struct inet_sock 之中,struct inet_sock 又在 struct inet_connection_sock 之中。C 语言使用多态的方式,是使用一个指针类型,想要访问 TCP 里的 struct sock 就把指针强转成 struct *sock 类型,访问 struct inet_sock 就把指针强转成 struct *inet_sock 类型,以此类推。

struct sock:

c 复制代码
struct sock {
    /* 同步控制相关 */
    spinlock_t            sk_lock;            /* 用于保护此 sock 的自旋锁 */
    atomic_t              sk_refcnt;          /* 引用计数 */

    /* 协议层状态 */
    struct sock_common    __sk_common;
    struct proto         *sk_prot;            /* 指向协议相关的函数操作 */
    void                 *sk_user_data;       /* 用户数据 */
    
    /* 发送和接收缓冲区 */
    struct sk_buff_head    sk_receive_queue;   /* 接收队列 */
    struct sk_buff_head    sk_write_queue;     /* 发送队列 */

    /* 协议相关的状态变量 */
    unsigned long          sk_flags;           /* socket 标志 */
    unsigned int           sk_rcvbuf;          /* 接收缓冲区大小 */
    unsigned int           sk_sndbuf;          /* 发送缓冲区大小 */
    
    /* ... 还有很多其他字段 */
};

struct tcp_sock:

c 复制代码
struct tcp_sock {
    struct inet_sock       inet;             /* IPv4 socket */
    u32                    rcv_nxt;          /* 下一个要接收的序列号 */
    u32                    snd_nxt;          /* 下一个要发送的序列号 */
    u32                    snd_una;          /* 已确认的第一个字节 */
    u32                    snd_wnd;          /* 发送窗口 */
    u32                    rcv_wnd;          /* 接收窗口 */

    u32                    srtt;             /* 平滑的往返时间 */
    u32                    mdev;             /* 往返时间的均方差 */
    
    u32                    retransmit_high;  /* 重传的最高序列号 */
    
    /* 拥塞控制相关 */
    u32                    snd_cwnd;         /* 拥塞窗口 */
    u32                    snd_ssthresh;     /* 慢启动阈值 */

    /* ... 其他和 TCP 协议相关的字段 */
};

struct udp_sock:

c 复制代码
struct udp_sock {
    struct inet_sock       inet;            /* IPv4 协议相关的 socket */

    __u16                  len;             /* UDP 数据长度 */
    __u16                  pending;         /* 未决的数据包状态 */

    /* 用于UDP-Lite传输的校验和,普通UDP协议不使用 */
    __u8                   encap_type;      /* 封装类型 */
    __u8                   encap_enabled;   /* 是否启用了封装 */
    __u8                   no_check6_tx;    /* 不校验传出数据 */
    __u8                   no_check6_rx;    /* 不校验传入数据 */

    int                    (*encap_rcv)(struct sock *sk, struct sk_buff *skb);
    int                    (*encap_err_rcv)(struct sock *sk, struct sk_buff *skb,
                                            struct iphdr *iph, u32 info);

    /* UDP-Lite特有的字段(在标准 UDP 协议下不使用) */
    __u16                  pcslen;          /* 部分校验和长度 */
    __u16                  pcflag;          /* 部分校验标志 */

    /* ... 其他字段 */
};

2. 一切皆文件与套接字

Linux 具有一切皆文件的特点,在套接字编程中,Linux 能够将 sockfd 当成普通的文件描述符使用、关闭,但在 Windows 环境下,必须为 sockfd 额外写一个关闭文件描述符的函数。那么,Linux 是如何做到将连接当成文件看待的呢?

根据文件操作,进程的PCB结构体 task_struct 中有一个 *file 指针,指向 struct file_struct 结构体,这个结构体中,有一个 struct file *fd_array[] 记录着所有打开的文件描述符 fd ,它的元素指向对应打开的文件,也是由一个 struct file 结构体管理的,其中的 void *private_data 在套接字编程中,就是指向 struct socket 套接字结构体,这一串的联系,使得套接字能够像文件一样被管理,也是 Linux 具有一切皆文件的特点的原因。

相关推荐
zhoupenghui16815 分钟前
golang时间相关函数总结
服务器·前端·golang·time
mit6.82416 分钟前
[实现Rpc] 通信-Muduo库的实现 | && 完美转发 | reserve | unique_lock
c++·网络协议·rpc
lllsure17 分钟前
Linux 实用指令
linux·物联网
努力的小T38 分钟前
使用 Docker 部署 Apache Spark 集群教程
linux·运维·服务器·docker·容器·spark·云计算
IsToRestart44 分钟前
什么是RPC,和HTTP有什么区别?
网络协议·http·rpc
okok__TXF1 小时前
Rpc导读
网络·网络协议·rpc
Nerd Nirvana1 小时前
OpenSSL crt & key (生成一套用于TLS双向认证的证书密钥)
linux·ssl·shell·认证·加密·tls·oepnssl
不修×蝙蝠1 小时前
HTTP 协议(Ⅲ)
服务器·http·javaee·http协议
&向上2 小时前
RK3588配置成为路由器
网络·智能路由器·rk3588
letisgo52 小时前
记录一次部署PC端网址全过程
linux·阿里云·服务器运维