网络设备的注册与初始化
2019獨角獸企業(yè)重金招聘Python工程師標準>>>
NIC可用之前,其相關(guān)聯(lián)的net_device數(shù)據(jù)結(jié)構(gòu)必須先初始化,添加到內(nèi)核網(wǎng)絡(luò)設(shè)備數(shù)據(jù)庫、配置并開啟。不要把注冊/除名以及開啟/關(guān)閉混淆是十分重要的,這是兩種不同的概念:
- 如果把加載設(shè)備驅(qū)動程序模塊的動作排除的話,注冊和除名是獨立于用戶之外的,是由內(nèi)核驅(qū)動的。僅僅注冊了的設(shè)備還不能運轉(zhuǎn)。
- 開啟和關(guān)閉設(shè)備都需要用戶參與。一旦設(shè)備已由內(nèi)核注冊,用戶就可通過用戶命令看到該設(shè)備,配置并予以開啟。
設(shè)備注冊之時
加載NIC設(shè)備驅(qū)動程序
如果NIC設(shè)備驅(qū)動程序內(nèi)建至內(nèi)核中,則在引導期間初始化;否則,如果以模塊加載,就會在運行期間初始化;否則,如果以模塊加載,就會在運行期間初始化。每當發(fā)生初始化時,該驅(qū)動程序所控制的所有NIC都會被注冊。
插入可熱插拔網(wǎng)絡(luò)設(shè)備
當用戶把可熱插拔NIC設(shè)備插入進來時,內(nèi)核會通知其驅(qū)動程序,而驅(qū)動程序在注冊該設(shè)備。
設(shè)備除名之時
卸載NIC設(shè)備驅(qū)動程序
這僅是針對那些以模塊加載的驅(qū)動程序,不適合那些內(nèi)建至內(nèi)核的驅(qū)動程序。當管理員卸載NIC設(shè)備驅(qū)動程序時,所有相關(guān)聯(lián)的NIC都必須除名。
刪除可熱插拔網(wǎng)絡(luò)設(shè)備
當用戶從系統(tǒng)(其運行的內(nèi)核支持可熱插拔設(shè)備)刪除可熱插拔NIC時,則網(wǎng)絡(luò)設(shè)備就會被除名。
設(shè)備注冊狀態(tài)通知
內(nèi)核組件和用戶空間應(yīng)用程序可能都想知道何時發(fā)生網(wǎng)絡(luò)設(shè)備注冊、除名、關(guān)閉或者開啟之事。這類事件的通知是通過兩種通道傳送的:
netdev_chain,內(nèi)核組件可以注冊此通知鏈。設(shè)備的注冊和除名在各個階段的進展都是通過netdev_chain通知鏈報告的。此鏈定義在net/core/dev.c中,而對此類事件感興趣的內(nèi)核組件可以通過register_netdevice_notifier和unregister_netdevice_notifier分別對該鏈注冊或除名。
netdev_chain報告的事件:
NETDEV_UP,NETDEV_GOING_DOWN,NETDEV_DOWN,送出NETDEV_UP以報告設(shè)備已開啟,而且此事件是由dev_open產(chǎn)生。當設(shè)備要關(guān)閉時,就會送出NETDEV_GOING_DOWN.當設(shè)備已關(guān)閉時,就會送出NETDEV_DOWN.這些事件都是由dev_close()產(chǎn)生的。
NETDEV_REGISTER設(shè)備已注冊,此事件是有register_netdevice產(chǎn)生的。
NETDEV_UNREGISTER,設(shè)備已經(jīng)除名,此事件是由unregister_netdevice產(chǎn)生的。
NETDEV_CHANGEADDR設(shè)備的硬件地址已改變。
NETDEV_CHANGENAME設(shè)備已改變其名稱
NETDEV_CHANGE設(shè)備的狀態(tài)或配置已經(jīng)改變,此事件會用在上述情況未包括的其他情況下。
注意,向鏈注冊時,register_netdevice_notifier也會(僅對新注冊者)重放當前系統(tǒng)已注冊設(shè)備的所有過去的NETDEV_REGISTER和NETDEV_UP通知信息。這樣就能給新注冊者有關(guān)已注冊設(shè)備的清晰圖像。內(nèi)核注冊該鏈的子系統(tǒng):路由,使用此通知信息新增或刪除與此設(shè)備相關(guān)聯(lián)的所有路由項目;協(xié)議代碼,當改變一個本地設(shè)備的MAC地址時,ARP表也必須據(jù)此更新。RTnetlink。
Netlink的RTMGRP_LINK多播群組,用戶空間程序可以注冊netlink的RTMGRP_LINK多播群組,當設(shè)備的狀態(tài)或配置中有變更時,就會用rtmsg_ifinfo把通知信息傳送給Link多播群組RTMGRP_LINK,其中一些通知信息如下:
- 當netdev_chain通知鏈收到一個通知信息時,RTnetlink會注冊前一節(jié)所提及的netdev_chain,然后重放其接受到的通知信息。
- 當一個已關(guān)閉的設(shè)備開啟時或者處于相反的過程。
- 當net_device->flags中的一個標識有變動時。
netplugd是守護進程,會監(jiān)聽這些通知信息,然后根據(jù)用戶配置文件而反應(yīng)。
函數(shù)netdev_wait_allrefs
netdev_wait_allrefs由一個循環(huán)組成,只有當dev->refcnt建至零時才會結(jié)束。此函數(shù)每秒都會發(fā)送出一個NETDEV_UNREGISTER通知信息,而沒10秒都會在控制臺上打印出一條警告信息。剩余時間都在休眠。此函數(shù)直到對輸入net_device結(jié)構(gòu)的所有引用都已釋放為止。有兩種常見情況需要傳送一個以上的通知信息:
bug,例如有段代碼持有對net_device結(jié)構(gòu)的引用,但是因為沒有在netdev_chain通知鏈注冊,或因為沒有正確處理通知信息,使其無法釋放。
未決的定時器,例如,假設(shè)當定時器到期時要執(zhí)行的那個函數(shù)必須訪問的數(shù)據(jù)中,包含了對net_device結(jié)構(gòu)的引用。在這種情況下,你必須等待直到該定時器到期,而且其處理函數(shù)有望會釋放其引用。
1: /* 2: * netdev_wait_allrefs - wait until all references are gone. 3: * 4: * This is called when unregistering network devices. 5: * 6: * Any protocol or device that holds a reference should register 7: * for netdevice notification, and cleanup and put back the 8: * reference if they receive an UNREGISTER event. 9: * We can get stuck here if buggy protocols don't correctly 10: * call dev_put. 11: */ 12: static void netdev_wait_allrefs(struct net_device *dev) 13: { 14: unsigned long rebroadcast_time, warning_time; 15: int refcnt; 16: ? 17: linkwatch_forget_dev(dev); 18: ? 19: rebroadcast_time = warning_time = jiffies; 20: refcnt = netdev_refcnt_read(dev); 21: ? 22: while (refcnt != 0) { 23: if (time_after(jiffies, rebroadcast_time + 1 * HZ)) { 24: rtnl_lock(); 25: ? 26: /* Rebroadcast unregister notification */ 27: call_netdevice_notifiers(NETDEV_UNREGISTER, dev); 28: /* don't resend NETDEV_UNREGISTER_BATCH, _BATCH users 29: * should have already handle it the first time */ 30: ? 31: if (test_bit(__LINK_STATE_LINKWATCH_PENDING, 32: &dev->state)) { 33: /* We must not have linkwatch events 34: * pending on unregister. If this 35: * happens, we simply run the queue 36: * unscheduled, resulting in a noop 37: * for this device. 38: */ 39: linkwatch_run_queue(); 40: } 41: ? 42: __rtnl_unlock(); 43: ? 44: rebroadcast_time = jiffies; 45: } 46: ? 47: msleep(250); 48: ? 49: refcnt = netdev_refcnt_read(dev); 50: ? 51: if (time_after(jiffies, warning_time + 10 * HZ)) { 52: printk(KERN_EMERG "unregister_netdevice: " 53: "waiting for %s to become free. Usage " 54: "count = %d\n", 55: dev->name, refcnt); 56: warning_time = jiffies; 57: } 58: } 59: }開啟和關(guān)閉網(wǎng)絡(luò)設(shè)備
設(shè)備一旦注冊就可用,但是,除非用戶明確的開啟,否則還是無法傳輸和接收數(shù)據(jù)流。開始設(shè)備函數(shù)為dev_open
- 如果有定義的話,調(diào)用dev->open。并非所有設(shè)備驅(qū)動程序都初始化此函數(shù)
- 設(shè)置dev->state中的__LINK_STATE_START標識,把設(shè)備標識為開啟和運行中。
- 設(shè)置dev->flags中的IFF_UP標識,把設(shè)備標識為開啟。
- 調(diào)用dev_activate以初始化由流量控制使用的出口隊列規(guī)則,然后啟動看門狗定時器。如果流量控制沒有用戶配置,就指定默認的FIFO
- 傳送NETDEV_UP通知信息給netdev_chain通知鏈,以通知感興趣的內(nèi)核組件,該設(shè)備現(xiàn)已開啟。
設(shè)備關(guān)閉
- 傳送NETDEV_GOING_DOWN通知信息給netdev_chain通知鏈,以通知感興趣的內(nèi)核組件該設(shè)備即將被關(guān)閉
- 調(diào)用dev_deactivate以關(guān)閉出口隊列規(guī)則,使得該設(shè)備再也無法用于傳輸,然后因為不在需要,停止看門狗定時器
- 清除dev->state中的__LINK_STATE_START標識,把設(shè)備標識為關(guān)閉
- 如果一個輪詢動作被調(diào)度,以讀取設(shè)備上的入口封包,就要等待該動作完成。因為__LINK_STATE_START標識已被清除,該設(shè)備上已不能再為其他接收的輪詢動作進行調(diào)度了,但是在該標志清除前可能有一個輪詢動作未決。
- 如果有定義,調(diào)用dev->stop。
- 清除dev->flags中的IFF_UP標識,把設(shè)備標識為關(guān)閉
- 傳送NETDEV_DOWN通知鏈
鏈接狀態(tài)變更偵測
可能導致鏈接狀態(tài)變更的一些情況:
1,電纜線插入NIC,或者從NIC中拔出。
2. 電纜線另一端的設(shè)備電源關(guān)掉或關(guān)閉了。這類設(shè)備有HUB,橋接器,路由器以及PC NIC等
轉(zhuǎn)載于:https://my.oschina.net/longscu/blog/57354
總結(jié)
以上是生活随笔為你收集整理的网络设备的注册与初始化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AutoRobot微信/QQ定时自动发
- 下一篇: FastReport问题整理(转)