Linux-2.6.25 TCPIP函數調用大致流程

Linux-2.6.25 TCPIP函數調用大致流程
學習目的,隨手筆記。函數和文字說明會不斷補充更新。

Changelog
2008.10.08??? 最近找工作忙。暫時緩緩

插口層
系統調用
send
??? sys_send
??????? sys_sendto
sendto
??? sys_sendto
??????? sock_sendmsg
sendmsg
??? sys_sendmsg
??????? sock_sendmsg
write
??? sys_write
??????? vfs_write
??????????? file->f_op->write = do_sync_write
??????????????? filp->f_op->aio_write = sock_aio_write
??????????????????? do_sock_write
??????????????????????? __sock_sendmsg
writev
??? sys_writev
??????? vfs_writev
??????????? do_readv_writev
??????????????? do_sync_readv_writev
??????????????????? sock_aio_write
??????????????????????? do_sock_write
??????????????????????????? __sock_sendmsg
recv
??? sys_recv
??????? sys_recvfrom
recvfrom
??? sys_recvfrom
??????? sock_recvmsg
recvmsg
??? sys_recvmsg
??????? sock_recvmsg
read
??? sys_read
??????? vfs_read
??????????? file->f_op->read= do_sync_read
??????????????? filp->f_op->aio_read= sock_aio_read
??????????????????? do_sock_read
??????????????????????? __sock_recvmsg
readv
??? sys_readv
??????? vfs_readv
??????????? do_readv_readv
??????????????? do_sync_readv_readv
??????????????????? sock_aio_read
??????????????????????? do_sock_read
??????????????????????????? __sock_recvmsg
socket
listen
connect
bind
select
close
shutdown
ioctl
getsockname
getpeername
setsockopt
getsockopt

內部實現函數
sock_sendmsg
??? __sock_sendmsg

__sock_sendmsg
??? sock->ops->sendmsg
對于TCP就是tcp_sendmsg,否則就是inet_sendmsg。后者調用sk->sk_prot->sendmsg,會繼續分用為udp_sendmsg或raw_sendmsg函數

sock_recvmsg
??? __sock_recvmsg

__sock_recvmsg
??? sock->ops->recvmsg = sock_common_recvmsg
??? sock_common_recvmsg對于不同協議,是tcp_recvmsg,udp_sendmsg或raw_sendmsg函數。

運輸層

TCP
系統調用sys_connect間接調用了tcp_v4_connect
tcp_v4_connect
??? ip_route_connect(尋找路由)
??? __ip_route_output_key
??? ip_route_output_flow★
??? tcp_connect(構造一個SYN并發送)
??????? tcp_transmit_skb
??????? inet_csk_reset_xmit_timer(啟動一個超時定時器,等待SYN+ACK)


TCP的寫函數最終都調用了tcp_sendmsg
tcp_sendmsg★
??? __tcp_push_appending_frames
??????? tcp_write_xmit
??????????? tcp_transmit_skb
??? tcp_push_one
??????? tcp_transmit_skb???
??? tcp_push
??????? __tcp_push_pending_frames
???????
TCP發送數據共有三種途徑__tcp_push_appending_frames,tcp_push_one,tcp_push,其中tcp_push調用了__tcp_push_pending_frames。到底調用哪個或哪些函數取決于是否有PUSH標志、NAGLE是否開啟、和一些其他情況。
__tcp_push_appending_frames是試圖一次發送完緩存隊列中所有的skb。tcp_push_one先計算擁塞窗口,然后只發送窗口大小的數據,如果窗口大小為0,則不發送任何數據。


TCP實際的發送函數,tcp_transmit_skb
/* This routine actually transmits TCP packets queued in by
* tcp_do_sendmsg().? This is used by both the initial
* transmission and possible later retransmissions.
* All SKB's seen here are completely headerless.? It is our
* job to build the TCP header, and pass the packet down to
* IP so it can do the same plus pass the packet off to the
* device.
*
* We are working here with either a clone of the original
* SKB, or a fresh unique copy made by the retransmit engine.
*/
tcp_transmit_skb
??? build包頭
??? icsk->icsk_af_ops->queue_xmit = ip_queue_xmit★


