成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Linux網(wǎng)絡(luò)數(shù)據(jù)包接收:原理、流程與優(yōu)化策略

系統(tǒng) Linux
在 Linux 系統(tǒng)中,Socket 是應(yīng)用程序與網(wǎng)絡(luò)協(xié)議棧進(jìn)行交互的重要接口。它為應(yīng)用程序提供了一種統(tǒng)一的方式來發(fā)送和接收網(wǎng)絡(luò)數(shù)據(jù),隱藏了底層網(wǎng)絡(luò)協(xié)議的復(fù)雜性。

在當(dāng)今數(shù)字化時(shí)代,網(wǎng)絡(luò)已成為計(jì)算機(jī)系統(tǒng)不可或缺的部分。無論是日常的網(wǎng)頁瀏覽、文件傳輸,還是大規(guī)模數(shù)據(jù)中心的高效通信,網(wǎng)絡(luò)數(shù)據(jù)包的收發(fā)都在其中扮演著重要角色。對于 Linux 系統(tǒng)而言,深入理解網(wǎng)絡(luò)數(shù)據(jù)包的接收過程,是優(yōu)化網(wǎng)絡(luò)性能、解決網(wǎng)絡(luò)問題的關(guān)鍵。

想象一下,你正在進(jìn)行在線視頻會(huì)議,突然畫面卡頓、聲音中斷;或者在進(jìn)行大規(guī)模數(shù)據(jù)傳輸時(shí),速度遠(yuǎn)遠(yuǎn)低于預(yù)期。這些令人困擾的網(wǎng)絡(luò)問題,很多都與 Linux 系統(tǒng)對網(wǎng)絡(luò)數(shù)據(jù)包的接收處理過程緊密相關(guān)。而這一過程,涉及到從硬件層面的網(wǎng)卡,到軟件層面的內(nèi)核協(xié)議棧、中斷處理機(jī)制等多個(gè)復(fù)雜環(huán)節(jié)。

通過深入剖析 Linux 下網(wǎng)絡(luò)數(shù)據(jù)包的接收過程,我們不僅能夠洞悉網(wǎng)絡(luò)數(shù)據(jù)在系統(tǒng)內(nèi)部的流轉(zhuǎn)路徑,還能明確各環(huán)節(jié)對網(wǎng)絡(luò)性能的影響。這對于開發(fā)者、系統(tǒng)管理員來說,無疑是優(yōu)化網(wǎng)絡(luò)性能、解決網(wǎng)絡(luò)故障的有力武器。它可以幫助我們在面對網(wǎng)絡(luò)問題時(shí),迅速定位問題根源,采取有效的解決措施,如調(diào)整網(wǎng)絡(luò)配置、優(yōu)化內(nèi)核參數(shù)等,從而保障網(wǎng)絡(luò)的穩(wěn)定高效運(yùn)行。

一、引言

這里深度理解一下在Linux下網(wǎng)絡(luò)包的接收過程,為了簡單起見,我們用udp來舉例,如下:

int main(){
    int serverSocketFd = socket(AF_INET, SOCK_DGRAM, 0);
    bind(serverSocketFd, ...);

    char buff[BUFFSIZE];
    int readCount = recvfrom(serverSocketFd, buff, BUFFSIZE, 0, ...);
    buff[readCount] = '\0';
    printf("Receive from client:%s\n", buff);

}

上面代碼是一段udp server接收收據(jù)的邏輯。只要客戶端有對應(yīng)的數(shù)據(jù)發(fā)送過來,服務(wù)器端執(zhí)行recv_from后就能收到它,并把它打印出來。那么當(dāng)網(wǎng)絡(luò)包達(dá)到網(wǎng)卡,直到recvfrom收到數(shù)據(jù),這中間究竟都發(fā)生過什么?

二、Linux網(wǎng)絡(luò)基礎(chǔ)

2.1 網(wǎng)絡(luò)協(xié)議棧

在Linux內(nèi)核實(shí)現(xiàn)中,鏈路層協(xié)議靠網(wǎng)卡驅(qū)動(dòng)來實(shí)現(xiàn),內(nèi)核協(xié)議棧來實(shí)現(xiàn)網(wǎng)絡(luò)層和傳輸層。內(nèi)核對更上層的應(yīng)用層提供socket接口來供用戶進(jìn)程訪問。我們用Linux的視角來看到的TCP/IP網(wǎng)絡(luò)分層模型應(yīng)該是下面這個(gè)樣子的。

圖片圖片

鏈路層,作為網(wǎng)絡(luò)協(xié)議棧的最底層,主要負(fù)責(zé)將網(wǎng)絡(luò)層傳來的數(shù)據(jù)轉(zhuǎn)換為物理信號,通過物理介質(zhì)進(jìn)行傳輸,并處理物理介質(zhì)上的數(shù)據(jù)接收。在以太網(wǎng)環(huán)境中,鏈路層使用以太網(wǎng)協(xié)議,它定義了數(shù)據(jù)幀的格式,包括源 MAC 地址、目的 MAC 地址、類型字段以及數(shù)據(jù)部分等。

例如,當(dāng)你的計(jì)算機(jī)向同一局域網(wǎng)內(nèi)的另一臺計(jì)算機(jī)發(fā)送數(shù)據(jù)時(shí),鏈路層會(huì)在數(shù)據(jù)幀中填入對方的 MAC 地址,以便數(shù)據(jù)能夠準(zhǔn)確地發(fā)送到目標(biāo)設(shè)備。ARP(地址解析協(xié)議)也工作在這一層,它用于將 IP 地址解析為對應(yīng)的 MAC 地址。當(dāng)主機(jī)需要向某個(gè) IP 地址發(fā)送數(shù)據(jù)時(shí),如果不知道該 IP 地址對應(yīng)的 MAC 地址,就會(huì)發(fā)送 ARP 請求廣播,目標(biāo)主機(jī)收到后會(huì)回復(fù)其 MAC 地址,從而建立起 IP 地址與 MAC 地址的映射關(guān)系。

網(wǎng)絡(luò)層,其主要職責(zé)是實(shí)現(xiàn)數(shù)據(jù)包的路由和轉(zhuǎn)發(fā),使數(shù)據(jù)能夠在不同的網(wǎng)絡(luò)之間傳輸。IP 協(xié)議是網(wǎng)絡(luò)層的核心協(xié)議,它為每個(gè)網(wǎng)絡(luò)設(shè)備分配唯一的 IP 地址,并負(fù)責(zé)將數(shù)據(jù)包從源 IP 地址發(fā)送到目的 IP 地址。IP 協(xié)議還支持?jǐn)?shù)據(jù)包的分片與重組功能,當(dāng)數(shù)據(jù)包的大小超過鏈路層的最大傳輸單元(MTU)時(shí),IP 協(xié)議會(huì)將數(shù)據(jù)包進(jìn)行分片,在到達(dá)目標(biāo)主機(jī)后再進(jìn)行重組。例如,當(dāng)你訪問外網(wǎng)的網(wǎng)站時(shí),數(shù)據(jù)包會(huì)經(jīng)過多個(gè)路由器的轉(zhuǎn)發(fā),每個(gè)路由器根據(jù) IP 地址來決定數(shù)據(jù)包的下一跳路徑,最終將數(shù)據(jù)包送達(dá)目標(biāo)服務(wù)器。ICMP(互聯(lián)網(wǎng)控制消息協(xié)議)也是網(wǎng)絡(luò)層的重要協(xié)議,它用于在網(wǎng)絡(luò)設(shè)備之間傳遞控制消息,如 ping 命令就是利用 ICMP 協(xié)議來測試網(wǎng)絡(luò)的連通性。

