海闊天空

          I'm on my way!
          隨筆 - 17, 文章 - 69, 評論 - 21, 引用 - 0
          數據加載中……

          Linux (2.6.24.4)網卡接收數據包的流程

          2.6.24.4內核網絡接收數據包分析
          瀚海書香
          在2.6.24.4中所有的網卡,不管是否支持napi,都是通過struct napi_struct結構進行。所有我們先說一下這個結構。
          struct napi_struct{
             struct list_head poll_list;
             unsigned long state;
             int weight;
             int (*poll)(struct napi_struct *,int);
          }
          對應支持napi的網卡,自己填充這個結構體;而非napi網卡,則使用per cpu的softnet_data>backlog,這個結構的初始化在net_dev_init()中完成。
          我們先說一下非napi機制的網卡:
              網卡接收到數據包后dma到內核空間,然后調用netif_rx()將數據包掛接到softnet_data>input_pkt_queue中, 如果backlog這個napi_struct沒有被調度,則napi_schedule(&backlog).napi_schedule() 會將backlog的poll_list掛接到softnet_data->poll_list上,同時出發軟中斷NET_RX_SOFTIRQ。 NET_RX_SOFTIRQ軟中斷,調用相應的函數net_rx_action()。
          對應napi機制的網卡:
               網卡初始化是會自己初始化一個自己的數據包接收隊列,當有數據包到達時,將數據包dma到自己的數據包隊列中,如果自己的napi沒有調度,則 napi_schedule(mynapi),這里的mynapi是網卡自己的napi_struct.napi_schedule()會將網卡自己的 poll_list掛接到softnet_data->poll_list上,同時出發軟中斷NET_RX_SOFTIRQ。 NET_RX_SOFTIRQ軟中斷,調用相應的函數net_rx_action()。
           
          net_rx_action():
              首先獲取softnet_data->poll_list,通過遍歷poll_list,獲取每個poll_list對應的napi_struct 結構(container_of實現),然后根據napi_struct的weight調用poll函數,如果是非napi網卡,這里的 napi_struct是backlog,所以poll函數就是process_backlog;如果是napi的網卡,則會使自己的poll函數。
          napi網卡的poll函數就是從自己數據包隊列中dequeue出一個skb,然后調用netif_receive_skb().
          非napi的process_backlog會獲取softnet_data->input_pkt_queue,然后對隊列input_pkt_queue進行dequeue操作,獲得一個skb,之后調用netif_receive_skb(skb)。
          netif_receive_skb():
              對skb做一些準備工作,例如設置mac_len等,調用deliver_skb()給所有的注冊ptype_all類型的協議處理handle,然后是 網橋和VLAN的處理,之后會給注冊的相應協議的ptype_base的handle。這里假設是ip協議,則會調用相應的ip協議handle的處理函 數ip_rcv。
          ip_rcv():
              對skb做一些檢查工作,如果skb->users!=1,則clone一個skb,之后會轉入netfilter的 NF_IP_PRE_ROUTING的hook點,調用所有在該點注冊的hook函數。比如說如果開啟了conntrack,則會在這里進行數據包重組。 之后調用ip_rcv_finish().
          ip_rcv_finish():
              首先調用ip_route_input()決定數據包的路由,初始化skb->dst,調用dst_input(skb).
          dst_input():
              實際上是調用skb->dst->input(skb),對應input的初始化在route.c中。如果是發往本地的數據包 dst->input=ip_local_deliver;如果是轉發的數據包dst->input=ip_forward;
          本地流程:
          ip_local_deliver():
              首先是對分片的數據包重組,會轉入netfilter的NF_IP_LOCAL_IN的hook點,調用所有在該點注冊的hook函數。之后會調用ip_local_deliver_finish(),之后就到第四層了。
          轉發流程:
          ip_forward():
              做一些源路由等方面的檢查后,會轉入netfilter的NF_IP_FORWARD的hook點,調用所有在該點注冊的hook函數。之后會調用ip_forward_finish().
          ip_forward_finish():
              調用dst_output().
          dst_output():
              skb->dst->output(skb).一般output=ip_output.
          ip_output():
              設置skb的dev為發包的dev,同時設置skb->protocol,會轉入netfilter的NF_IP_POST_ROUTING的hook點,調用所有在該點注冊的hook函數。之后會調用ip_finish_output().
          ip_finish_output():
              檢查一下數據包是否需要分片,如果需要分片,則進行ip_fragement(),之后調用ip_finish_output2().
          ip_finish_output2():
              根據neighbour,調用dst->neighbour->output.
          到這為止,數據包會經過dev_queue_xmit放入dev的qdisc中。之后就是流控出隊列。

          出處:http://pengliang.cublog.cn

          posted on 2009-12-02 13:32 石頭@ 閱讀(2604) 評論(0)  編輯  收藏 所屬分類: Tcp/Ip

          主站蜘蛛池模板: 土默特左旗| 灵璧县| 勐海县| 晋宁县| 苗栗县| 桓台县| 额济纳旗| 宜兰县| 新巴尔虎右旗| 游戏| 五寨县| 泉州市| 延吉市| 通海县| 靖西县| 宿迁市| 浦东新区| 阳新县| 阳信县| 鄂尔多斯市| 噶尔县| 新丰县| 蓬莱市| 双城市| 盐源县| 德兴市| 高清| 炉霍县| 扎鲁特旗| 昌宁县| 铁岭市| 会理县| 凤城市| 沅江市| 元阳县| 乐都县| 开原市| 榆树市| 乌拉特中旗| 深水埗区| 新巴尔虎左旗|