Linux內(nèi)核源代碼:tcp/ip協(xié)議棧的調(diào)用
這里共維護(hù)了三個(gè)隊(duì)列:prequeue、backlog、receive_queue,分別為預(yù)處理隊(duì)列,后備隊(duì)列和接收隊(duì)列,在連接建立后,若沒(méi)有數(shù)據(jù)到來(lái),接收隊(duì)列為空,進(jìn)程會(huì)在sk_busy_loop函數(shù)內(nèi)循環(huán)等待,知道接收隊(duì)列不為空,并調(diào)用函數(shù)數(shù)skb_copy_datagram_msg將接收到的數(shù)據(jù)拷貝到用戶態(tài),實(shí)際調(diào)用的是__skb_datagram_iter,這里同樣用了struct msghdr *msg來(lái)實(shí)現(xiàn)。__skb_datagram_iter函數(shù)如下:
int __skb_datagram_iter(const struct sk_buff *skb, int offset,
struct iov_iter *to, int len, bool fault_short,
size_t (*cb)(const void *, size_t, void *, struct iov_iter *),
void *data)
{
int start = skb_h(yuǎn)eadlen(skb);
int i, copy = start - offset, start_off = offset, n;
struct sk_buff *frag_iter;
拷貝tcp頭部
if (copy > 0) {
if (copy > len)
copy = len;
n = cb(skb->data + offset, copy, data, to);
offset += n;
if (n 。 copy)
goto short_copy;
if ((len -= copy) == 0)
return 0;
}
拷貝數(shù)據(jù)部分
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
int end;
const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
WARN_ON(start > offset + len);
end = start + skb_frag_size(frag);
if ((copy = end - offset) > 0) {
struct page *page = skb_frag_page(frag);
u8 *vaddr = kmap(page);
if (copy > len)
copy = len;
n = cb(vaddr + frag->page_offset +
offset - start, copy, data, to);
kunmap(page);
offset += n;
if (n 。 copy)
goto short_copy;
if (。╨en -= copy))
return 0;
}
start = end;
}
拷貝完成后,函數(shù)返回,整個(gè)接收的過(guò)程也就完成了。
用一張函數(shù)間的相互調(diào)用圖可以表示:
通過(guò)gdb調(diào)試驗(yàn)證如下:
Breakpoint 1, __sys_recvfrom (fd=5, ubuf=0x7ffd9428d960, size=1024, flags=0,
addr=0x0 <fixed_percpu_data>, addr_len=0x0 <fixed_percpu_data>)
at net/socket.c:1990
1990 {
(gdb) c
Continuing.
Breakpoint 2, sock_recvmsg (sock=0xffff888006df1900, msg=0xffffc900001f7e28,
flags=0) at net/socket.c:891
891 {
(gdb) c
Continuing.
Breakpoint 3, tcp_recvmsg (sk=0xffff888006479100, msg=0xffffc900001f7e28,
len=1024, nonblock=0, flags=0, addr_len=0xffffc900001f7df4)
at net/ipv4/tcp.c:1933
1933 {
(gdb) cBreakpoint 1, __sys_recvfrom (fd=5, ubuf=0x7ffd9428d960, size=1024, flags=0,
addr=0x0 <fixed_percpu_data>, addr_len=0x0 <fixed_percpu_data>)
at net/socket.c:1990
1990 {
(gdb) c
Continuing.
Breakpoint 2, sock_recvmsg (sock=0xffff888006df1900, msg=0xffffc900001f7e28,
flags=0) at net/socket.c:891
891 {
(gdb) c
Continuing.
Breakpoint 3, tcp_recvmsg (sk=0xffff888006479100, msg=0xffffc900001f7e28,
len=1024, nonblock=0, flags=0, addr_len=0xffffc900001f7df4)
at net/ipv4/tcp.c:1933
1933 {
(gdb) c
Continuing.
Breakpoint 4, __skb_datagram_iter (skb=0xffff8880068714e0, offset=0,
to=0xffffc900001efe38, len=2, fault_short=false,
cb=0xffffffff817ff860 <simple_copy_to_iter>, data=0x0 <fixed_percpu_data>)
at net/core/datagram.c:414
414 {
符合我們之前的分析。
5 IP層流程
5.1 發(fā)送端
網(wǎng)絡(luò)層的任務(wù)就是選擇合適的網(wǎng)間路由和交換結(jié)點(diǎn), 確保數(shù)據(jù)及時(shí)傳送。網(wǎng)絡(luò)層將數(shù)據(jù)鏈路層提供的幀組成數(shù)據(jù)包,包中封裝有網(wǎng)絡(luò)層包頭,其中含有邏輯地址信息- -源站點(diǎn)和目的站點(diǎn)地址的網(wǎng)絡(luò)地址。其主要任務(wù)包括 (1)路由處理,即選擇下一跳 (2)添加 IP header(3)計(jì)算 IP header checksum,用于檢測(cè) IP 報(bào)文頭部在傳播過(guò)程中是否出錯(cuò) (4)可能的話,進(jìn)行 IP 分片(5)處理完畢,獲取下一跳的 MAC 地址,設(shè)置鏈路層報(bào)文頭,然后轉(zhuǎn)入鏈路層處理。
IP 頭:
IP 棧基本處理過(guò)程如下圖所示:
首先,ip_queue_xmit(skb)會(huì)檢查skb->dst路由信息。如果沒(méi)有,比如套接字的第一個(gè)包,就使用ip_route_output()選擇一個(gè)路由。
接著,填充IP包的各個(gè)字段,比如版本、包頭長(zhǎng)度、TOS等。
中間的一些分片等,可參閱相關(guān)文檔。基本思想是,當(dāng)報(bào)文的長(zhǎng)度大于mtu,gso的長(zhǎng)度不為0就會(huì)調(diào)用 ip_fragment 進(jìn)行分片,否則就會(huì)調(diào)用ip_finish_output2把數(shù)據(jù)發(fā)送出去。ip_fragment 函數(shù)中,會(huì)檢查 IP_DF 標(biāo)志位,如果待分片IP數(shù)據(jù)包禁止分片,則調(diào)用 icmp_send()向發(fā)送方發(fā)送一個(gè)原因?yàn)樾枰制O(shè)置了不分片標(biāo)志的目的不可達(dá)ICMP報(bào)文,并丟棄報(bào)文,即設(shè)置IP狀態(tài)為分片失敗,釋放skb,返回消息過(guò)長(zhǎng)錯(cuò)誤碼。
接下來(lái)就用 ip_finish_ouput2 設(shè)置鏈路層報(bào)文頭了。如果,鏈路層報(bào)頭緩存有(即hh不為空),那就拷貝到skb里。如果沒(méi),那么就調(diào)用neigh_resolve_output,使用 ARP 獲取。
具體代碼分析如下:
入口函數(shù)是ip_queue_xmit,函數(shù)如下:
發(fā)現(xiàn)調(diào)用了__ip_queue_xmit函數(shù):
發(fā)現(xiàn)調(diào)用了skb_rtable函數(shù),實(shí)際上是開(kāi)始找路由緩存,繼續(xù)看:
發(fā)現(xiàn)調(diào)用ip_local_out進(jìn)行數(shù)據(jù)發(fā)送:
發(fā)現(xiàn)調(diào)用__ip_local_out函數(shù):
發(fā)現(xiàn)返回一個(gè)nf_h(yuǎn)ook函數(shù),里面調(diào)用了dst_output,這個(gè)函數(shù)實(shí)質(zhì)上是調(diào)用ip_finish__output函數(shù):
發(fā)現(xiàn)調(diào)用__ip_finish_output函數(shù):
如果分片就調(diào)用ip_fragment,否則就調(diào)用IP_finish_output2函數(shù):
在構(gòu)造好 ip 頭,檢查完分片之后,會(huì)調(diào)用鄰居子系統(tǒng)的輸出函數(shù) neigh_output 進(jìn)行輸 出。neigh_output函數(shù)如下:
輸出分為有二層頭緩存和沒(méi)有兩種情況,有緩存時(shí)調(diào)用 neigh_h(yuǎn)h_output 進(jìn)行快速輸 出,沒(méi)有緩存時(shí),則調(diào)用鄰居子系統(tǒng)的輸出回調(diào)函數(shù)進(jìn)行慢速輸出。這個(gè)函數(shù)如下:
最后調(diào)用dev_queue_xmit函數(shù)進(jìn)行向鏈路層發(fā)送包,到此結(jié)束。gdb驗(yàn)證如下:
5.2 接收端
IP 層的入口函數(shù)在 ip_rcv 函數(shù)。該函數(shù)首先會(huì)做包括 package checksum 在內(nèi)的各種檢查,如果需要的話會(huì)做 IP defragment(將多個(gè)分片合并),然后 packet 調(diào)用已經(jīng)注冊(cè)的 Pre-routing netfilter hook ,完成后最終到達(dá) ip_rcv_finish 函數(shù)。
ip_rcv_finish 函數(shù)會(huì)調(diào)用 ip_router_input 函數(shù),進(jìn)入路由處理環(huán)節(jié)。它首先會(huì)調(diào)用 ip_route_input 來(lái)更新路由,然后查找 route,決定該 package 將會(huì)被發(fā)到本機(jī)還是會(huì)被轉(zhuǎn)發(fā)還是丟棄:
如果是發(fā)到本機(jī)的話,調(diào)用 ip_local_deliver 函數(shù),可能會(huì)做 de-fragment(合并多個(gè) IP packet),然后調(diào)用 ip_local_deliver 函數(shù)。該函數(shù)根據(jù) package 的下一個(gè)處理層的 protocal number,調(diào)用下一層接口,包括 tcp_v4_rcv (TCP), udp_rcv (UDP),icmp_rcv (ICMP),igmp_rcv(IGMP)。對(duì)于 TCP 來(lái)說(shuō),函數(shù) tcp_v4_rcv 函數(shù)會(huì)被調(diào)用,從而處理流程進(jìn)入 TCP 棧。
如果需要轉(zhuǎn)發(fā) (forward),則進(jìn)入轉(zhuǎn)發(fā)流程。該流程需要處理 TTL,再調(diào)用 dst_input 函數(shù)。該函數(shù)會(huì)
(1)處理 Netfilter Hook
(2)執(zhí)行 IP fragmentation
(3)調(diào)用 dev_queue_xmit,進(jìn)入鏈路層處理流程。
接收相對(duì)簡(jiǎn)單,入口在ip_rcv,這個(gè)函數(shù)如下:
里面調(diào)用ip_rcv_finish函數(shù):
發(fā)現(xiàn)調(diào)用dst_input函數(shù),實(shí)際上是調(diào)用ip_local_deliver函數(shù):
如果分片,就調(diào)用ip_defrag函數(shù),沒(méi)有則調(diào)用ip_local_deliver_finish函數(shù):
發(fā)現(xiàn)調(diào)用ip_protocol_deliver_rcu函數(shù):
調(diào)用完畢之后進(jìn)入tcp棧,調(diào)用完畢,通過(guò)gdb驗(yàn)證如下:

發(fā)表評(píng)論
請(qǐng)輸入評(píng)論內(nèi)容...
請(qǐng)輸入評(píng)論/評(píng)論長(zhǎng)度6~500個(gè)字
您提交的評(píng)論過(guò)于頻繁,請(qǐng)輸入驗(yàn)證碼繼續(xù)
最新活動(dòng)更多
-
3月27日立即報(bào)名>> 【工程師系列】汽車(chē)電子技術(shù)在線大會(huì)
-
4月30日立即下載>> 【村田汽車(chē)】汽車(chē)E/E架構(gòu)革新中,新智能座艙挑戰(zhàn)的解決方案
-
5月15-17日立即預(yù)約>> 【線下巡回】2025年STM32峰會(huì)
-
即日-5.15立即報(bào)名>>> 【在線會(huì)議】安森美Hyperlux™ ID系列引領(lǐng)iToF技術(shù)革新
-
5月15日立即下載>> 【白皮書(shū)】精確和高效地表征3000V/20A功率器件應(yīng)用指南
-
5月16日立即參評(píng) >> 【評(píng)選啟動(dòng)】維科杯·OFweek 2025(第十屆)人工智能行業(yè)年度評(píng)選
推薦專題
- 1 UALink規(guī)范發(fā)布:挑戰(zhàn)英偉達(dá)AI統(tǒng)治的開(kāi)始
- 2 北電數(shù)智主辦酒仙橋論壇,探索AI產(chǎn)業(yè)發(fā)展新路徑
- 3 降薪、加班、裁員三重暴擊,“AI四小龍”已折戟兩家
- 4 “AI寒武紀(jì)”爆發(fā)至今,五類新物種登上歷史舞臺(tái)
- 5 國(guó)產(chǎn)智駕迎戰(zhàn)特斯拉FSD,AI含量差幾何?
- 6 光計(jì)算迎來(lái)商業(yè)化突破,但落地仍需時(shí)間
- 7 東陽(yáng)光:2024年扭虧、一季度凈利大增,液冷疊加具身智能打開(kāi)成長(zhǎng)空間
- 8 地平線自動(dòng)駕駛方案解讀
- 9 封殺AI“照騙”,“淘寶們”終于不忍了?
- 10 優(yōu)必選:營(yíng)收大增主靠小件,虧損繼續(xù)又逢關(guān)稅,能否乘機(jī)器人東風(fēng)翻身?