傳輸層,主要負(fù)責(zé)為應(yīng)用層提供端到端的通信服務(wù),確保數(shù)據(jù)的可靠傳輸或高效傳輸。TCP(傳輸控制協(xié)議)和 UDP(用戶數(shù)據(jù)報(bào)協(xié)議)是傳輸層的兩個(gè)主要協(xié)議。TCP 是一種面向連接的、可靠的協(xié)議,它通過三次握手建立連接,在數(shù)據(jù)傳輸過程中使用序列號、確認(rèn)號和重傳機(jī)制來確保數(shù)據(jù)的完整性和順序性。例如,在進(jìn)行文件傳輸時(shí),TCP 協(xié)議能夠保證文件的每個(gè)字節(jié)都準(zhǔn)確無誤地到達(dá)對方。UDP 則是一種無連接的、不可靠的協(xié)議,它不保證數(shù)據(jù)的可靠傳輸,但具有傳輸速度快、開銷小的特點(diǎn),常用于對實(shí)時(shí)性要求較高的應(yīng)用場景,如視頻流、音頻流等。比如,在觀看在線視頻時(shí),UDP 協(xié)議可以快速地將視頻數(shù)據(jù)傳輸給用戶,即使偶爾有數(shù)據(jù)包丟失,也不會(huì)對用戶體驗(yàn)造成太大影響。

這些不同層次的協(xié)議相互協(xié)作,共同完成了網(wǎng)絡(luò)數(shù)據(jù)包的接收與發(fā)送過程。從物理介質(zhì)上接收到的數(shù)據(jù),會(huì)依次經(jīng)過鏈路層、網(wǎng)絡(luò)層和傳輸層的處理,最終被傳遞到應(yīng)用層,供應(yīng)用程序使用。

2.2 中斷機(jī)制

在 Linux 網(wǎng)絡(luò)數(shù)據(jù)處理中,中斷機(jī)制起著至關(guān)重要的作用,它主要包括硬中斷和軟中斷 。

硬中斷,是由硬件設(shè)備產(chǎn)生的,用于通知 CPU 有緊急事件需要處理。當(dāng)網(wǎng)卡接收到網(wǎng)絡(luò)數(shù)據(jù)包時(shí),會(huì)觸發(fā)一個(gè)硬中斷信號,通知 CPU 有新的數(shù)據(jù)到達(dá)。這個(gè)過程就像是門鈴?fù)蝗豁懫穑嵝阎魅擞性L客到來。CPU 在接收到硬中斷信號后,會(huì)立即暫停當(dāng)前正在執(zhí)行的任務(wù),保存現(xiàn)場信息,然后跳轉(zhuǎn)到對應(yīng)的中斷處理程序。對于網(wǎng)卡的硬中斷處理程序,其主要任務(wù)是將接收到的數(shù)據(jù)從網(wǎng)卡的緩沖區(qū)拷貝到內(nèi)存中,并進(jìn)行一些初步的處理,如設(shè)置數(shù)據(jù)的相關(guān)標(biāo)志位等。由于硬中斷處理需要快速響應(yīng),以避免數(shù)據(jù)丟失,因此硬中斷處理程序通常只執(zhí)行一些緊急且耗時(shí)較短的操作。

軟中斷,是一種推后執(zhí)行的機(jī)制,用于處理那些可以稍微延遲處理的任務(wù)。在硬中斷處理完成后,會(huì)觸發(fā)相應(yīng)的軟中斷,將數(shù)據(jù)包的后續(xù)處理工作交給軟中斷來完成。軟中斷的處理函數(shù)通常在一個(gè)特定的上下文中執(zhí)行,這個(gè)上下文允許執(zhí)行一些相對耗時(shí)的操作,而不會(huì)影響硬中斷的及時(shí)響應(yīng)。例如,在網(wǎng)絡(luò)數(shù)據(jù)處理中,軟中斷會(huì)對從網(wǎng)卡拷貝到內(nèi)存中的數(shù)據(jù)包進(jìn)行進(jìn)一步的解析和處理,將其傳遞給相應(yīng)的網(wǎng)絡(luò)協(xié)議棧層進(jìn)行后續(xù)處理。軟中斷的優(yōu)勢在于可以在合適的時(shí)機(jī)批量處理多個(gè)任務(wù),提高系統(tǒng)的整體效率。與硬中斷相比,軟中斷的觸發(fā)不是由硬件直接產(chǎn)生,而是由軟件在特定條件下觸發(fā),并且軟中斷的處理可以在多個(gè) CPU 核心上并行進(jìn)行,從而提高處理速度。

硬中斷和軟中斷在 Linux 網(wǎng)絡(luò)數(shù)據(jù)處理中緊密配合。硬中斷負(fù)責(zé)快速響應(yīng)硬件事件,及時(shí)將數(shù)據(jù)從硬件設(shè)備傳輸?shù)絻?nèi)存;軟中斷則負(fù)責(zé)對數(shù)據(jù)進(jìn)行后續(xù)的詳細(xì)處理,將數(shù)據(jù)傳遞給相應(yīng)的協(xié)議棧層進(jìn)行進(jìn)一步的分析和處理,確保網(wǎng)絡(luò)數(shù)據(jù)能夠被系統(tǒng)有效地接收和處理。

內(nèi)核實(shí)現(xiàn)了網(wǎng)絡(luò)層的 ip 協(xié)議,也實(shí)現(xiàn)了傳輸層的 tcp 協(xié)議和 udp 協(xié)議。這些協(xié)議對應(yīng)的實(shí)現(xiàn)函數(shù)分別是 ip_rcv(),tcp_v4_rcv()和udp_rcv()。

網(wǎng)絡(luò)協(xié)議棧是通過函數(shù) inet_init() 注冊的,通過inet_init,將這些函數(shù)注冊到了inet_protos和ptype_base數(shù)據(jù)結(jié)構(gòu)中了。如下圖:

相關(guān)代碼如下:

//net/ipv4/af_inet.c
static struct packet_type ip_packet_type __read_mostly = {
 .type = cpu_to_be16(ETH_P_IP),
 .func = ip_rcv,
 .list_func = ip_list_rcv,
};

static const struct net_protocol tcp_protocol = {
 .handler = tcp_v4_rcv,
 .err_handler = tcp_v4_err,
 .no_policy = 1,
 .icmp_strict_tag_validation = 1,
};

static const struct net_protocol udp_protocol = {
 .handler = udp_rcv,
 .err_handler = udp_err,
 .no_policy = 1,
};

