- 網(wǎng)絡(luò)驅(qū)動的核心:
1、就是初始化 net_device 結(jié)構(gòu)體中的各個成員變量,
2、然后將初始化完成以后的 net_device 注冊到 Linux 內(nèi)核中
1、網(wǎng)絡(luò)設(shè)備(用net_device結(jié)構(gòu)體)
/*
@ net_device 結(jié)構(gòu)體
*/
struct net_device {
char name[IFNAMSIZ]; /*網(wǎng)絡(luò)設(shè)備名字*/
struct hlist_node name_hlist;
char *ifalias;
unsigned long mem_end;/*共享內(nèi)存結(jié)束地址*/
unsigned long mem_start;/*共享內(nèi)存起始地址*/
unsigned long base_addr; /*網(wǎng)絡(luò)設(shè)備IO地址*/
int irq; /*網(wǎng)絡(luò)設(shè)備的中斷號*/
atomic_t carrier_changes;
unsigned long state;
struct list_head dev_list; /*全局網(wǎng)絡(luò)設(shè)備列表*/
struct list_head napi_list;/*napi機制==napi設(shè)備的列表入口*/
struct list_head unreg_list;/*注銷網(wǎng)絡(luò)設(shè)備的列表入口*/
struct list_head close_list; /*關(guān)閉的網(wǎng)絡(luò)設(shè)備列表入口*/
...
const struct net_device_ops *netdev_ops; /*網(wǎng)絡(luò)設(shè)備的操作集函數(shù)*/
const struct ethtool_ops *ethtool_ops; /*網(wǎng)絡(luò)管理工具相關(guān)函數(shù)集*/
#ifdef CONFIG_NET_SWITCHDEV
const struct swdev_ops *swdev_ops;
#endif
const struct header_ops *header_ops; /*頭部的相關(guān)操作函數(shù)集,比如創(chuàng)建、解析、緩沖等*/
unsigned int flags; /*網(wǎng)絡(luò)接口標志*/
...
unsigned char if_port; /*指定接口的端口類型*/
unsigned char dma; /*網(wǎng)絡(luò)設(shè)備所使用的DMA通道*/
unsigned int mtu; /*網(wǎng)絡(luò)最大傳輸單元*/
unsigned short type;/*指定 ARP模塊的類型*/
unsigned short hard_header_len;
unsigned short needed_headroom;
unsigned short needed_tailroom;
/* Interface address info. */
unsigned char perm_addr[MAX_ADDR_LEN]; /*永久的硬件地址*/
unsigned char addr_assign_type;
unsigned char addr_len; /*硬件地址長度。*/
...
/*
* Cache lines mostly used on receive path (including
eth_type_trans())
*/
unsigned long last_rx; /*最后接收的數(shù)據(jù)包時間戳,記錄的是jiffies*/
/* Interface address info used in eth_type_trans() */
unsigned char *dev_addr; /*硬件地址,是當前分配的MAC 地址*/
#ifdef CONFIG_SYSFS
struct netdev_rx_queue *_rx; /*接收隊列*/
unsigned int num_rx_queues; /*接收隊列數(shù)量*/
unsigned int real_num_rx_queues; /*當前活動的隊列數(shù)量*/
#endif
...
/*
* Cache lines mostly used on transmit path
*/
struct netdev_queue *_tx /*發(fā)送隊列*/ ____cacheline_aligned_in_smp;
unsigned int num_tx_queues; /*發(fā)送隊列數(shù)量*/
unsigned int real_num_tx_queues; /*當前有效的發(fā)送隊列數(shù)量。*/
struct Qdisc *qdisc;
unsigned long tx_queue_len;
spinlock_t tx_global_lock;
int watchdog_timeo;
...
/* These may be needed for future network-power-down code. *
/*
* trans_start here is expensive for high speed devices on S
* please use netdev_queue->trans_start instead.
*/
unsigned long trans_start; /*最后的數(shù)據(jù)包發(fā)送的時間戳,記錄的是jiffies*/
...
struct phy_device *phydev; /*對應(yīng)的PHY 設(shè)備*/
struct lock_class_key *qdisc_tx_busylock;
};
/*
@ 申請net_device ===11111111111111111111111111111111
*/
#define alloc_netdev(sizeof_priv, name, name_assign_type, setup) \
alloc_netdev_mqs(sizeof_priv, name, name_assign_type, setup, 1, 1)
/*可以看出alloc_netdev的本質(zhì)是alloc_netdev_mqs函數(shù)*/
/*
@ sizeof_priv:私有數(shù)據(jù)塊大小。
@ name:設(shè)備名字
@ setup:回調(diào)函數(shù),初始化設(shè)備的設(shè)備后調(diào)用此函數(shù)
@ txqs:分配的發(fā)送隊列數(shù)量
@ rxqs:分配的接收隊列數(shù)量
@ 如果申請成功的話就返回申請到的 net_device指針,失敗的話就返回NULL。
*/
struct net_device * alloc_netdev_mqs ( int sizeof_priv, const char *name,
void (*setup) (struct net_device *))
unsigned int txqs,
unsigned int rxqs);
/*事實上網(wǎng)絡(luò)設(shè)備有多種,大家不要以為就只有以太網(wǎng)一種,比如光纖分布式數(shù)據(jù)接口(FDDI)、以太網(wǎng)設(shè)備(Ethernet)、紅外數(shù)據(jù)接口(InDA)、高性能并行接口(HPPI)、CAN 網(wǎng)絡(luò)等*/
/*
@ 本章講解的以太網(wǎng),針對以太網(wǎng)封裝的net_device申請函數(shù)是alloc_etherdev,這也是一個宏
@ alloc_etherdev 最終依靠的是 alloc_etherdev_mqs 函數(shù)
*/
#define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)
#define alloc_etherdev_mq(sizeof_priv, count) alloc_etherdev_mqs(sizeof_priv, count, count)
/*
@ alloc_etherdev 最終依靠的是 alloc_etherdev_mqs 函數(shù)
*/
struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs, unsigned int rxqs)
{
return alloc_netdev_mqs(sizeof_priv, "eth%d", NET_NAME_UNKNOWN,ether_setup, txqs, rxqs); /*申請net_device,*/
}
/*
@ 對是申請的設(shè)備進行初始化
@ ether_setup函數(shù)會對 net_device做初步的初始化
*/
void ether_setup(struct net_device *dev)
{
dev->header_ops = ð_header_ops;
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->mtu = ETH_DATA_LEN;
dev->addr_len = ETH_ALEN;
dev->tx_queue_len = 1000; /* Ethernet wants good queues *
dev->flags = IFF_BROADCAST|IFF_MULTICAST;
dev->priv_flags |= IFF_TX_SKB_SHARING;
eth_broadcast_addr(dev->broadcast);
}
/*
@ 刪除net_device(net_device)=====22222222222222222222222222222222222222222
@ 注銷網(wǎng)絡(luò)驅(qū)動的時候需要釋放掉前面已經(jīng)申請到的 net_device
@ dev:要釋放掉的net_device指針。
@ 返回值:無
*/
void free_netdev(struct net_device *dev)
/*
@ 注冊net_device ==33333333333333333333333333333333333333
@ net_device申請并初始化完成以后就需要向內(nèi)核注冊net_device,要用到函數(shù)register_netdev
@ dev:要注冊的net_device指針
@ 返回值:0 注冊成功,負值 注冊失敗。
*/
int register_netdev(struct net_device *dev)
/*
@ 注銷net_device ==44444444444444444444444444444444444
@ dev:要注銷的net_device指針
@ 返回值:無
*/
void unregister_netdev(struct net_device *dev)
2、網(wǎng)絡(luò)設(shè)備的操作集( net_device_ops結(jié)構(gòu)體 )
/*
@ 定義在 include/linux/netdevice.h 文件中
@ net_device_ops結(jié)構(gòu)體里面都是一些以“ndo_”開頭的函數(shù),這些函數(shù)就需要網(wǎng)絡(luò)驅(qū)動編寫人員去實現(xiàn)
@ net_device_ops 結(jié)構(gòu)體
*/
struct net_device_ops {
int (*ndo_init)(struct net_device *dev); /*當?shù)谝淮巫跃W(wǎng)絡(luò)設(shè)備的時候此函數(shù)會執(zhí)行*/
void (*ndo_uninit)(struct net_device *dev); /*卸載網(wǎng)絡(luò)設(shè)備的時候此函數(shù)會執(zhí)行*/
int (*ndo_open)(struct net_device *dev); /*打開網(wǎng)絡(luò)設(shè)備的時候此函數(shù)會執(zhí)行,網(wǎng)絡(luò)驅(qū)動程序需要實現(xiàn)此函數(shù),非常重要*/
int (*ndo_stop)(struct net_device *dev);
netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb,
struct net_device *dev); /*當需要發(fā)送數(shù)據(jù)的時候此函數(shù)就會執(zhí)行,此函數(shù)有一個參數(shù)為 sk_buff結(jié)構(gòu)體指針,sk_buff結(jié)構(gòu)體在Linux的網(wǎng)絡(luò)驅(qū)動中非常重要,sk_buff保存了上層傳遞給網(wǎng)絡(luò)驅(qū)動層的數(shù)據(jù)。也就是說,要發(fā)送出去的數(shù)據(jù)都存在了sk_buff中*/
u16 (*ndo_select_queue)(struct net_device *dev, struct sk_buff *skb, void *accel_priv,select_queue_fallback_t fallback); /*設(shè)備支持多傳輸隊列的時候選擇使用哪個隊列*/
void (*ndo_change_rx_flags)(struct net_device *dev,
int flags);
void (*ndo_set_rx_mode)(struct net_device *dev);/*此函數(shù)用于改變地址過濾列表*/
int (*ndo_set_mac_address)(struct net_device *dev, void *addr); /*此函數(shù)用于修改網(wǎng)卡的 MAC 地址*/
int (*ndo_validate_addr)(struct net_device *dev); /*驗證 MAC 地址是否合法,*/
int (*ndo_do_ioctl)(struct net_device *dev,
struct ifreq *ifr, int cmd); /*用戶程序調(diào)用 ioctl 的時候此函數(shù)就會執(zhí)行,*/
int (*ndo_set_config)(struct net_device *dev,
struct ifmap *map);
int (*ndo_change_mtu)(struct net_device *dev,
int new_mtu); /*更改MTU大小。*/
int (*ndo_neigh_setup)(struct net_device *dev,
struct neigh_parms *);
void (*ndo_tx_timeout) (struct net_device *dev); /*當發(fā)送超時的時候產(chǎn)生會執(zhí)行,一般都是網(wǎng)絡(luò)出問題了導
致發(fā)送超*/
...
#ifdef CONFIG_NET_POLL_CONTROLLER
void (*ndo_poll_controller)(struct net_device *dev); /*使用查詢方式來處理網(wǎng)卡數(shù)據(jù)的收發(fā)。*/
int (*ndo_netpoll_setup)(struct net_device *dev,
struct netpoll_info *info);
void (*ndo_netpoll_cleanup)(struct net_device *dev);
#endif
...
int (*ndo_set_features)(struct net_device *dev, etdev_features_t features); /*修改net_device的 features屬性,設(shè)置相應(yīng)的硬件屬性*/
...
};
/*
@ :ndo_open 函數(shù),:ndo_stop 函數(shù)==以NXP的I.MX 系列SOC 網(wǎng)絡(luò)驅(qū)動為例,會在此函數(shù)中做如下工作
*/
·使能網(wǎng)絡(luò)外設(shè)時鐘。
·申請網(wǎng)絡(luò)所使用的環(huán)形緩沖區(qū)。
·初始化MAC 外設(shè)。
·綁定接口對應(yīng)的 PHY。
·如果使用NAPI 的話要使能NAPI模塊,通過 napi_enable函數(shù)來使能。
·開啟PHY。
·調(diào)用netif_tx_start_all_queues來使能傳輸隊列,也可能調(diào)用netif_start_queue函數(shù)。
·……
do_stop 函數(shù),關(guān)閉網(wǎng)絡(luò)設(shè)備的時候此函數(shù)會執(zhí)行,網(wǎng)絡(luò)驅(qū)動程序也需要實現(xiàn)此
函數(shù)。以NXP的I.MX 系列SOC 網(wǎng)絡(luò)驅(qū)動為例,會在此函數(shù)中做如下工作:
·停止PHY。
·停止NAPI功能。
·停止發(fā)送功能。
·關(guān)閉MAC。
·斷開PHY 連接。
·關(guān)閉網(wǎng)絡(luò)時鐘。
·釋放數(shù)據(jù)緩沖區(qū)。
·……
3、sk_buff結(jié)構(gòu)體
- 網(wǎng)絡(luò)是分層的,對于應(yīng)用層而言不用關(guān)系具體的底層是如何工作的,只需要按照協(xié)議將要發(fā)送或接收的數(shù)據(jù)打包好即可
- 打包好以后都通過dev_queue_xmit函數(shù)將數(shù)據(jù)發(fā)送出去,接收數(shù)據(jù)的話使用netif_rx函數(shù)即可
/*
@ 定義在 include/linux/netdevice.h 中
@ 函數(shù)用于將網(wǎng)絡(luò)數(shù)據(jù)發(fā)送出去
@ skb: 要發(fā)送的數(shù)據(jù),這是一個sk_buff結(jié)構(gòu)體指針,sk_buff是Linux網(wǎng)絡(luò)驅(qū)動中一個非常重要的結(jié)構(gòu)體,網(wǎng)絡(luò)數(shù)據(jù)就是以 sk_buff 保存的,各個協(xié)議層在 sk_buff 中添加自己的協(xié)議頭,最終由底層驅(qū)動講sk_buff中的數(shù)據(jù)發(fā)送出去
@ 網(wǎng)絡(luò)數(shù)據(jù)的接收過程恰好相反,網(wǎng)絡(luò)底層驅(qū)動將接收到的原始數(shù)據(jù)打包成 sk_buff,然后發(fā)送給上層協(xié)議,上層會取掉相應(yīng)的頭部,然后將最終的數(shù)據(jù)發(fā)送給用戶
@ 返回值:0 發(fā)送成功,負值 發(fā)送失敗。
*/
static inline int dev_queue_xmit(struct sk_buff *skb)
/*
@ 上層接收數(shù)據(jù)的話使用netif_rx函數(shù).但是最原始的網(wǎng)絡(luò)數(shù)據(jù)一般是通過輪詢、中斷或NAPI的方式來接收
@ skb:保存接收數(shù)據(jù)的 sk_buff
@ 返回值:NET_RX_SUCCESS 成功,NET_RX_DROP 數(shù)據(jù)包丟棄。
*/
int netif_rx(struct sk_buff *skb)
- sk_buff 是 Linux 網(wǎng)絡(luò)重要的數(shù)據(jù)結(jié)構(gòu),用于管理接收或發(fā)送數(shù)據(jù)包
/*
@ 定義在 include/linux/skbuff.h 中
@ sk_buff 結(jié)構(gòu)體
*/
struct sk_buff {
union {
struct {
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev; /*構(gòu)成一個雙向鏈表*/
union {
ktime_t tstamp; /*數(shù)據(jù)包接收時或準備發(fā)送時的時間戳*/
struct skb_mstamp skb_mstamp;
};
};
struct rb_node rbnode; /* used in netem & tcp stack */
};
struct sock *sk; /*當前 sk_buff所屬的Socket*/
struct net_device *dev; /*當前 sk_buff從哪個設(shè)備接收到或者發(fā)出的*/
char cb[48] __aligned(8); /*cb 為控制緩沖區(qū),不管哪個層都可以自由使用此緩沖區(qū),用于放置私有數(shù)據(jù)。 */
unsigned long _skb_refdst;
void (*destructor)(struct sk_buff *skb); /*當釋放緩沖區(qū)的時候可以在此函數(shù)里面完成某些動作*/
....
unsigned int len, data_len; /*:len為實際的數(shù)據(jù)長度*/
__u16 mac_len, hdr_len; /*:mac_len 為連接層頭部長度,也就是 MAC 頭的長度*/
....
__be16 protocol; /*protocol協(xié)議*/
__u16 transport_header; /*傳輸層頭部*/
__u16 network_header; /*網(wǎng)絡(luò)層頭部*/
__u16 mac_header; /*鏈接層頭部*/
/* private: */
__u32 headers_end[0]; /*緩沖區(qū)的尾部*/
/* public: */
/* These elements must be at the end, see alloc_skb() for
details. */
sk_buff_data_t tail; /*實際數(shù)據(jù)的尾部*/
sk_buff_data_t end; /*緩沖區(qū)的尾部*/
unsigned char *head, *data; /*:head 指向緩沖區(qū)的頭部,data 指向?qū)嶋H數(shù)據(jù)的頭部*/
unsigned int truesize;
atomic_t users;
};
- 再上述結(jié)構(gòu)體中的緩沖區(qū)的head、end和實際數(shù)據(jù)區(qū)的data、tail
/*===11111111111111111111111111111111111111111111111111
@ 分配sk_buff
@ 定義在include/linux/skbuff.h中
@ size:要分配的大小,也就是skb數(shù)據(jù)段大小
@ priority:為GFP MASK宏,比如GFP_KERNEL、GFP_ATOMIC 等
@ 返回值:分配成功的話就返回申請到的 sk_buff首地址,失敗的話就返回 NULL
*/
static inline struct sk_buff *alloc_skb(unsigned int size, gfp_t priority)
/*
@ 定義在include/linux/skbuff.h中
@ 在網(wǎng)絡(luò)設(shè)備驅(qū)動中常常使用 netdev_alloc_skb 來為某個設(shè)備申請一個用于接收的 skb_buff,
@ dev:要給哪個設(shè)備分配 sk_buff。
@ length:要分配的大小。
@ 返回值:分配成功的話就返回申請到的 sk_buff首地址,失敗的話就返回 NULL
*/
static inline struct sk_buff *netdev_alloc_skb(struct net_device *dev, unsigned int length)
/*=======222222222222222222222222222222222222222222222222
@ 釋放sk_buff
@ 定義在include/linux/skbuff.c中
@ skb:要釋放的sk_buff。
@ 返回值:無
*/
void kfree_skb(struct sk_buff *skb)
/*=======33333333333333333333333333333333333333333333333
@ skb_put、skb_push、sbk_pull和 skb_reserve 四個函數(shù)用于變更 sk_buff
*/
/*函數(shù)用于在尾部擴展 skb_buff的數(shù)據(jù)區(qū),也就將 skb_buff 的 tail 后移 n 個字節(jié),從而導致 skb_buff 的 len 增加 n 個字節(jié)
@ skb:要操作的sk_buff
@ len:要增加多少個字節(jié)。
@ 返回值:擴展出來的那一段數(shù)據(jù)區(qū)首地址
*/
unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
/* skb_push函數(shù)用于在頭部擴展 skb_buff的數(shù)據(jù)區(qū)
@ skb:要操作的sk_buff
@ 要增加多少個字節(jié)
@ 返回值:擴展完成以后新的數(shù)據(jù)區(qū)首地址。
*/
unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
/* sbk_pull函數(shù)用于從 sk_buff的數(shù)據(jù)區(qū)起始位置刪除數(shù)據(jù)
@ skb:要操作的sk_buff
@ len:要刪除的字節(jié)數(shù)。
@ 返回值:刪除以后新的數(shù)據(jù)區(qū)首地址
*/
unsigned char *skb_pull(struct sk_buff *skb, unsigned int len
/* sbk_reserve函數(shù)用于調(diào)整緩沖區(qū)的頭部大小
@ skb:要操作的sk_buff
@ len:要增加的緩沖區(qū)頭部大小
@ 返回值:無。
*/
static inline void skb_reserve(struct sk_buff *skb, int len)
-
為了更好的解釋上面四個函數(shù),下面是結(jié)構(gòu)示意圖:
4、網(wǎng)絡(luò)NAPI處理機制 =====很重要的 -
Linux 里面的網(wǎng)絡(luò)數(shù)據(jù)接收也輪詢和中斷兩種,
1、中斷的好處就是響應(yīng)快,數(shù)據(jù)量小的時候處理及時,速度、快,但是一旦當數(shù)據(jù)量大,而且都是短幀的時候會導致中斷頻繁發(fā)生,消耗大量的CPU處理時間在中斷自身處理上2、輪詢恰好相反,響應(yīng)沒有中斷及時,但是在處理大量數(shù)據(jù)的時候不需要消耗過多的CPU 處理時間。
-
linux在這兩個處理方式的基礎(chǔ)上,提出了另外一種網(wǎng)絡(luò)數(shù)據(jù)接收的處理方法:NAPI(New API),NAPI 是一種高效的網(wǎng)絡(luò)處理技術(shù).
-
核心思想:中斷(用來喚醒數(shù)據(jù)接收服務(wù)程序)+ 輪詢(在接受服務(wù)程序中采用POLL的方法來輪詢處理數(shù)據(jù))
-
目前 NAPI 已經(jīng)在 Linux 的網(wǎng)絡(luò)驅(qū)動中得到了大量的
應(yīng)用文章來源:http://www.zghlxwxcb.cn/news/detail-404142.html -
如何在驅(qū)動中使用 NAPI處理機制文章來源地址http://www.zghlxwxcb.cn/news/detail-404142.html
/*初始化NAPI==1111111111111111111111111111111111111111111111
@ 定義在 net/core/dev.c中
@ 初始化一個 napi_struct實例
@ dev:每個NAPI 必須關(guān)聯(lián)一個網(wǎng)絡(luò)設(shè)備,此參數(shù)指定NAPI要關(guān)聯(lián)的網(wǎng)絡(luò)設(shè)備。
@ napi:要初始化的 NAPI實例
@ poll: NAPI所使用的輪詢函數(shù),非常重要,一般在此輪詢函數(shù)中完成網(wǎng)絡(luò)數(shù)據(jù)接收的工作。
@ weight:NAPI默認權(quán)重(weight),一般為NAPI_POLL_WEIGHT。
@ 返回值:無。
*/
void netif_napi_add(struct net_device *dev, struct napi_struct *napi, int (*poll)(struct napi_struct *int weight)
/* 刪除NAPI ==2222222222222222222222222222222222222222222222
@ napi:要刪除的NAPI
@ 返回值:無。
*/
void netif_napi_del(struct napi_struct *napi)
/* 使能NAPI ==333333333333333333333333333333333333333333333
@ n:要使能的NAPI
@ 返回值:無。
*/
inline void napi_enable(struct napi_struct *n)
/*關(guān)閉NAPI ===444444444444444444444444444444444444444444444444
@ n:要關(guān)閉的NAPI。
@ 返回值:無。
*/
void napi_disable(struct napi_struct *n)
/* 檢查NAPI是否可以進行調(diào)度 ==555555555555555555555555555555555
@ n:要檢查的NAPI
@ 返回值:如果可以調(diào)度就返回真,如果不可調(diào)度就返回假。
*/
inline bool napi_schedule_prep(struct napi_struct *n)
/*NAPI調(diào)度 ===6666666666666666666666666666666666666666666666666
@ n:要調(diào)度的NAPI。
@ 返回值:無。
*/
void __napi_schedule(struct napi_struct *n)
/*是否可以調(diào)度+調(diào)度 ===5555555555555555+666666666666666666666
@ n:要調(diào)度的NAPI。
*/
static inline void napi_schedule(struct napi_struct *n)
{
if (napi_schedule_prep(n))
__napi_schedule(n);
}
/*NAPI處理完成 ===77777777777777777777777777777777777777777777777
@ n:處理完成的NAPI。
@ 返回值:無。
*/
inline void napi_complete(struct napi_struct *n)
到了這里,關(guān)于linux內(nèi)核網(wǎng)絡(luò)驅(qū)動框架(linux驅(qū)動開發(fā)篇)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!