生活随笔
收集整理的這篇文章主要介紹了
Linux内核--网络栈实现分析(三)--驱动程序层+链路层(上)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本文分析基于Linux Kernel 1.2.13
原創作品,轉載請標明http://blog.csdn.net/yming0221/article/details/7497260
更多請看專欄,地址http://blog.csdn.net/column/details/linux-kernel-net.html
作者:閆明
注:標題中的”(上)“,”(下)“表示分析過程基于數據包的傳遞方向:”(上)“表示分析是從底層向上分析、”(下)“表示分析是從上向下分析。
經過前面兩篇博文的分析,已經對Linux的內核網絡棧的結構有了一個模糊的認識,這里我們開始從底層開始詳細分析Linux內核網絡棧的實現。由于這是早期版本,代碼的層次隔離做的還不是很好,這里說是從底層分析,但是不免會牽扯上層或下層的函數,許多關鍵代碼都在驅動的文件夾下。
我們首先有第一篇博文中知道在網絡棧初始化的時候在net/socket.c中的函數sock_init()函數中當proto_init()完成后會執行dev_init()來進行網絡設備模塊的初始化。
首先說明一下,在drivers/net/space.c中定義了設備首節點地址dev_base,其實際上是回環設備的地址。
[cpp]?view plaincopy
struct?device?loopback_dev?=?{?? ????"lo",????????????? ????0x0,?????????????? ????0x0,?????????????? ????0x0,?????????????? ????0x0,?????????????? ????0,???????????? ????0,???????????? ????0,?0,?0,?????????? ????NEXT_DEV,????????? ????loopback_init????????? };?? ?? struct?device?*dev_base?=?&loopback_dev;?? 而NEXT_DEV宏定義即定義了下一個網絡設備的地址,這樣可以把設備串成鏈。
附網絡設備的定義(include/linux/netdevice.h)如下:
[cpp]?view plaincopy
? ? ? ? ? ?? struct?device??? {?? ?? ??? ? ? ? ?? ??char????????????*name;?? ?? ???? ??unsigned?long???????rmem_end;??????? ??unsigned?long???????rmem_start;????????? ??unsigned?long???????mem_end;???????? ??unsigned?long???????mem_start;?????????? ??unsigned?long???????base_addr;?????????? ??unsigned?char???????irq;???????????? ?? ???? ??volatile?unsigned?char??start,?????????? ??????????????????????????tbusy,?????????? ??????????????????????????interrupt;?????????? ?? ??struct?device???????*next;?? ?? ???? ??int?????????????(*init)(struct?device?*dev);?? ?? ??? ?? ??unsigned?char???????if_port;???????? ??unsigned?char???????dma;???????????? ?? ??struct?enet_statistics*?(*get_stats)(struct?device?*dev);?? ?? ??? ? ? ? ?? ?? ???? ??unsigned?long???????trans_start;???? ??unsigned?long???????last_rx;???? ?? ??unsigned?short??????flags;?????? ??unsigned?short??????family;????? ??unsigned?short??????metric;????? ??unsigned?short??????mtu;???????? ??unsigned?short??????type;??????? ??unsigned?short??????hard_header_len;???? ??void????????????*priv;?????? ?? ???? ??unsigned?char???????broadcast[MAX_ADDR_LEN];???? ??unsigned?char???????dev_addr[MAX_ADDR_LEN];????? ??unsigned?char???????addr_len;??? ??unsigned?long???????pa_addr;???? ??unsigned?long???????pa_brdaddr;????? ??unsigned?long???????pa_dstaddr;????? ??unsigned?long???????pa_mask;???? ??unsigned?short??????pa_alen;???? ?? ??struct?dev_mc_list?????*mc_list;???? ??int????????????mc_count;???? ???? ??struct?ip_mc_list??*ip_mc_list;????? ?????? ???? ???? ??unsigned?long????????pkt_queue;????? ??struct?device???????*slave;????? ???? ?? ???? ??struct?sk_buff_head?????buffs[DEV_NUMBUFFS];?? ?? ???? ??int?????????????(*open)(struct?device?*dev);?? ??int?????????????(*stop)(struct?device?*dev);?? ??int?????????????(*hard_start_xmit)?(struct?sk_buff?*skb,?? ??????????????????????????struct?device?*dev);?? ??int?????????????(*hard_header)?(unsigned?char?*buff,?? ??????????????????????struct?device?*dev,?? ??????????????????????unsigned?short?type,?? ??????????????????????void?*daddr,?? ??????????????????????void?*saddr,?? ??????????????????????unsigned?len,?? ??????????????????????struct?sk_buff?*skb);?? ??int?????????????(*rebuild_header)(void?*eth,?struct?device?*dev,?? ????????????????unsigned?long?raddr,?struct?sk_buff?*skb);?? ??unsigned?short??????(*type_trans)?(struct?sk_buff?*skb,?? ?????????????????????struct?device?*dev);?? #define?HAVE_MULTICAST????????????? ??void????????????(*set_multicast_list)(struct?device?*dev,?? ?????????????????????int?num_addrs,?void?*addrs);?? #define?HAVE_SET_MAC_ADDR?????????? ??int?????????????(*set_mac_address)(struct?device?*dev,?void?*addr);?? #define?HAVE_PRIVATE_IOCTL?? ??int?????????????(*do_ioctl)(struct?device?*dev,?struct?ifreq?*ifr,?int?cmd);?? #define?HAVE_SET_CONFIG?? ??int?????????????(*set_config)(struct?device?*dev,?struct?ifmap?*map);?? ???? };?? dev_init()網絡設備的初始化函數如下:
[cpp]?view plaincopy
? ? ? ? ? ? ? ? ?? ??? void?dev_init(void)?? {?? ????struct?device?*dev,?*dev2;?? ?? ????? ? ? ? ? ?? ??????? ????dev2?=?NULL;?? ????for?(dev?=?dev_base;?dev?!=?NULL;?dev=dev->next)??? ????{?? ????????if?(dev->init?&&?dev->init(dev))??? ????????{?? ????????????? ? ?? ??????????????? ????????????if?(dev2?==?NULL)??? ????????????????dev_base?=?dev->next;?? ????????????else??? ????????????????dev2->next?=?dev->next;?? ????????}??? ????????else?? ????????{?? ????????????dev2?=?dev;?? ????????}?? ????}?? }?? 這里我們看一下dev_base這個隊列是如何定義的,這里我們僅僅看eth網卡的定義方式即可
[cpp]?view plaincopy
? ? ?? ?? static?struct?device?eth3_dev?=?{?? ????"eth3",?0,0,0,0,0xffe0?,?0,0,0,0,?NEXT_DEV,?ethif_probe?};?? static?struct?device?eth2_dev?=?{?? ????"eth2",?0,0,0,0,0xffe0?,?0,0,0,0,?eth3_dev,?ethif_probe?};?? static?struct?device?eth1_dev?=?{?? ????"eth1",?0,0,0,0,0xffe0?,?0,0,0,0,?eth2_dev,?ethif_probe?};?? ?? static?struct?device?eth0_dev?=?{?? ????"eth0",?0,?0,?0,?0,?ETH0_ADDR,?ETH0_IRQ,?0,?0,?0,?eth1_dev,?ethif_probe?};?? ?? #???undef?NEXT_DEV?? #???define?NEXT_DEV?(eth0_dev)?? 可以看出eth系列網卡設備的init函數定義為ethif_probe(),該函數會調用具體網卡的探測函數,我們還是以?NS8390 ethernet網卡為例來分析,該網卡的驅動實現文件為drivers/net/ne.c ethif_probe()函數會調用函數ne_probe()探測函數,而該函數對設備地址進行檢查后調用ne_probe1()函數,具體工作有ne_probe1()函數完成。
函數如下:
[cpp]?view plaincopy
static?int?ne_probe1(struct?device?*dev,?int?ioaddr)?? {?? .....................?? ? ?? ????dev->irq?=?9;?? ?????? ????? ?? ????{?? ????int?irqval?=?request_irq?(dev->irq,?ei_interrupt,?0,?wordlength==2???"ne2000":"ne1000");?? ????if?(irqval)?{?? ????????printk?("?unable?to?get?IRQ?%d?(irqval=%d).\n",?dev->irq,?irqval);?? ????????return?EAGAIN;?? ????}?? ????}?? ?? ????dev->base_addr?=?ioaddr;?? ?? ????request_region(ioaddr,?NE_IO_EXTENT,?wordlength==2???"ne2000":"ne1000");?? ?? ????for(i?=?0;?i?<?ETHER_ADDR_LEN;?i++)?? ????dev->dev_addr[i]?=?SA_prom[i];?? ?? ????ethdev_init(dev);?? ????printk("\n%s:?%s?found?at?%#x,?using?IRQ?%d.\n",?? ???????dev->name,?name,?ioaddr,?dev->irq);?? ?? ????if?(ei_debug?>?0)?? ????printk(version);?? ?? ????ei_status.name?=?name;?? ????ei_status.tx_start_page?=?start_page;?? ????ei_status.stop_page?=?stop_page;?? ????ei_status.word16?=?(wordlength?==?2);?? ?? ????ei_status.rx_start_page?=?start_page?+?TX_PAGES;?? #ifdef?PACKETBUF_MEMSIZE?? ?????? ????ei_status.stop_page?=?ei_status.tx_start_page?+?PACKETBUF_MEMSIZE;?? #endif?? ?? ????ei_status.reset_8390?=?&ne_reset_8390;?? ????ei_status.block_input?=?&ne_block_input;?? ????ei_status.block_output?=?&ne_block_output;?? ????NS8390_init(dev,?0);?? ????return?0;?? }?? 初始化函數ethdev_init()在文件drivers/net/8390.c中。如下:
[cpp]?view plaincopy
?? int?ethdev_init(struct?device?*dev)?? {?? ????if?(ei_debug?>?1)?? ????????printk(version);?? ?????? ????if?(dev->priv?==?NULL)?{?? ????????struct?ei_device?*ei_local;?? ?????????? ????????dev->priv?=?kmalloc(sizeof(struct?ei_device),?GFP_KERNEL);?? ????????memset(dev->priv,?0,?sizeof(struct?ei_device));?? ????????ei_local?=?(struct?ei_device?*)dev->priv;?? #ifndef?NO_PINGPONG?? ????????ei_local->pingpong?=?1;?? #endif?? ????}?? ?????? ?????? ????if?(dev->open?==?NULL)?? ????????dev->open?=?&ei_open;?? ?????? ????dev->hard_start_xmit?=?&ei_start_xmit;?? ????dev->get_stats???=?get_stats;?? #ifdef?HAVE_MULTICAST?? ????dev->set_multicast_list?=?&set_multicast_list;?? #endif?? ?? ????ether_setup(dev);?? ?????????? ????return?0;?? }?? ether_setup()函數的實現如下:
[cpp]?view plaincopy
void?ether_setup(struct?device?*dev)?? {?? ????int?i;?? ????? ?? ????for?(i?=?0;?i?<?DEV_NUMBUFFS;?i++)?? ????????skb_queue_head_init(&dev->buffs[i]);?? ?? ?????? ????if?(dev->name?&&?(strncmp(dev->name,?"eth",?3)?==?0))?{?? ????????i?=?simple_strtoul(dev->name?+?3,?NULL,?0);?? ????????if?(ethdev_index[i]?==?NULL)?{?? ????????????ethdev_index[i]?=?dev;?? ????????}?? ????????else?if?(dev?!=?ethdev_index[i])?{?? ?????????????? ????????????printk("ether_setup:?Ouch!?Someone?else?took?%s\n",?? ????????????????dev->name);?? ????????}?? ????}?? ?? ????dev->hard_header?=?eth_header;?? ????dev->rebuild_header?=?eth_rebuild_header;?? ????dev->type_trans?=?eth_type_trans;?? ?? ????dev->type????????=?ARPHRD_ETHER;?? ????dev->hard_header_len?=?ETH_HLEN;?? ????dev->mtu?????=?1500;??? ????dev->addr_len????=?ETH_ALEN;?? ????for?(i?=?0;?i?<?ETH_ALEN;?i++)?{?? ????????dev->broadcast[i]=0xff;?? ????}?? ?? ?????? ????dev->flags???????=?IFF_BROADCAST|IFF_MULTICAST;?? ????dev->family??????=?AF_INET;?? ????dev->pa_addr?=?0;?? ????dev->pa_brdaddr?=?0;?? ????dev->pa_mask?=?0;?? ????dev->pa_alen?=?sizeof(unsigned?long);?? }?? 這樣,網絡設備的初始化工作就完成了。
在drivers/net/8390.c中實現了該網卡的設備的基本操作函數,
設備的打開函數ei_open()比較簡單,下面列出該設備的發送和接收函數,在這里不做具體的分析,如果想更多了解請點擊前面分析過的DM9000網卡驅動,下面給出鏈接:
ARM-Linux驅動--DM9000網卡驅動分析(一)
ARM-Linux驅動--DM9000網卡驅動分析(二)
ARM-Linux驅動--DM9000網卡驅動分析(三)
ARM-Linux驅動--DM9000網卡驅動分析(四)其基本結構是一致的。
ei_start_xmit()
[cpp]?view plaincopy
static?int?ei_start_xmit(struct?sk_buff?*skb,?struct?device?*dev)?? {?? ????int?e8390_base?=?dev->base_addr;?? ????struct?ei_device?*ei_local?=?(struct?ei_device?*)?dev->priv;?? ????int?length,?send_length;?? ????unsigned?long?flags;?? ?????? ? ? ? ? ?? ??? ????if?(dev->tbusy)?{?????? ????????int?txsr?=?inb(e8390_base+EN0_TSR),?isr;?? ????????int?tickssofar?=?jiffies?-?dev->trans_start;?? ????????if?(tickssofar?<?TX_TIMEOUT?||???(tickssofar?<?(TX_TIMEOUT+5)?&&?!?(txsr?&?ENTSR_PTX)))?{?? ????????????return?1;?? ????????}?? ????????isr?=?inb(e8390_base+EN0_ISR);?? ????????if?(dev->start?==?0)?{?? ????????????printk("%s:?xmit?on?stopped?card\n",?dev->name);?? ????????????return?1;?? ????????}?? ????????printk(KERN_DEBUG?"%s:?transmit?timed?out,?TX?status?%#2x,?ISR?%#2x.\n",?? ???????????????dev->name,?txsr,?isr);?? ?????????? ????????if?(isr)?? ????????????printk(KERN_DEBUG?"%s:?Possible?IRQ?conflict?on?IRQ%d?\n",?dev->name,?dev->irq);?? ????????else?{?? ?????????????? ????????????printk(KERN_DEBUG?"%s:?Possible?network?cable?problem?\n",?dev->name);?? ????????????if(ei_local->stat.tx_packets==0)?? ????????????????ei_local->interface_num?^=?1;?????? ????????}?? ?????????? ????????ei_reset_8390(dev);?? ????????NS8390_init(dev,?1);?? ????????dev->trans_start?=?jiffies;?? ????}?? ?????? ????? ? ?? ????if?(skb?==?NULL)?{?? ????????dev_tint(dev);?? ????????return?0;?? ????}?? ?????? ????length?=?skb->len;?? ????if?(skb->len?<=?0)?? ????????return?0;?? ?? ????save_flags(flags);?? ????cli();?? ?? ?????? ????if?((set_bit(0,?(void*)&dev->tbusy)?!=?0)?||?ei_local->irqlock)?{?? ????printk("%s:?Tx?access?conflict.?irq=%d?lock=%d?tx1=%d?tx2=%d?last=%d\n",?? ????????dev->name,?dev->interrupt,?ei_local->irqlock,?ei_local->tx1,?? ????????ei_local->tx2,?ei_local->lasttx);?? ????restore_flags(flags);?? ????return?1;?? ????}?? ?? ?????? ????outb(0x00,?e8390_base?+?EN0_IMR);?? ????ei_local->irqlock?=?1;?? ????restore_flags(flags);?? ?? ????send_length?=?ETH_ZLEN?<?length???length?:?ETH_ZLEN;?? ?? ????if?(ei_local->pingpong)?{?? ????????int?output_page;?? ????????if?(ei_local->tx1?==?0)?{?? ????????????output_page?=?ei_local->tx_start_page;?? ????????????ei_local->tx1?=?send_length;?? ????????????if?(ei_debug??&&??ei_local->tx2?>?0)?? ????????????????printk("%s:?idle?transmitter?tx2=%d,?lasttx=%d,?txing=%d.\n",?? ???????????????????????dev->name,?ei_local->tx2,?ei_local->lasttx,?? ???????????????????????ei_local->txing);?? ????????}?else?if?(ei_local->tx2?==?0)?{?? ????????????output_page?=?ei_local->tx_start_page?+?6;?? ????????????ei_local->tx2?=?send_length;?? ????????????if?(ei_debug??&&??ei_local->tx1?>?0)?? ????????????????printk("%s:?idle?transmitter,?tx1=%d,?lasttx=%d,?txing=%d.\n",?? ???????????????????????dev->name,?ei_local->tx1,?ei_local->lasttx,?? ???????????????????????ei_local->txing);?? ????????}?else?{?????? ????????????if?(ei_debug)?? ????????????????printk("%s:?No?Tx?buffers?free.?irq=%d?tx1=%d?tx2=%d?last=%d\n",?? ????????????????????dev->name,?dev->interrupt,?ei_local->tx1,??? ????????????????????ei_local->tx2,?ei_local->lasttx);?? ????????????ei_local->irqlock?=?0;?? ????????????dev->tbusy?=?1;?? ????????????outb_p(ENISR_ALL,?e8390_base?+?EN0_IMR);?? ????????????return?1;?? ????????}?? ????????ei_block_output(dev,?length,?skb->data,?output_page);?? ????????if?(!?ei_local->txing)?{?? ????????????ei_local->txing?=?1;?? ????????????NS8390_trigger_send(dev,?send_length,?output_page);?? ????????????dev->trans_start?=?jiffies;?? ????????????if?(output_page?==?ei_local->tx_start_page)?? ????????????????ei_local->tx1?=?-1,?ei_local->lasttx?=?-1;?? ????????????else?? ????????????????ei_local->tx2?=?-1,?ei_local->lasttx?=?-2;?? ????????}?else?? ????????????ei_local->txqueue++;?? ?? ????????dev->tbusy?=?(ei_local->tx1??&&??ei_local->tx2);?? ????}?else?{???? ????????ei_block_output(dev,?length,?skb->data,?ei_local->tx_start_page);?? ????????ei_local->txing?=?1;?? ????????NS8390_trigger_send(dev,?send_length,?ei_local->tx_start_page);?? ????????dev->trans_start?=?jiffies;?? ????????dev->tbusy?=?1;?? ????}?? ?????? ?????? ????ei_local->irqlock?=?0;?? ????outb_p(ENISR_ALL,?e8390_base?+?EN0_IMR);?? ?? ????dev_kfree_skb?(skb,?FREE_WRITE);?? ?????? ????return?0;?? }?? ei_receive()函數
[cpp]?view plaincopy
static?void?ei_receive(struct?device?*dev)?? {?? ????int?e8390_base?=?dev->base_addr;?? ????struct?ei_device?*ei_local?=?(struct?ei_device?*)?dev->priv;?? ????int?rxing_page,?this_frame,?next_frame,?current_offset;?? ????int?rx_pkt_count?=?0;?? ????struct?e8390_pkt_hdr?rx_frame;?? ????int?num_rx_pages?=?ei_local->stop_page-ei_local->rx_start_page;?? ?????? ????while?(++rx_pkt_count?<?10)?{?? ????????int?pkt_len;?? ?????????? ?????????? ????????outb_p(E8390_NODMA+E8390_PAGE1,?e8390_base?+?E8390_CMD);?? ????????rxing_page?=?inb_p(e8390_base?+?EN1_CURPAG);?? ????????outb_p(E8390_NODMA+E8390_PAGE0,?e8390_base?+?E8390_CMD);?? ?????????? ?????????? ????????this_frame?=?inb_p(e8390_base?+?EN0_BOUNDARY)?+?1;?? ????????if?(this_frame?>=?ei_local->stop_page)?? ????????????this_frame?=?ei_local->rx_start_page;?? ?????????? ????????? ?? ????????if?(ei_debug?>?0??&&??this_frame?!=?ei_local->current_page)?? ????????????printk("%s:?mismatched?read?page?pointers?%2x?vs?%2x.\n",?? ???????????????????dev->name,?this_frame,?ei_local->current_page);?? ?????????? ????????if?(this_frame?==?rxing_page)????? ????????????break;???????????????? ?????????? ????????current_offset?=?this_frame?<<?8;?? ????????ei_block_input(dev,?sizeof(rx_frame),?(char?*)&rx_frame,?? ???????????????????????current_offset);?? ?????????? ????????pkt_len?=?rx_frame.count?-?sizeof(rx_frame);?? ?????????? ????????next_frame?=?this_frame?+?1?+?((pkt_len+4)>>8);?? ?????????? ????????? ? ?? ????????if?(rx_frame.next?!=?next_frame?? ????????????&&?rx_frame.next?!=?next_frame?+?1?? ????????????&&?rx_frame.next?!=?next_frame?-?num_rx_pages?? ????????????&&?rx_frame.next?!=?next_frame?+?1?-?num_rx_pages)?{?? ????????????ei_local->current_page?=?rxing_page;?? ????????????outb(ei_local->current_page-1,?e8390_base+EN0_BOUNDARY);?? ????????????ei_local->stat.rx_errors++;?? ????????????continue;?? ????????}?? ?? ????????if?(pkt_len?<?60??||??pkt_len?>?1518)?{?? ????????????if?(ei_debug)?? ????????????????printk("%s:?bogus?packet?size:?%d,?status=%#2x?nxpg=%#2x.\n",?? ???????????????????????dev->name,?rx_frame.count,?rx_frame.status,?? ???????????????????????rx_frame.next);?? ????????????ei_local->stat.rx_errors++;?? ????????}?else?if?((rx_frame.status?&?0x0F)?==?ENRSR_RXOK)?{?? ????????????struct?sk_buff?*skb;?? ?????????????? ????????????skb?=?alloc_skb(pkt_len,?GFP_ATOMIC);?? ????????????if?(skb?==?NULL)?{?? ????????????????if?(ei_debug?>?1)?? ????????????????????printk("%s:?Couldn't?allocate?a?sk_buff?of?size?%d.\n",?? ???????????????????????????dev->name,?pkt_len);?? ????????????????ei_local->stat.rx_dropped++;?? ????????????????break;?? ????????????}?else?{?? ????????????????skb->len?=?pkt_len;?? ????????????????skb->dev?=?dev;?? ?????????????????? ????????????????ei_block_input(dev,?pkt_len,?(char?*)?skb->data,?? ???????????????????????????????current_offset?+?sizeof(rx_frame));?? ????????????????netif_rx(skb);?? ????????????????ei_local->stat.rx_packets++;?? ????????????}?? ????????}?else?{?? ????????????int?errs?=?rx_frame.status;?? ????????????if?(ei_debug)?? ????????????????printk("%s:?bogus?packet:?status=%#2x?nxpg=%#2x?size=%d\n",?? ???????????????????????dev->name,?rx_frame.status,?rx_frame.next,?? ???????????????????????rx_frame.count);?? ????????????if?(errs?&?ENRSR_FO)?? ????????????????ei_local->stat.rx_fifo_errors++;?? ????????}?? ????????next_frame?=?rx_frame.next;?? ?????????? ?????????? ????????if?(next_frame?>=?ei_local->stop_page)?{?? ????????????printk("%s:?next?frame?inconsistency,?%#2x\n",?dev->name,?? ???????????????????next_frame);?? ????????????next_frame?=?ei_local->rx_start_page;?? ????????}?? ????????ei_local->current_page?=?next_frame;?? ????????outb_p(next_frame-1,?e8390_base+EN0_BOUNDARY);?? ????}?? ????? ? ?? ?? ?????? ????if?(rx_pkt_count?>?high_water_mark)?? ????????high_water_mark?=?rx_pkt_count;?? ?? ?????? ????outb_p(ENISR_RX+ENISR_RX_ERR+ENISR_OVER,?e8390_base+EN0_ISR);?? ????return;?? } ?
轉載于:https://www.cnblogs.com/wangfengju/archive/2013/04/13/6173204.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀
總結
以上是生活随笔為你收集整理的Linux内核--网络栈实现分析(三)--驱动程序层+链路层(上)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。