static int __init inet_init(void){

    ......
    if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
        pr_crit("%s: Cannot add ICMP protocol\n", __func__);
    if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)  //注冊 udp_rcv()
        pr_crit("%s: Cannot add UDP protocol\n", __func__);
    if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)  //注冊 tcp_v4_rcv()
        pr_crit("%s: Cannot add TCP protocol\n", __func__);
    ......
    dev_add_pack(&ip_packet_type);  /注冊 ip_rcv()

}

上面的代碼中我們可以看到,udp_protocol結(jié)構(gòu)體中的handler是udp_rcv,tcp_protocol結(jié)構(gòu)體中的handler是tcp_v4_rcv,通過inet_add_protocol被初始化了進(jìn)來。

int inet_add_protocol(const struct net_protocol *prot, unsigned char protocol){
    if (!prot->netns_ok) {
        pr_err("Protocol %u is not namespace aware, cannot register.\n",
            protocol);
        return -EINVAL;
    }

    return !cmpxchg((const struct net_protocol **)&inet_protos[protocol],
            NULL, prot) ? 0 : -1;

}

inet_add_protocol函數(shù)將tcp和udp對應(yīng)的處理函數(shù)都注冊到了inet_protos數(shù)組中了。

再看dev_add_pack(&ip_packet_type);這一行,ip_packet_type結(jié)構(gòu)體中的type是協(xié)議名,func是ip_rcv函數(shù),在dev_add_pack中會(huì)被注冊到ptype_base哈希表中。

//net/core/dev.c
void dev_add_pack(struct packet_type *pt){

    struct list_head *head = ptype_head(pt);
    ......

}

static inline struct list_head *ptype_head(const struct packet_type *pt){

    if (pt->type == htons(ETH_P_ALL))
        return &ptype_all;
    else
        return &ptype_base[ntohs(pt->type) & PTYPE_HASH_MASK];

}

這里我們需要記住inet_protos記錄著udp,tcp的處理函數(shù)地址,ptype_base存儲著ip_rcv()函數(shù)的處理地址。后面我們會(huì)看到軟中斷中會(huì)通過ptype_base找到ip_rcv函數(shù)地址,進(jìn)而將ip包正確地送到ip_rcv()中執(zhí)行。在ip_rcv中將會(huì)通過inet_protos找到tcp或者udp的處理函數(shù),再而把包轉(zhuǎn)發(fā)給udp_rcv()或tcp_v4_rcv()函數(shù)。

三、接收前的準(zhǔn)備工作

3.1 網(wǎng)絡(luò)子系統(tǒng)初始化

在 Linux 內(nèi)核啟動(dòng)的過程中,網(wǎng)絡(luò)子系統(tǒng)的初始化是一項(xiàng)關(guān)鍵任務(wù),它為后續(xù)的網(wǎng)絡(luò)數(shù)據(jù)接收和處理奠定了基礎(chǔ)。這一過程涉及一系列復(fù)雜的步驟和眾多關(guān)鍵函數(shù)的調(diào)用。

在內(nèi)核啟動(dòng)階段,start_kernel函數(shù)作為啟動(dòng)的入口,會(huì)有條不紊地調(diào)用一系列初始化函數(shù)。其中,net_dev_init函數(shù)在網(wǎng)絡(luò)子系統(tǒng)初始化中扮演著核心角色。net_dev_init函數(shù)會(huì)對每個(gè)可能的 CPU 進(jìn)行初始化操作,為網(wǎng)絡(luò)數(shù)據(jù)的接收和處理準(zhǔn)備好相關(guān)的數(shù)據(jù)結(jié)構(gòu)。例如,它會(huì)初始化每個(gè) CPU 上的softnet_data結(jié)構(gòu)體,該結(jié)構(gòu)體包含了用于存儲網(wǎng)絡(luò)數(shù)據(jù)包的隊(duì)列,如input_pkt_queue用于接收來自網(wǎng)絡(luò)設(shè)備的數(shù)據(jù)包,process_queue則用于存放等待進(jìn)一步處理的數(shù)據(jù)包 。通過skb_queue_head_init函數(shù)對這些隊(duì)列進(jìn)行初始化,確保它們能夠正確地存儲和管理數(shù)據(jù)包。

在網(wǎng)絡(luò)子系統(tǒng)初始化過程中,還需要對軟中斷進(jìn)行注冊。軟中斷是一種用于處理可延遲任務(wù)的機(jī)制,在網(wǎng)絡(luò)數(shù)據(jù)處理中起著至關(guān)重要的作用。通過open_softirq函數(shù),分別注冊了用于網(wǎng)絡(luò)發(fā)送的NET_TX_SOFTIRQ和用于網(wǎng)絡(luò)接收的NET_RX_SOFTIRQ軟中斷。以NET_RX_SOFTIRQ為例,它會(huì)關(guān)聯(lián)到net_rx_action函數(shù),當(dāng)網(wǎng)絡(luò)設(shè)備接收到數(shù)據(jù)包并觸發(fā)相應(yīng)的軟中斷時(shí),net_rx_action函數(shù)將被調(diào)用,從而啟動(dòng)對接收數(shù)據(jù)包的后續(xù)處理流程。linux內(nèi)核通過調(diào)用subsys_initcall來初始化各個(gè)子系統(tǒng),其中網(wǎng)絡(luò)子系統(tǒng)的初始化會(huì)執(zhí)行到net_dev_init函數(shù):

//net/core/dev.c

static int __init net_dev_init(void){

    ......

    for_each_possible_cpu(i) {
        struct softnet_data *sd = &per_cpu(softnet_data, i);

        memset(sd, 0, sizeof(*sd));
        skb_queue_head_init(&sd->input_pkt_queue);
        skb_queue_head_init(&sd->process_queue);
        sd->completion_queue = NULL;
        INIT_LIST_HEAD(&sd->poll_list);
        ......
    }
    ......
    open_softirq(NET_TX_SOFTIRQ, net_tx_action);
    open_softirq(NET_RX_SOFTIRQ, net_rx_action);

}

subsys_initcall(net_dev_init);

首先為每個(gè)CPU都申請一個(gè)softnet_data數(shù)據(jù)結(jié)構(gòu),在這個(gè)數(shù)據(jù)結(jié)構(gòu)里的poll_list是等待驅(qū)動(dòng)程序?qū)⑵鋚oll函數(shù)注冊進(jìn)來,稍后網(wǎng)卡驅(qū)動(dòng)初始化的時(shí)候我們可以看到這一過程。

然后 open_softirq 為每一種軟中斷都注冊一個(gè)處理函數(shù)。NET_TX_SOFTIRQ的處理函數(shù)為net_tx_action,NET_RX_SOFTIRQ的為net_rx_action。

//kernel/softirq.c

void open_softirq(int nr, void (*action)(struct softirq_action *)){

    softirq_vec[nr].action = action;

}

open_softirq 會(huì)把不同的 action 記錄在softirq_vec變量里的。后面ksoftirqd線程收到軟中斷的時(shí)候,也會(huì)使用這個(gè)變量來找到每一種軟中斷對應(yīng)的處理函數(shù)。

