原文:https://segmentfault.com/a/1190000008836467
原文講的很好,描述了大致的流程。
第一步:網卡到內存
網卡須要有驅動才能工做,驅動是加載到內核中的模塊,負責銜接網卡和內核的網絡模塊,驅動在加載的時候將本身註冊進網絡模塊,當相應的網卡收到數據包時,網絡模塊會調用相應的驅動程序處理數據。segmentfault
1: 數據包從外面的網絡進入物理網卡。若是目的地址不是該網卡,且該網卡沒有開啓混雜模式(是指一臺機器的網卡可以接收全部通過它的數據流,而不論其目的地址是不是它,混雜模式是爲網絡分析而提供的),該包會被網卡丟棄。
2: 網卡將數據包經過DMA的方式寫入到指定的內存地址,該地址由網卡驅動分配並初始化。注: 老的網卡可能不支持DMA,不過新的網卡通常都支持。
3: 網卡經過硬件中斷(IRQ)通知CPU,告訴它有數據來了。
4:CPU根據中斷表,調用已經註冊的中斷函數,這個中斷函數會調到驅動程序(NIC Driver)中相應的函數。
5: 驅動先禁用網卡的中斷,表示驅動程序已經知道內存中有數據了,告訴網卡下次再收到數據包直接寫內存就能夠了,不要再通知CPU了,這樣能夠提升效率,避免CPU不停的被中斷。
6: 啓動軟中斷。這步結束後,硬件中斷處理函數就結束返回了。因爲硬中斷處理程序執行的過程當中不能被中斷,因此若是它執行時間過長,會致使CPU無法響應其它硬件的中斷,因而內核引入軟中斷,這樣能夠將硬中斷處理函數中耗時的部分移到軟中斷處理函數裏面來慢慢處理。
第二步:內核的網絡模塊api
7: 內核中的ksoftirqd進程專門負責軟中斷的處理,當它收到軟中斷後,就會調用相應軟中斷所對應的處理函數,對於上面第6步中是網卡驅動模塊拋出的軟中斷,ksoftirqd會調用網絡模塊的net_rx_action函數。
8: net_rx_action調用網卡驅動裏的poll函數來一個一個的處理數據包。
9: 在pool函數中,驅動會一個接一個的讀取網卡寫到內存中的數據包,內存中數據包的格式只有驅動知道。
10: 驅動程序將內存中的數據包轉換成內核網絡模塊能識別的skb格式,而後調用napi_gro_receive函數。
11: napi_gro_receive會處理GRO相關的內容,也就是將能夠合併的數據包進行合併,這樣就只須要調用一次協議棧。而後判斷是否開啓了RPS,若是開啓了,將會調用enqueue_to_backlog
12: 在enqueue_to_backlog函數中,會將數據包放入CPU的softnet_data結構體的input_pkt_queue中,而後返回,若是input_pkt_queue滿了的話,該數據包將會被丟棄,queue的大小能夠經過net.core.netdev_max_backlog來配置。
13: CPU會接着在本身的軟中斷上下文中處理本身input_pkt_queue裏的網絡數據(調用__netif_receive_skb_core)
14: 若是沒開啓RPS,napi_gro_receive會直接調用__netif_receive_skb_core。
15: 看是否是有AF_PACKET類型的socket(也就是咱們常說的原始套接字),若是有的話,拷貝一份數據給它。tcpdump抓包就是抓的這裏的包。
16: 調用協議棧相應的函數,將數據包交給協議棧處理。
17: 待內存中的全部數據包被處理完成後(即poll函數執行完成),啓用網卡的硬中斷,這樣下次網卡再收到數據的時候就會通知CPU。
第三步:下面送到協議棧的內容就不復制了。網絡