硬件->IP層->運輸層收到數據,添加到對應的SOCKET緩沖區中,回復ACK
由ip_rcv間接調用
tcp_v4_rcv
??? __inet_lookup(根據一些參數,查找sock結構)
??????? __inet_lookup_established(在已經建立的連接中找,通過inet_lhashfn在哈希表中查找)
??????? __inet_lookup_listener(在監聽中的Socket中找,通過inet_lhashfn在哈希表中查找)
??? tcp_v4_do_rcv
??????? tcp_rcv_established(ESTABLISHED)★
??????? tcp_child_process
??????????? tcp_rcv_state_process
??????? tcp_rcv_state_process(除ESTABLISHED和TIME_WAIT之外)★
??? tcp_prequeue(見后面詳細解釋)
??????? sk->sk_backlog_rcv = tcp_v4_do_rcv(又回到開頭)
??? sk_add_backlog(見后面詳細解釋)
??? tcp_timewait_state_process(TIME_WAIT)
??? tcp_v4_timewait_ack(TIME_WAIT)
??????? tcp_v4_send_ack(發送ACK)

sock結構被初始化的時候,發送和接收數據的緩沖隊列也被初始化完成,接收數據用到以下三個隊列:
sk->receive_queue
sk->prequeue
sk->sk_backlog
sk->prequeue:如果sk沒有被用戶態程序鎖定,則先進入prequeue
sk->receive_queue:接收到數據包的sk_buff鏈表隊列,如果數據包過多,造成receive_queue滿,或者sock被用戶程序鎖定,將轉入sk_backlog
sk->sk_backlog:當sock_owned_by_user函數返回真時候,(sk)->sk_lock.owner被鎖定,使用sk_add_backlog()函數(該函數實現非常簡單,只是一個為鏈表添加節點的動作)將SKB加入這個后備隊列。


tcp_rcv_established
TCP接受里面最主要的就是tcp_rcv_established和tcp_rcv_state_process了
tcp_rcv_established★
??? if(fast path)
??????? 檢查包頭各字段
??????? tcp_ack(處理CK)
??????? tcp_data_snd_check(發送ACK)
??????? __skb_pull(騰出空間)
??????? __skb_queue_tail(把數據追加到接受緩沖區)
??? else(slow path)
??????? tcp_data_queue
??????????? 對滑動窗口、序號做出處理
??????????? __skb_pull
??????????? __skb_queue_tail
??????????? tcp_event_data_recv(更新狀態)


tcp_rcv_state_process
TCP協議的狀態機,狀態轉移函數。ESTABLISHED和TIME_WAIT狀態之外的其他狀態都會調用此函數
tcp_rcv_state_process★
??? icsk->icsk_af_ops->conn_request(是tcp_v4_conn_request,LISTEN狀態)
??????? tcp_v4_send_synack(發送SYN+ACK)
??????????? ip_build_and_send_pkt
??????????????? ip_local_out
??????????????????? __ip_local_out
??????????????????????? nf_hook(dst_output)
??????????????????? dst_output
??? tcp_rcv_synsent_state_process(SYN_SENT)
??? tcp_reset
??? tcp_ack(收到ACK)
??? tcp_set_state(SYN_RECV->ESTABLISHED或者FIN_WAIT1->FIN_WAIT2)
??? tcp_time_wait(CLOSING->TIME_WAIT)
??? tcp_update_metrics(LAST_ACK)
??? ...(都是和TCP協議狀態轉移相關的東西,這里目的是打通上下,以后慢慢分析)


還有兩個出鏡率較高的函數tcp_v4_send_reset和tcp_v4_send_ack
tcp_v4_send_reset(發送RST)
??? ip_send_reply
??????? ip_route_output_key
??????? ip_push_pending_frames