圖片圖片

3.2 網(wǎng)卡驅(qū)動(dòng)初始化

網(wǎng)卡驅(qū)動(dòng)的初始化是網(wǎng)絡(luò)數(shù)據(jù)包能夠被正確接收的前提條件,它涉及到驅(qū)動(dòng)的加載與注冊,以及為數(shù)據(jù)接收所做的一系列準(zhǔn)備工作。

在 Linux 系統(tǒng)中,大多數(shù)網(wǎng)卡通過 PCI 總線與系統(tǒng)相連,內(nèi)核通過 PCI 子系統(tǒng)對這些設(shè)備進(jìn)行管理。當(dāng)系統(tǒng)啟動(dòng)時(shí),會(huì)遍歷 PCI 總線上的設(shè)備,并為它們尋找合適的驅(qū)動(dòng)程序。對于網(wǎng)卡驅(qū)動(dòng)而言,首先要使用pci_register_driver函數(shù)向內(nèi)核注冊驅(qū)動(dòng)程序。在注冊過程中,需要提供一個(gè)pci_driver結(jié)構(gòu)體,該結(jié)構(gòu)體包含了驅(qū)動(dòng)的名稱、所支持的 PCI 設(shè)備 ID 表、探測函數(shù)(probe)等重要信息。

以常見的igb網(wǎng)卡驅(qū)動(dòng)為例,igb_driver結(jié)構(gòu)體中定義了驅(qū)動(dòng)的名稱為igb_driver_name,并指定了igb_pci_tbl作為所支持的 PCI 設(shè)備 ID 表。當(dāng)內(nèi)核發(fā)現(xiàn)一個(gè) PCI 設(shè)備的 ID 與igb_pci_tbl中的某個(gè)條目匹配時(shí),就會(huì)調(diào)用igb_probe探測函數(shù)。在igb_probe函數(shù)中,會(huì)進(jìn)行一系列的初始化操作,如為網(wǎng)卡分配內(nèi)存空間、初始化 DMA(直接內(nèi)存訪問)、設(shè)置網(wǎng)卡的相關(guān)寄存器等。它還會(huì)注冊net_device結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體代表了網(wǎng)絡(luò)設(shè)備,包含了設(shè)備的各種屬性和操作函數(shù),如設(shè)備的打開、關(guān)閉、數(shù)據(jù)發(fā)送和接收等函數(shù)。通過這些初始化操作,網(wǎng)卡驅(qū)動(dòng)為后續(xù)的數(shù)據(jù)接收做好了充分準(zhǔn)備,確保網(wǎng)卡能夠與內(nèi)核進(jìn)行有效的通信,將接收到的網(wǎng)絡(luò)數(shù)據(jù)包傳遞給內(nèi)核進(jìn)行處理。這里以 FSL 系列網(wǎng)卡為例,其驅(qū)動(dòng)位于:drivers/net/ethernet/freescale/fec_main.c

static struct platform_driver fec_driver = {
 .driver = {
  .name = DRIVER_NAME,
  .pm = &fec_pm_ops,
  .of_match_table = fec_dt_ids,
  .suppress_bind_attrs = true,
 },
 .id_table = fec_devtype,
 .probe = fec_probe,
 .remove = fec_drv_remove,
};

static int
fec_probe(struct platform_device *pdev)
{
  fec_enet_clk_enable
  fec_reset_phy      //使用gpio 復(fù)位phy 芯片
  fec_enet_init      //設(shè)置netdev_ops、設(shè)置ethtool_ops
  for (i = 0; i < irq_cnt; i++) {
    devm_request_irq(..., irq, fec_enet_interrupt, ...);
  }
  fec_enet_mii_init  //讀取dts mdio節(jié)點(diǎn)下phy子節(jié)點(diǎn),并注冊phy_device
  register_netdev    //注冊網(wǎng)絡(luò)設(shè)備
}

Linux 以太網(wǎng)驅(qū)動(dòng)會(huì)向上層提供 net_device_ops ,方便應(yīng)用層控制網(wǎng)卡,比如網(wǎng)卡被啟動(dòng)(例如,通過 ifconfig eth0 up)的時(shí)候會(huì)被調(diào)用 fec_enet_open,此外它還包含著網(wǎng)卡發(fā)包、設(shè)置 mac 地址等回調(diào)函數(shù)。

static const struct net_device_ops fec_netdev_ops = {
 .ndo_open  = fec_enet_open,
 .ndo_stop  = fec_enet_close,
 .ndo_start_xmit  = fec_enet_start_xmit,
 .ndo_select_queue       = fec_enet_select_queue,
 .ndo_set_rx_mode = set_multicast_list,
 .ndo_validate_addr = eth_validate_addr,
 .ndo_tx_timeout  = fec_timeout,
 .ndo_set_mac_address = fec_set_mac_address,
 .ndo_eth_ioctl  = fec_enet_ioctl,
#ifdef CONFIG_NET_POLL_CONTROLLER
 .ndo_poll_controller = fec_poll_controller,
#endif
 .ndo_set_features = fec_set_features,
 .ndo_bpf  = fec_enet_bpf,
 .ndo_xdp_xmit  = fec_enet_xdp_xmit,
};

此外,網(wǎng)卡驅(qū)動(dòng)實(shí)現(xiàn)了 ethtool 所需要的接口,當(dāng) ethtool 發(fā)起一個(gè)系統(tǒng)調(diào)用之后,內(nèi)核會(huì)找到對應(yīng)操作的回調(diào)函數(shù)。可以看到 ethtool 這個(gè)命令之所以能查看網(wǎng)卡收發(fā)包統(tǒng)計(jì)、能修改網(wǎng)卡自適應(yīng)模式、能調(diào)整RX 隊(duì)列的數(shù)量和大小,是因?yàn)?ethtool 命令最終調(diào)用到了網(wǎng)卡驅(qū)動(dòng)的相應(yīng)方法。

static const struct ethtool_ops fec_enet_ethtool_ops = {
 .supported_coalesce_params = ETHTOOL_COALESCE_USECS |
         ETHTOOL_COALESCE_MAX_FRAMES,
 .get_drvinfo  = fec_enet_get_drvinfo,
 .get_regs_len  = fec_enet_get_regs_len,
 .get_regs  = fec_enet_get_regs,
 .nway_reset  = phy_ethtool_nway_reset,
 .get_link  = ethtool_op_get_link,
 .get_coalesce  = fec_enet_get_coalesce,
 .set_coalesce  = fec_enet_set_coalesce,
#ifndef CONFIG_M5272
 .get_pauseparam  = fec_enet_get_pauseparam,
 .set_pauseparam  = fec_enet_set_pauseparam,
 .get_strings  = fec_enet_get_strings,
 .get_ethtool_stats = fec_enet_get_ethtool_stats,
 .get_sset_count  = fec_enet_get_sset_count,
#endif
 .get_ts_info  = fec_enet_get_ts_info,
 .get_tunable  = fec_enet_get_tunable,
 .set_tunable  = fec_enet_set_tunable,
 .get_wol  = fec_enet_get_wol,
 .set_wol  = fec_enet_set_wol,
 .get_eee  = fec_enet_get_eee,
 .set_eee  = fec_enet_set_eee,
 .get_link_ksettings = phy_ethtool_get_link_ksettings,
 .set_link_ksettings = phy_ethtool_set_link_ksettings,
 .self_test  = net_selftest,
};

