標籤:
轉自:http://linux.chinaunix.net/techdoc/net/2007/01/30/949381.shtml
R.wen (
[email protected]
)
1),Skb_buff
/* To allow 64K frame to be packed as single skb without frag_list */
#define MAX_SKB_FRAGS (65536/PAGE_SIZE + 2)
typedef struct skb_frag_struct skb_frag_t;
struct skb_frag_struct {
struct page *page;
__u16 page_offset;
__u16 size;
};
/* This data is invariant across clones and lives at
* the end of the header data, ie. at skb->end.
*/
struct skb_shared_info {
atomic_t dataref;
unsigned short nr_frags;
unsigned short gso_size;
/* Warning: this field is not always filled in (UFO)! */
unsigned short gso_segs;
unsigned short gso_type;
unsigned int ip6_frag_id;
struct sk_buff *frag_list;
skb_frag_t frags[MAX_SKB_FRAGS];
};
Skb比較複雜的部分在於skb_shared_info部分,alloc_skb()在為資料分配空間的時候,會在這個資料的末尾加上一個skb_shared_info結構,這個結構就是用於scatter/gather IO的實現的。它主要用於提高效能,避免資料的多次拷貝。例如,當使用者用sendmsg分送一個數組結構的資料時,這些資料在物理可能是不連續的(大多數情況),在不支援scatter/gather IO的網卡上,它只能通過重新拷貝,將它重裝成連續的skb(skb_linearize),才可以進行DMA操作。而在支援S/G IO上,它就省去了這次拷貝。
2),網卡(PCI裝置的註冊)
系統啟動的時候,pci會掃描所有的PCI裝置然後根據註冊驅動的id_table,找到相匹配的驅動,實現關聯。當找到匹配的驅動時,它會執行相關驅動程式中的probe函數,而網卡的net_device就是在這個函數裡面初始化的並註冊到核心的。
3),網卡鏈路狀態檢測
當網卡鏈路狀態變化時(如斷開或連上),網卡會通知驅動程式或者由驅動程式去查詢網卡的相關寄存器位(例如在timeout時去查詢這些位),然後由netif_carrier_on/off去通知核心這個變化。
void netif_carrier_on(struct net_device *dev)
{ // test_and_clear_bit - Clear a bit and return its old value
if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state))
linkwatch_fire_event(dev);
if (netif_running(dev))
__netdev_watchdog_up(dev);
}
static inline netif_carrier_off(struct net_device *dev)
{
//test_and_set_bit - Set a bit and return its old value
if (!test_and_set_bit(_ _LINK_STATE_NOCARRIER, &dev->state))
linkwatch_fire_event(dev);
}
這樣,netif_carrier_on會調用linkwatch_fire_event,它會建立一個lw_event結構:
struct lw_event {
struct list_head list;
struct net_device *dev;
};
並將這個結構初始化後(event->dev = dev;)加入到事件隊列中:
spin_lock_irqsave(&lweventlist_lock, flags);
list_add_tail(&event->list, &lweventlist);
spin_unlock_irqrestore(&lweventlist_lock, flags);
然後它調用schedule_work(&linkwatch_work)由核心線程去處理這些事件。它最終由linkwatch_run_queue(void)去完成這些處理工作:
list_for_each_safe(n, next, &head) {
struct lw_event *event = list_entry(n, struct lw_event, list);
struct net_device *dev = event->dev;
…
if (dev->flags & IFF_UP) {
if (netif_carrier_ok(dev)) {
dev_activate(dev);
} else
dev_deactivate(dev);
netdev_state_change(dev);
}
}
可以看到,它的最主要工作之一就是netdev_state_change(dev):
void netdev_state_change(struct net_device *dev)
{
if (dev->flags & IFF_UP) {
raw_notifier_call_chain(&netdev_chain,
NETDEV_CHANGE, dev);
rtmsg_ifinfo(RTM_NEWLINK, dev, 0);
}
}
這個函數通知註冊到netdev_chain鏈表的所有子系統,這個網卡的鏈路狀態有了變化。就是說,如果某個子系統對網卡的鏈路狀態變化感興趣,它就可以註冊到進這個鏈表,在變化產生時,核心便會通知這些子系統。
注意:a. 它只會在網卡狀態為UP時,才會發出通知,因為,如果狀態為DOWN,網卡鏈路的狀態改變也沒什麼意義。
b. 每個見網卡的這些狀態變化的事件lw_event是不會隊列的,即每個網卡只有一個事件的執行個體在隊列中。還有由上面看到的lw_event結構,它只是包含發生狀態變化的網卡裝置,而沒有包含它是鏈上或是斷開的狀狀參數。
07.01.29
網路子系統在鏈路層的收發過程剖析(一)【轉】