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 具有一切皆文件的特点的原因。

相关推荐
Antonio9151 小时前
【Linux】 Linux 进程控制
linux·运维·服务器
秋水丶秋水1 小时前
IP证书与 域名证书有什么区别?
网络·网络协议·tcp/ip
thinkMoreAndDoMore1 小时前
linux驱动开发(1)-内核模块
linux·运维·驱动开发
darin_ฅ( ̳• ◡ • ̳)ฅ11 小时前
Linux环境-通过命令查看zookeeper注册的服务
linux·zookeeper
不想头秃a2 小时前
JavaEE初阶-网络编程
java·运维·服务器·网络
一ge科研小菜鸡2 小时前
云原生 DevOps 实践路线:构建敏捷、高效、可观测的交付体系
运维·云原生·devops
哈哈哈哈哈哈哈哈哈...........2 小时前
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
tcp/ip·nginx·lua
Chuncheng's blog2 小时前
CentOS 7 如何pip3安装pyaudio?
linux·运维·python·centos
西阳未落2 小时前
Linux(12)——基础IO(下)
linux·运维·服务器
什么半岛铁盒2 小时前
云服务器Xshell登录拒绝访问排查
运维·服务器