3.3 ksoftirqd 內(nèi)核線程創(chuàng)建

ksoftirqd內(nèi)核線程在 Linux 網(wǎng)絡(luò)數(shù)據(jù)處理中承擔(dān)著處理軟中斷的重要職責(zé),其創(chuàng)建過程與系統(tǒng)的啟動(dòng)流程緊密相關(guān)。

在系統(tǒng)啟動(dòng)時(shí),start_kernel函數(shù)會(huì)創(chuàng)建init線程,隨后init線程調(diào)用do_pre_smp_initcalls函數(shù)。在do_pre_smp_initcalls函數(shù)中,會(huì)調(diào)用spawn_ksoftirqd函數(shù)來創(chuàng)建ksoftirqd內(nèi)核線程。spawn_ksoftirqd函數(shù)通過兩次調(diào)用cpu_callback函數(shù),分別使用CPU_UP_PREPARE和CPU_ONLINE參數(shù)來完成ksoftirqd線程的創(chuàng)建和喚醒。

當(dāng)使用CPU_UP_PREPARE參數(shù)調(diào)用cpu_callback函數(shù)時(shí),會(huì)通過kthread_create函數(shù)創(chuàng)建一個(gè)新的內(nèi)核線程,并將其命名為ksoftirqd/%d,其中%d為 CPU 的編號。創(chuàng)建完成后,將該線程的task_struct指針存儲在per_cpu(ksoftirqd, hotcpu)變量中,以便后續(xù)對該線程進(jìn)行管理和調(diào)度。接著,使用CPU_ONLINE參數(shù)再次調(diào)用cpu_callback函數(shù),此時(shí)的作用是喚醒剛剛創(chuàng)建的ksoftirqd線程,使其能夠開始執(zhí)行任務(wù)。

ksoftirqd線程的主要任務(wù)是處理系統(tǒng)中的軟中斷。當(dāng)網(wǎng)絡(luò)設(shè)備接收到數(shù)據(jù)包并觸發(fā)硬中斷后,硬中斷處理程序會(huì)快速完成一些必要的操作,然后觸發(fā)相應(yīng)的軟中斷。ksoftirqd線程會(huì)不斷地檢查是否有軟中斷等待處理,一旦發(fā)現(xiàn)有軟中斷,就會(huì)調(diào)用相應(yīng)的軟中斷處理函數(shù),對網(wǎng)絡(luò)數(shù)據(jù)包進(jìn)行進(jìn)一步的處理,如解析數(shù)據(jù)包、將其傳遞給協(xié)議棧的上層進(jìn)行處理等。通過這種方式,ksoftirqd線程有效地分擔(dān)了硬中斷處理的工作量,避免了硬中斷處理時(shí)間過長而影響系統(tǒng)對其他硬件中斷的響應(yīng),確保了系統(tǒng)能夠高效、穩(wěn)定地處理網(wǎng)絡(luò)數(shù)據(jù)。

四、數(shù)據(jù)包接收流程

4.1 網(wǎng)卡接收數(shù)據(jù)

當(dāng)網(wǎng)絡(luò)數(shù)據(jù)包在網(wǎng)絡(luò)中傳輸并抵達(dá)網(wǎng)卡時(shí),網(wǎng)卡便開始發(fā)揮其關(guān)鍵作用。網(wǎng)卡具備專門的硬件電路,能夠識別和捕獲這些數(shù)據(jù)包。通過直接內(nèi)存訪問(DMA)技術(shù),網(wǎng)卡得以將接收到的數(shù)據(jù)包高效地存入內(nèi)存緩沖區(qū)中。

DMA 是一種強(qiáng)大的技術(shù),它允許外部設(shè)備(如網(wǎng)卡)直接與內(nèi)存進(jìn)行數(shù)據(jù)傳輸,而無需 CPU 的頻繁干預(yù) 。在這個(gè)過程中,網(wǎng)卡中的 DMA 控制器會(huì)負(fù)責(zé)管理數(shù)據(jù)的傳輸過程,它會(huì)在內(nèi)存中開辟出一塊特定的緩沖區(qū),通常被稱為 Ring Buffer(環(huán)形緩沖區(qū))。Ring Buffer 就像是一個(gè)環(huán)形的隊(duì)列,數(shù)據(jù)包按照順序依次存入其中。由于其環(huán)形的結(jié)構(gòu),當(dāng)緩沖區(qū)的尾部到達(dá)末尾時(shí),下一個(gè)數(shù)據(jù)包會(huì)接著存入緩沖區(qū)的頭部,從而實(shí)現(xiàn)了連續(xù)的數(shù)據(jù)存儲。

與傳統(tǒng)的通過 CPU 進(jìn)行數(shù)據(jù)傳輸?shù)姆绞较啾龋珼MA 具有顯著的優(yōu)勢。傳統(tǒng)方式下,CPU 需要親自參與數(shù)據(jù)的讀取和寫入操作,這會(huì)占用大量的 CPU 時(shí)間和資源,導(dǎo)致 CPU 無法高效地處理其他任務(wù)。而采用 DMA 技術(shù),CPU 可以將精力集中在其他重要的任務(wù)上,大大提高了系統(tǒng)的整體性能。例如,在進(jìn)行大規(guī)模數(shù)據(jù)傳輸時(shí),如果沒有 DMA 技術(shù),CPU 可能會(huì)被數(shù)據(jù)傳輸任務(wù)所占據(jù),導(dǎo)致系統(tǒng)響應(yīng)遲緩,其他應(yīng)用程序無法正常運(yùn)行。而有了 DMA 技術(shù),網(wǎng)卡可以在后臺默默地將數(shù)據(jù)傳輸?shù)絻?nèi)存中,CPU 則可以同時(shí)處理用戶的其他操作,如運(yùn)行其他應(yīng)用程序、處理文件等,極大地提高了系統(tǒng)的并發(fā)處理能力。

4.2 硬件中斷處理

當(dāng)網(wǎng)卡成功將數(shù)據(jù)包存入內(nèi)存緩沖區(qū)后,為了及時(shí)通知 CPU 有新的數(shù)據(jù)到達(dá),網(wǎng)卡會(huì)觸發(fā)一個(gè)硬件中斷信號 。這個(gè)信號就像是一個(gè)緊急通知,告訴 CPU 需要立即處理新到達(dá)的數(shù)據(jù)。

CPU 在接收到這個(gè)硬件中斷信號時(shí),會(huì)迅速做出響應(yīng)。它會(huì)暫停當(dāng)前正在執(zhí)行的任務(wù),保存當(dāng)前任務(wù)的現(xiàn)場信息,包括寄存器的值、程序計(jì)數(shù)器的值等,以便在處理完中斷后能夠恢復(fù)到原來的任務(wù)執(zhí)行狀態(tài)。之后,CPU 會(huì)根據(jù)中斷向量表,找到對應(yīng)的中斷處理函數(shù)。中斷向量表就像是一個(gè)索引表,它記錄了每個(gè)中斷源對應(yīng)的中斷處理函數(shù)的入口地址。通過查詢中斷向量表,CPU 可以快速找到處理網(wǎng)卡中斷的函數(shù)。