tcp_v4_send_ack(發送ACK)
??? ip_send_reply
??????? ip_route_output_key
??????? ip_push_pending_frames


用戶子上而下的讀函數都間接的調用了tcp_recvmsg
tcp_recvmsg★
skb_copy_datagram_iovec
tcp_recv_urg(接受一個字節的URG數據)

UDP
UDP的寫函數都調用了udp_sendmsg
udp_sendmsg★
??? ip_route_output_flow
??? ip_append_data
??? udp_flush_pending_frames
??????? ip_flush_pending_frames
??? udp_push_pending_frames
??????? ip_push_pending_frames

硬件->IP層->運輸層收到數據,添加到對應的SOCKET緩沖區中
由ip_rcv間接調用
udp_rcv
??? __udp4_lib_rcv
??????? if(是多播或廣播)
??????????? __udp4_lib_mcast_deliver
??????????????? udp_queue_rcv_skb(對每個需要接受的UDP SOCKET緩沖調用)
??????? __udp4_lib_lookup
??????? udp_queue_rcv_skb


把數據塊sk_buff放到一個sock結構的接受緩存的末尾中
udp_queue_rcv_skb
??? sock_queue_rcv_skb
??????? skb_queue_tail

用戶子上而下的讀函數都間接的調用了udp_recvmsg
udp_recvmsg★
__skb_recv_datagram
skb_copy_datagram_iovec
skb_copy_and_csum_datagram_iovec

原始套接字
RAW Socket的寫函數都調用了raw_sendmsg
raw_sendmsg★
??? ip_route_output_flow
??? if(設置了IP_HDRINCL選項,即自己構造ip頭部)
??????? raw_send_hdrinc★
??? else
??????? ip_append_data
??????? ip_flush_pending_frames或
??????? ip_push_pending_frames


自底向上的收包
raw_rcv
由ip_forward調用ip_call_ra_chain,然后再調用的raw_rcv
raw_rcv
sock_queue_rcv_skb
skb_queue_tail
sk->sk_data_ready = sock_def_readable
waitqueue_active
sk_wake_async


用戶子上而下的讀函數都間接的調用了raw_recvmsg
raw_recvmsg★
skb_recv_datagram
__skb_recv_datagram
wait_for_packet(如果沒有數據,則調用此函數等待數據)


ICMP
在任何需要發送ICMP報文的時候都會調用此函數
icmp_send
??? __ip_route_output_key
??????? ip_route_output_slow
??? ip_route_output_key
??????? ip_route_output_flow
??? icmp_push_reply???
??????? ip_append_data
??????? ip_flush_pending_frames或
??????? ip_push_pending_frames


硬件->IP層->運輸層收到ICMP數據,作出處理邏輯
由ip_rcv間接調用
icmp_rcv
??? 完全就是icmp協議的處理邏輯,通過函數指針icmp_pointers[icmph->type].handler調用了一下函數中的某一個
??? icmp_discard
??? icmp_unreach
??? icmp_redirect
??? icmp_timestamp
??? icmp_address
??? icmp_address_reply
??? icmp_echo


網絡層
IP發送
網絡層中主要的發送函數有以下三個:ip_push_pending_frames,ip_queue_xmit,raw_send_hdrinc
ip_push_pending_frames★
將所有pending狀態的IP分組組合成一個IP分組,并發送
??? ip_local_out


ip_queue_xmit★
??? ip_route_output_flow(找路由)
??? ip_local_out


raw_send_hdrinc★
??? NF_HOOK(dst_output)


ip_local_out★
??? __ip_local_out
??????? nf_hook(dst_output)
??? dst_output

路由選擇
ip_route_output_flow★
??? __ip_route_output_key
??????? ip_route_output_slow

路由選擇
ip_route_output_slow★
??? fib_lookup
??? ip_mkroute_output
??????? __mkroute_output
??????? rt_hash
??????? rt_intern_hash
??????????? arp_bind_neighbour
??????????????? __neigh_lookup_errno
??????????????????? neigh_lookup
??????????????????? neigh_create


