Linux內(nèi)核源代碼:tcp/ip協(xié)議棧的調(diào)用
這里共維護(hù)了三個(gè)隊(duì)列:prequeue、backlog、receive_queue,分別為預(yù)處理隊(duì)列,后備隊(duì)列和接收隊(duì)列,在連接建立后,若沒有數(shù)據(jù)到來,接收隊(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來實(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è)接收的過程也就完成了。
用一張函數(shù)間的相互調(diào)用圖可以表示:
通過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)文頭部在傳播過程中是否出錯(cuò) (4)可能的話,進(jìn)行 IP 分片(5)處理完畢,獲取下一跳的 MAC 地址,設(shè)置鏈路層報(bào)文頭,然后轉(zhuǎn)入鏈路層處理。
IP 頭:
IP ;咎幚磉^程如下圖所示:
首先,ip_queue_xmit(skb)會(huì)檢查skb->dst路由信息。如果沒有,比如套接字的第一個(gè)包,就使用ip_route_output()選擇一個(gè)路由。
接著,填充IP包的各個(gè)字段,比如版本、包頭長度、TOS等。
中間的一些分片等,可參閱相關(guān)文檔。基本思想是,當(dāng)報(bào)文的長度大于mtu,gso的長度不為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,返回消息過長錯(cuò)誤碼。
接下來就用 ip_finish_ouput2 設(shè)置鏈路層報(bào)文頭了。如果,鏈路層報(bào)頭緩存有(即hh不為空),那就拷貝到skb里。如果沒,那么就調(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í)際上是開始找路由緩存,繼續(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ù)如下:
輸出分為有二層頭緩存和沒有兩種情況,有緩存時(shí)調(diào)用 neigh_h(yuǎn)h_output 進(jìn)行快速輸 出,沒有緩存時(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 來更新路由,然后查找 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 來說,函數(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ì)簡單,入口在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ù),沒有則調(diào)用ip_local_deliver_finish函數(shù):
發(fā)現(xiàn)調(diào)用ip_protocol_deliver_rcu函數(shù):
調(diào)用完畢之后進(jìn)入tcp棧,調(diào)用完畢,通過gdb驗(yàn)證如下:

發(fā)表評(píng)論
請(qǐng)輸入評(píng)論內(nèi)容...
請(qǐng)輸入評(píng)論/評(píng)論長度6~500個(gè)字
最新活動(dòng)更多
-
6月20日立即下載>> 【白皮書】精準(zhǔn)測(cè)量 安全高效——福祿克光伏行業(yè)解決方案
-
7月3日立即報(bào)名>> 【在線會(huì)議】英飛凌新一代智能照明方案賦能綠色建筑與工業(yè)互聯(lián)
-
7月22-29日立即報(bào)名>> 【線下論壇】第三屆安富利汽車生態(tài)圈峰會(huì)
-
7.30-8.1火熱報(bào)名中>> 全數(shù)會(huì)2025(第六屆)機(jī)器人及智能工廠展
-
7月31日免費(fèi)預(yù)約>> OFweek 2025具身機(jī)器人動(dòng)力電池技術(shù)應(yīng)用大會(huì)
-
免費(fèi)參會(huì)立即報(bào)名>> 7月30日- 8月1日 2025全數(shù)會(huì)工業(yè)芯片與傳感儀表展
推薦專題
- 1 AI 眼鏡讓百萬 APP「集體失業(yè)」?
- 2 大廠紛紛入局,百度、阿里、字節(jié)搶奪Agent話語權(quán)
- 3 深度報(bào)告|中國AI產(chǎn)業(yè)正在崛起成全球力量,市場(chǎng)潛力和關(guān)鍵挑戰(zhàn)有哪些?
- 4 上海跑出80億超級(jí)獨(dú)角獸:獲上市公司戰(zhàn)投,干人形機(jī)器人
- 5 國家數(shù)據(jù)局局長劉烈宏調(diào)研格創(chuàng)東智
- 6 下一代入口之戰(zhàn):大廠為何紛紛押注智能體?
- 7 百億AI芯片訂單,瘋狂傾銷中東?
- 8 Robotaxi新消息密集釋放,量產(chǎn)元年誰在領(lǐng)跑?
- 9 一文看懂視覺語言動(dòng)作模型(VLA)及其應(yīng)用
- 10 格斗大賽出圈!人形機(jī)器人致命短板曝光:頭腦過于簡單