對于網(wǎng)卡的中斷處理函數(shù),其主要任務(wù)是將接收到的數(shù)據(jù)從網(wǎng)卡的緩沖區(qū)拷貝到內(nèi)核緩沖區(qū)中,并進(jìn)行一些初步的處理 。它會(huì)對數(shù)據(jù)包進(jìn)行簡單的校驗(yàn),檢查數(shù)據(jù)包是否完整、是否存在錯(cuò)誤等。如果發(fā)現(xiàn)數(shù)據(jù)包存在問題,可能會(huì)采取相應(yīng)的措施,如丟棄該數(shù)據(jù)包或者發(fā)送錯(cuò)誤通知。中斷處理函數(shù)還會(huì)設(shè)置一些與數(shù)據(jù)包相關(guān)的標(biāo)志位,以便后續(xù)的處理程序能夠了解數(shù)據(jù)包的狀態(tài)。由于硬件中斷處理需要快速響應(yīng),以避免數(shù)據(jù)丟失或其他硬件事件的延遲處理,因此硬件中斷處理函數(shù)通常只執(zhí)行一些緊急且耗時(shí)較短的操作,而將更復(fù)雜的處理工作交給后續(xù)的軟中斷處理。

首先當(dāng)數(shù)據(jù)幀從網(wǎng)線到達(dá)網(wǎng)卡,網(wǎng)卡在分配給自己的 ringBuffer 中尋找可用的內(nèi)存位置,找到后 DMA 會(huì)把數(shù)據(jù)拷貝到網(wǎng)卡之前關(guān)聯(lián)的內(nèi)存里。當(dāng) DMA 操作完成以后,網(wǎng)卡會(huì)向 CPU 發(fā)起一個(gè)硬中斷,通知 CPU 有數(shù)據(jù)到達(dá)。中斷處理函數(shù)為:

//drivers/net/ethernet/freescale/fec_main.c
static irqreturn_t
fec_enet_interrupt(int irq, void *dev_id)
{
 struct net_device *ndev = dev_id;
 struct fec_enet_private *fep = netdev_priv(ndev);
 irqreturn_t ret = IRQ_NONE;

 if (fec_enet_collect_events(fep) && fep->link) {
  ret = IRQ_HANDLED;

  if (napi_schedule_prep(&fep->napi)) {
   /* Disable interrupts */
   writel(0, fep->hwp + FEC_IMASK);
   __napi_schedule(&fep->napi);
  }
 }

 return ret;
}
//net/core/dev.c
__napi_schedule->____napi_schedule
static inline void ____napi_schedule(struct softnet_data *sd,

                     struct napi_struct *napi){
    list_add_tail(&napi->poll_list, &sd->poll_list);
    __raise_softirq_irqoff(NET_RX_SOFTIRQ);

}

這里我們看到,list_add_tail修改了CPU變量softnet_data里的poll_list,將驅(qū)動(dòng)napi_struct傳過來的poll_list添加了進(jìn)來。其中softnet_data中的poll_list是一個(gè)雙向列表,其中的設(shè)備都帶有輸入幀等著被處理。緊接著 __raise_softirq_irqoff 觸發(fā)了一個(gè)軟中斷 NET_RX_SOFTIRQ。

圖片圖片

注意:當(dāng)RingBuffer滿的時(shí)候,新來的數(shù)據(jù)包將給丟棄。ifconfig查看網(wǎng)卡的時(shí)候,可以里面有個(gè)overruns,表示因?yàn)榄h(huán)形隊(duì)列滿被丟棄的包。如果發(fā)現(xiàn)有丟包,可能需要通過ethtool命令來加大環(huán)形隊(duì)列的長度。

4.3 軟中斷處理

在硬件中斷處理完成后,會(huì)觸發(fā)相應(yīng)的軟中斷,將數(shù)據(jù)包的后續(xù)處理工作交給軟中斷機(jī)制來完成 。軟中斷的處理主要由ksoftirqd內(nèi)核線程負(fù)責(zé)。

ksoftirqd線程會(huì)不斷地檢查是否有軟中斷等待處理。當(dāng)檢測到網(wǎng)絡(luò)接收相關(guān)的軟中斷(NET_RX_SOFTIRQ)被觸發(fā)時(shí),它會(huì)調(diào)用net_rx_action函數(shù)。net_rx_action函數(shù)是軟中斷處理網(wǎng)絡(luò)數(shù)據(jù)包的核心函數(shù),它會(huì)從 Ring Buffer 中取出數(shù)據(jù)包,并調(diào)用網(wǎng)卡驅(qū)動(dòng)注冊的poll函數(shù)。

poll函數(shù)的作用是對數(shù)據(jù)包進(jìn)行進(jìn)一步的處理和準(zhǔn)備工作 。它會(huì)對數(shù)據(jù)包進(jìn)行合并、整理等操作。有時(shí),一個(gè)完整的數(shù)據(jù)包可能會(huì)被分成多個(gè)部分存儲在 Ring Buffer 中,poll函數(shù)會(huì)將這些分散的部分合并成一個(gè)完整的數(shù)據(jù)包。poll函數(shù)還會(huì)對數(shù)據(jù)包進(jìn)行一些過濾和篩選,去除不符合要求的數(shù)據(jù)包。經(jīng)過poll函數(shù)處理后的數(shù)據(jù)包,會(huì)被傳遞給內(nèi)核協(xié)議棧的網(wǎng)絡(luò)層進(jìn)行后續(xù)的處理,從而實(shí)現(xiàn)了從硬件層面到軟件層面的數(shù)據(jù)交接,確保數(shù)據(jù)包能夠在系統(tǒng)中繼續(xù)流轉(zhuǎn)和處理。接下來進(jìn)入軟中斷處理函數(shù):

//kernel/softirq.c
static void run_ksoftirqd(unsigned int cpu){
    local_irq_disable();
    if (local_softirq_pending()) {
        __do_softirq();
        rcu_note_context_switch(cpu);
        local_irq_enable();
        cond_resched();
        return;
    }
    local_irq_enable();

}

asmlinkage __visible void __softirq_entry __do_softirq(void)
{
  while ((softirq_bit = ffs(pending))) {
    h->action(h);
  }
}

在網(wǎng)絡(luò)設(shè)備子系統(tǒng)初始化中,講到為 NET_RX_SOFTIRQ 注冊了處理函數(shù) net_rx_action。所以 net_rx_action 函數(shù)就會(huì)被執(zhí)行到了。

//net/core/dev.c
static __latent_entropy void net_rx_action(struct softirq_action *h)
{
  struct softnet_data *sd = this_cpu_ptr(&softnet_data);

  list_splice_init(&sd->poll_list, &list);

  for (;;) {
    ...
    n = list_first_entry(&list, struct napi_struct, poll_list);
    budget -= napi_poll(n, &repoll);
    ...
  }
  ...
}