dst_output★
??? dst->output = ip_output
??? NF_HOOK_COND(ip_finish_output)
??????? dst_output
??????? ip_fragment
??????? ip_finish_output2
??????????? neigh_hh_output
??????????????? hh->hh_output = dev_queue_xmit★
??????????? dst->neighbour->output = neigh_resolve_output
??????????????? neigh->ops->queue_xmit = dev_queue_xmit★

IP接受
接收IPv4包,由netif_rx間接調用
ip_rcv★
??? NF_HOOK
??? ip_rcv_finish
??????? ip_route_input
??????? dst_input
??????????? dst->input(可能是ip_local_deliver或ip_forward)
??????????? if(是發給本地的包)
??????????????? dst->input是ip_local_deliver
??????????????????? NF_HOOK
??????????????????? ip_local_deliver_finish
??????????????????? ipprot->handler(可能是tcp_v4_rcv,udp_rcv,icmp_rcv,igmp_rcv)
??????????? else
??????????????? dst->input是ip_forward

更新路由
ip_route_input★
??? ip_route_input_mc(多播)
??????? rt_hash
??????? rt_intern_hash
??? ip_route_input_slow(其它)
??????? ip_mkroute_input
??????????? __mkroute_input
??????????? rt_hash
??????????? rt_intern_hash
每收到一個IP報文都會調用此函數更新路由表。ip_route_input函數的上半部分是在hash table尋找路由項,如果找到就返回。找不到才會調用后面的ip_route_input_mc或ip_route_input_slow來更新路由表。


轉發
ip_forward★
ip_call_ra_chain
raw_rcv★
??? xfrm4_route_forward(處理路由)
??????? xfrm_route_forward
??????????? __xfrm_route_forward
??????????????? xfrm_lookup
??????????????????? __xfrm_lookup
??????????????????????? xfrm_find_bundle
??????????????????????????? afinfo->find_bundle = __xfrm4_find_bundle
??????????????????????? xfrm_bundle_create
??????????????????????????? xfrm_dst_lookup
??????????????????????????????? afinfo->dst_lookup = xfrm4_dst_lookup
??????????????????????????????????? __ip_route_output_key
??????????????????????????????????????? ip_route_output_slow★
??? 處理各個參數(在一定條件下發送ICMP)
??? ip_decrease_ttl(減少TTL)
??? NF_HOOK(ip_forward_finish)
??????? dst_output


鏈路層
接收幀
由硬件驅動在中斷處理程序中直接調用netif_rx
netif_rx★
??? if(netpoll_rx函數與把數據拿走)
??????? return
??? __skb_queue_tail(把所有收到的數據保存起來)
??? napi_schedule
??????? __napi_schedule
??????????? __raise_softirq_irqoff(NET_RX_SOFTIRQ);

在net_dev_init函數中初始化了軟中斷:
open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);
所以NET_RX_SOFTIRQ中斷的處理函數是net_rx_action,NET_TX_SOFTIRQ中斷的處理函數是net_tx_action。需要讓上層接收數據時,只要觸發相應的軟中斷,如__raise_softirq_irqoff(NET_RX_SOFTIRQ)。內核就會在適當時機執行do_softirq來處理pending的軟中斷。


net_rx_action★
??? n->poll = process_backlog
??????? netif_receive_skb
??????????? pt_prev->func = ip_rcv★(在這里完成了交接)
??? __raise_softirq_irqoff(NET_RX_SOFTIRQ)


發送幀
dev_queue_xmit★
??? rcu_read_lock_bh
??? if(設備有發送隊列)
??????? q->enqueue(將數據追加到發送隊列,軟中斷處理函數net_tx_action會執行真正的發送工作)
??? else
??????? dev_hard_start_xmit
??????????? dev->hard_start_xmit = el_start_xmit★
??????????????? 調用outw匯編指令發送數據,夠底層了
??? rcu_read_unlock_bh