napi_poll->__napi_poll->work = n->poll(n, weight)

首先獲取到當(dāng)前CPU變量softnet_data,對其poll_list進(jìn)行遍歷, 然后執(zhí)行到網(wǎng)卡驅(qū)動(dòng)注冊到的 poll 函數(shù)。對于 FSL 網(wǎng)卡來說,其驅(qū)動(dòng)對應(yīng)的 poll 函數(shù)就是 fec_enet_rx_napi。

//drivers/net/ethernet/freescale/fec_main.c
static int fec_enet_rx_napi(struct napi_struct *napi, int budget)
{
 struct net_device *ndev = napi->dev;
 struct fec_enet_private *fep = netdev_priv(ndev);
 int done = 0;

 do {
  done += fec_enet_rx(ndev, budget - done);
  fec_enet_tx(ndev);
 } while ((done < budget) && fec_enet_collect_events(fep));

 if (done < budget) {
  napi_complete_done(napi, done);
  writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
 }

 return done;
}
fec_enet_rx->fec_enet_rx_queue

圖片圖片

然后進(jìn)入 GRO 處理,流程如下:

napi_gro_receive->napi_skb_finish->gro_normal_one->gro_normal_list->netif_receive_skb_list_internal

圖片圖片

最終通過函數(shù) netif_receive_skb_list_internal() 進(jìn)入內(nèi)核協(xié)議棧。

4.4 協(xié)議棧逐層處理

⑴網(wǎng)絡(luò)接口層

當(dāng)數(shù)據(jù)包從軟中斷處理環(huán)節(jié)傳遞到內(nèi)核協(xié)議棧的網(wǎng)絡(luò)接口層時(shí),網(wǎng)絡(luò)接口層首先會(huì)對數(shù)據(jù)包的合法性進(jìn)行嚴(yán)格檢查 。它會(huì)驗(yàn)證數(shù)據(jù)包的格式是否符合網(wǎng)絡(luò)接口層協(xié)議的規(guī)定,例如以太網(wǎng)協(xié)議規(guī)定了數(shù)據(jù)幀的特定格式,包括前導(dǎo)碼、幀起始定界符、源 MAC 地址、目的 MAC 地址、類型字段、數(shù)據(jù)字段和幀校驗(yàn)序列(FCS)等。網(wǎng)絡(luò)接口層會(huì)檢查這些字段是否完整、正確,F(xiàn)CS 是否匹配,以確保數(shù)據(jù)包在傳輸過程中沒有出現(xiàn)錯(cuò)誤。

網(wǎng)絡(luò)接口層還會(huì)仔細(xì)判斷數(shù)據(jù)包所使用的協(xié)議類型 。在以太網(wǎng)環(huán)境中,類型字段用于標(biāo)識上層協(xié)議,常見的類型值如 0x0800 表示 IP 協(xié)議,0x0806 表示 ARP 協(xié)議等。通過識別這個(gè)類型字段,網(wǎng)絡(luò)接口層能夠確定該數(shù)據(jù)包應(yīng)該被傳遞給哪個(gè)上層協(xié)議進(jìn)行進(jìn)一步處理。如果是 IP 協(xié)議的數(shù)據(jù)包,網(wǎng)絡(luò)接口層會(huì)去掉以太網(wǎng)幀頭和幀尾,將剩余的 IP 數(shù)據(jù)包傳遞給網(wǎng)絡(luò)層;如果是 ARP 協(xié)議的數(shù)據(jù)包,則會(huì)在網(wǎng)絡(luò)接口層進(jìn)行相應(yīng)的 ARP 處理,如更新 ARP 緩存表等。

⑵網(wǎng)絡(luò)層

在網(wǎng)絡(luò)接口層完成處理后,IP 數(shù)據(jù)包被傳遞到網(wǎng)絡(luò)層。網(wǎng)絡(luò)層的首要任務(wù)是對 IP 包進(jìn)行深入的路由判斷 。它會(huì)檢查 IP 包的目的 IP 地址,然后查詢系統(tǒng)的路由表,以確定該數(shù)據(jù)包應(yīng)該被發(fā)送到哪里。路由表中存儲了一系列的路由規(guī)則,這些規(guī)則根據(jù)目的 IP 地址的網(wǎng)絡(luò)部分來確定數(shù)據(jù)包的下一跳地址。例如,如果目的 IP 地址屬于本地網(wǎng)絡(luò),網(wǎng)絡(luò)層會(huì)直接將數(shù)據(jù)包發(fā)送到本地的目標(biāo)主機(jī);如果目的 IP 地址屬于其他網(wǎng)絡(luò),網(wǎng)絡(luò)層會(huì)根據(jù)路由表將數(shù)據(jù)包發(fā)送到合適的路由器,由路由器繼續(xù)轉(zhuǎn)發(fā)數(shù)據(jù)包。

網(wǎng)絡(luò)層還負(fù)責(zé)將數(shù)據(jù)包分發(fā)給正確的上層協(xié)議 。IP 包的首部中有一個(gè)協(xié)議字段,該字段用于標(biāo)識上層協(xié)議的類型,如 6 表示 TCP 協(xié)議,17 表示 UDP 協(xié)議等。根據(jù)這個(gè)協(xié)議字段的值,網(wǎng)絡(luò)層能夠確定將數(shù)據(jù)包傳遞給傳輸層的哪個(gè)協(xié)議模塊進(jìn)行后續(xù)處理。當(dāng)確定上層協(xié)議為 TCP 時(shí),網(wǎng)絡(luò)層會(huì)去掉 IP 頭,將剩余的 TCP 數(shù)據(jù)段傳遞給傳輸層的 TCP 模塊;若上層協(xié)議為 UDP,則將 UDP 數(shù)據(jù)報(bào)傳遞給傳輸層的 UDP 模塊。

⑶傳輸層

傳輸層在接收到網(wǎng)絡(luò)層傳遞過來的 TCP 數(shù)據(jù)段或 UDP 數(shù)據(jù)報(bào)后,會(huì)依據(jù)端口號來準(zhǔn)確地將數(shù)據(jù)送到對應(yīng)的 Socket 。每個(gè) Socket 都與一個(gè)特定的端口號相關(guān)聯(lián),而端口號則用于區(qū)分不同的應(yīng)用程序或服務(wù)。

對于 TCP 協(xié)議,傳輸層會(huì)根據(jù) TCP 數(shù)據(jù)段中的源端口號、目的端口號、源 IP 地址和目的 IP 地址這四元組,在系統(tǒng)中查找與之匹配的 Socket 。這個(gè)四元組就像是一個(gè)唯一的標(biāo)識,能夠準(zhǔn)確地確定一個(gè) TCP 連接。一旦找到對應(yīng)的 Socket,傳輸層會(huì)將 TCP 數(shù)據(jù)段中的數(shù)據(jù)拷貝到該 Socket 的接收緩沖區(qū)中,等待應(yīng)用程序通過 Socket 接口來讀取數(shù)據(jù)。例如,當(dāng)你在瀏覽器中訪問一個(gè)網(wǎng)站時(shí),瀏覽器會(huì)與網(wǎng)站服務(wù)器建立一個(gè) TCP 連接,傳輸層會(huì)根據(jù)這個(gè)連接的四元組信息,將接收到的網(wǎng)頁數(shù)據(jù)準(zhǔn)確地送到對應(yīng)的 Socket 接收緩沖區(qū),供瀏覽器讀取并顯示網(wǎng)頁內(nèi)容。

對于 UDP 協(xié)議,傳輸層同樣根據(jù) UDP 數(shù)據(jù)報(bào)中的源端口號和目的端口號,查找對應(yīng)的 Socket 。由于 UDP 是無連接的協(xié)議,它不需要像 TCP 那樣進(jìn)行復(fù)雜的連接建立和管理過程。一旦找到匹配的 Socket,傳輸層就會(huì)將 UDP 數(shù)據(jù)報(bào)中的數(shù)據(jù)拷貝到該 Socket 的接收緩沖區(qū)中。例如,在進(jìn)行實(shí)時(shí)視頻聊天時(shí),視頻數(shù)據(jù)通常通過 UDP 協(xié)議傳輸,傳輸層會(huì)快速地將接收到的 UDP 視頻數(shù)據(jù)報(bào)送到對應(yīng)的 Socket 接收緩沖區(qū),以便視頻播放程序能夠及時(shí)讀取并顯示視頻畫面。

五、數(shù)據(jù)包到達(dá)應(yīng)用層

經(jīng)過一系列復(fù)雜的處理,數(shù)據(jù)包終于抵達(dá)了應(yīng)用層,這是數(shù)據(jù)旅程的最后一站,也是數(shù)據(jù)能夠被應(yīng)用程序?qū)嶋H使用的關(guān)鍵環(huán)節(jié)。應(yīng)用程序通過 Socket 接口來讀取這些數(shù)據(jù),從而實(shí)現(xiàn)各種網(wǎng)絡(luò)功能。

在 Linux 系統(tǒng)中,Socket 是應(yīng)用程序與網(wǎng)絡(luò)協(xié)議棧進(jìn)行交互的重要接口。它為應(yīng)用程序提供了一種統(tǒng)一的方式來發(fā)送和接收網(wǎng)絡(luò)數(shù)據(jù),隱藏了底層網(wǎng)絡(luò)協(xié)議的復(fù)雜性。當(dāng)數(shù)據(jù)包到達(dá)傳輸層并被存入對應(yīng)的 Socket 接收緩沖區(qū)后,應(yīng)用程序便可以通過系統(tǒng)調(diào)用,如recv或recvfrom函數(shù),從 Socket 緩沖區(qū)中讀取數(shù)據(jù)。

以一個(gè)簡單的網(wǎng)絡(luò)聊天程序?yàn)槔?dāng)用戶發(fā)送消息時(shí),消息會(huì)被封裝成數(shù)據(jù)包,經(jīng)過網(wǎng)絡(luò)協(xié)議棧的層層處理發(fā)送出去;而當(dāng)接收方收到數(shù)據(jù)包時(shí),數(shù)據(jù)包會(huì)沿著協(xié)議棧層層向上傳遞,最終到達(dá)應(yīng)用層。接收方的聊天程序通過 Socket 接口調(diào)用recv函數(shù),從 Socket 接收緩沖區(qū)中讀取數(shù)據(jù),將其解析為用戶能夠理解的消息內(nèi)容,并顯示在聊天窗口中。

在實(shí)際應(yīng)用中,應(yīng)用程序可能會(huì)根據(jù)自身的需求,對讀取到的數(shù)據(jù)進(jìn)行進(jìn)一步的處理和解析。例如,對于一個(gè) HTTP 服務(wù)器應(yīng)用程序,它接收到的數(shù)據(jù)包可能包含了 HTTP 請求信息,服務(wù)器會(huì)解析這些請求,提取出請求的資源路徑、請求方法等信息,然后根據(jù)這些信息返回相應(yīng)的 HTTP 響應(yīng)。

責(zé)任編輯:武曉燕 來源: 深度Linux
相關(guān)推薦

2017-03-28 13:25:14

Linux網(wǎng)絡(luò)數(shù)據(jù)包

2017-04-07 09:30:49

Linux網(wǎng)絡(luò)數(shù)據(jù)包

2019-04-29 07:53:11

TCP數(shù)據(jù)包TCP網(wǎng)絡(luò)編程

2021-05-12 00:07:27

TCPIP協(xié)議

2020-10-29 15:05:31

Linux網(wǎng)絡(luò)包代碼

2018-01-18 09:05:05

存儲數(shù)據(jù)包分層

2013-01-28 13:32:52

路由器網(wǎng)絡(luò)設(shè)置數(shù)據(jù)傳輸

2012-09-04 11:08:57

VMwarevSwitchvSwitch配置

2021-10-08 10:05:10

數(shù)據(jù)包三次握手HTTP

2023-03-27 00:17:21

eBPF技術(shù)網(wǎng)絡(luò)

2023-05-08 12:03:14

Linux內(nèi)核進(jìn)程

2025-01-10 11:28:58

2017-08-22 11:30:15

LinuxWireshark過濾數(shù)據(jù)包

2017-10-26 16:29:43

2013-05-24 08:56:23

VMware虛擬機(jī)數(shù)據(jù)包

2020-11-18 07:54:31

太網(wǎng)數(shù)據(jù)包架構(gòu)

2023-03-27 00:13:26

數(shù)據(jù)包Kubernete網(wǎng)絡(luò)

2021-07-15 09:57:39

Wireshark數(shù)據(jù)包長度

2025-06-10 01:22:00

2025-02-08 10:54:02

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 欧美福利| 国产欧美日韩精品在线观看 | 国产99热精品 | 欧美激情精品久久久久久 | 香蕉久久网 | 国产一区二区三区四区五区加勒比 | 精品国产欧美一区二区三区不卡 | 午夜影院视频 | 免费在线h视频 | 亚洲国产精品suv | 日韩成年人视频在线 | 99精品网 | 中文字幕在线人 | 一区二区三区视频在线观看 | 一级毛片视频在线观看 | 亚洲精品一区在线观看 | 亚洲国产精品一区二区久久 | 人人看人人搞 | 久草新视频 | 天天操天天舔 | 日本羞羞影院 | 国产精品a久久久久 | 免费黄色大片 | 久久精品久久久久久 | 国产精品嫩草影院精东 | 91pao对白在线播放 | 特级特黄特色的免费大片 | 国产精品成人一区二区三区 | 在线视频国产一区 | 欧美自拍一区 | www.婷婷| 欧美一级黄 | 亚洲色图综合 | 成人亚洲性情网站www在线观看 | 亚洲成人在线免费 | 精品一区二区三区四区在线 | 欧美国产亚洲一区二区 | 在线国产欧美 | 日韩精品一区二区三区在线 | 欧美八区| 久久久久久久久久久国产 |