net_tx_action★
??? __kfree_skb(釋放已發送的,此時中斷由dev_kfree_skb_irq函數發起)
??? qdisc_run
??????? __qdisc_run
qdisc_restart
??????????????? dev_hard_start_xmit★
??????????? netif_schedule
??? netif_schedule


netif_schedule★
??? __netif_schedule
??????? raise_softirq_irqoff(NET_TX_SOFTIRQ)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/445116.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/445116.shtml
英文地址,請注明出處:http://en.pswp.cn/news/445116.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python(28)-文件,os模塊

文件1. 文件2. 文件的基本操作3. 讀取文件open()3.1 文件指針: 標記從哪一個位置開始讀取數據.3.2 文件的打開方式mode3.3 文件按行讀取3.3.1 readline()3.3.2 readlines()4.文件輸出f.write(),print()5.文件復制5.1 小文件復制(搬家)5.2 大文件復制&…

IOCP的程序

C代碼 #include <winsock2.h> #include <mswsock.h> #include <windows.h> #include <stdio.h> #include <stdlib.h> #include <assert.h> #include "vld.h" #pragma message("automatic link to ws2_32.lib and…

PaperNotes(3)-圖像分割-RCNN-FCN-Boxsup

圖像分割算法對比小結1.{基本概念}2.{R-CNN}2.1R-CNN 網絡結構選擇性搜索算法為什么選擇SVM作分類器邊框回歸2.2{R-CNN 訓練}2.3{R-CNN實驗結果}2.4{R-CNN語義分割}2.5{補充材料}2.5.1{R-CNN建議區域放縮}2.5.2{IOU閾值設置不一樣的原因}2.5.3{Bounding-box回歸修正}2.6{R-CNN存…

Python模塊(3)--PIL 簡易使用教程

PIL模塊-用與記1.圖片導入Image.open()2.圖像顯示.show()4.查看圖片屬性.format,.size,.mode3.圖像格式轉換.convert()4.圖像模式“L”&#xff0c;“RGB”,"CYMK"5. 圖片旋轉.rotate()旋轉方式1&#xff1a;旋轉不擴展旋轉方式2&#xff1a;旋轉擴展旋轉方式3&#…

日志級別 debug info warn eirror fatal

日志級別 debug info warn eirror fatal 軟件中總免不了要使用諸如 Log4net, Log4j, Tracer 等東東來寫日志&#xff0c;不管用什么&#xff0c;這些東東大多是大同小異的&#xff0c;一般都提供了這樣5個日志級別&#xff1a; Debug Info Warn Error Fatal一個等級比一個高&…

輸入輸出系統

I/O設備&#xff1a;輸入輸出和存儲功能的設備 I/O設備的分類 按傳輸的速度&#xff1a; 低速設備&#xff08;如鍵盤、鼠標、語音輸入輸出設備&#xff09; 中速設備&#xff08;如行式打印機、激光打印機等&#xff09; 高速設備&#xff08;如磁帶機、磁盤機、光盤機等&…

vue2源碼解析---v-model雙向數據綁定

什么是v-model v-model 是 Vue 中的一個指令&#xff0c;用于實現表單元素與 Vue 實例中數據的雙向綁定。這意味著當表單元素的值發生變化時&#xff0c;Vue 實例中的數據也會隨之更新 工作原理 生成ast樹 本質上是語法糖 結合了v-bind和v-on兩個指令 示例代碼 new Vue({e…

php收集的精典代碼

1. οncοntextmenu"window.event.return&#xff06;#118aluefalse" 將徹底屏蔽鼠標右鍵 <table border οncοntextmenureturn(false)><td>no</table> 可用于Table 2. <body onselectstart"return false"> 取消選取、防止復制…

python外卷(7)--glob

glob模塊1.glob.glob()2.對比os.listdir()glob是python自帶的一個操作文件的模塊&#xff0c;可用于查找 指定路徑 中 匹配的 文件。1.glob.glob() 下面是一個測試文件路徑&#xff1a; (base) pppp-System-Product-Name:~/Desktop/test_glob$ tree . ├── a │ ├── 1…

Sublime Text 2配置強大的IDE開發環境,運行java

Sublime Text 2是我無意中發現的、據說十分強大的、便捷的編輯器&#xff0c;許多程序員都投入到Sublime Text 2的懷抱中。 1 配置java開發環境的方法如下&#xff1a; 在jdk安裝目錄下的bin文件夾下新建一個bat格式的文件&#xff0c;文件命為javacexec.bat。 如果是在Wind…

thinkphp的快捷方法實例化對象

D、F、S、C、L、A、I 他們都在functions.php這個文件家 下面我分別說明一下他們的功能 D&#xff08;&#xff09; 加載Model類 M&#xff08;&#xff09; 加載Model類 A&#xff08;&#xff09; 加載Action類 L&#xff08;&#xff09; 獲取語言定義 C&#xff08;&#xf…

Python外卷(8)--pdist, squareform

pdist, squareform1.pdist, squareform使用例子2.通過矩陣的四則運算實現上述pdist, squareformscipy.spatial.distance 距離計算庫中有兩個函數&#xff1a;pdist, squareform&#xff0c;用于計算樣本對之間的歐式距離&#xff0c;并且將樣本間距離用方陣表示出來。&#xff…

模擬進程調度

功能 data.h #ifndef _Data_h_ #define _Data_h_#include <stdio.h> #include <stdlib.h> #include <string.h>#define ElemType PCB #define Status int #define OK 1 #define ERROR 0 #define TimeSlice 1 #define Infinity 10 //INT_MAX#define NAME_M…

gdb調試多進程和多線程命令

1. 默認設置下&#xff0c;在調試多進程程序時GDB只會調試主進程。但是GDB&#xff08;>V7.0&#xff09;支持多進程的 分別以及同時 調試&#xff0c;換句話說&#xff0c;GDB可以同時調試多個程序。只需要設置follow-fork-mode(默認值&#xff1a;parent)和detach-on-fork…

python外卷(10)--取整

"取整"那些事1.python 內置函數1.1int()--向下取整1.2round()--四舍五入2.math模塊取整函數2.1 math.floor()--向下取整2.2 math.ceil()--向上取整2.3 math.modf()--分別取小數部分和整數部分3.numpy模塊取整函數3.1 numpy.floor()--向下取整3.2 numpy.ceil()--向上取…

模擬銀行家算法

介紹 data.h #ifndef _Data_h_ #define _Data_h_#include <stdio.h> #include <stdlib.h> #include <string.h>#define ElemType PCB #define Status int #define true 1 #define false 0 #define OK 1 #define ERROR 0 #define RESOURCE_NUM …

Lua 與 C混合編程 .

本文通過程序實例說明C調用lua腳本和lua調用C的方法: 先建立一個 test.c文件: #include <stdio.h> #include <stdlib.h> #include "lua.h" #include "lualib.h" #include "lauxlib.h" #pragma comment(lib, "lua5.1.lib&qu…

模擬固定分區分配

介紹 data.h #ifndef _Data_h_ #define _Data_h_#include <stdio.h> #include <stdlib.h> #include <string.h> #define LIST_INIT_SIZE 10 #define LISTINCREMENT 2 #define true 1 #define false 0 #define PCBType PCB #define Status int…

Linux下的lua和boost c++的搭建和安裝

先下載lua &#xff0c;boost c http://www.lua.org/versions.html#5.2 http://www.boost.org/ http://sourceforge.net/projects/luabind/ 1. 安裝lua [rootlocalhost ~]#tar zxvf lua-5.1.2.tar.gz -C /usr/local [rootlocalhost ~]# cd /usr/local/ [rootlocalhost lo…

模擬基本分頁存儲

介紹 data.h #ifndef _Data_h_ #define _Data_h_#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h>#define LIST_INIT_SIZE 10 #define LISTINCREMENT 2 #define true 1 #define false 0 #define PCBType PC…