1. 程式人生 > >程式碼學習-Linux核心網絡卡收包過程(NAPI)

程式碼學習-Linux核心網絡卡收包過程(NAPI)

 本文通過學習RealTek8169/8168/8101網絡卡的驅動程式碼(drivers/net/r8169.c),梳理一下Linux下網絡卡的收包過程。

在下水平相當有限,有不當之處,還請大家斧正^_^

驅動的初始化

如下的rtl8169_init_module函式是此驅動的初始化程式碼,此函式只幹了一件事,就是向核心註冊一個pci驅動rtl8169_pci_driver。

static int __init rtl8169_init_module(void)

{

        returnpci_register_driver(&rtl8169_pci_driver);

}

rtl8169_pci_driver驅動的定義如下。

static struct pci_driver rtl8169_pci_driver= {

        .name               = MODULENAME,

        .id_table  = rtl8169_pci_tbl,

        .probe              = rtl8169_init_one,

        .remove            = __devexit_p(rtl8169_remove_one),

        .shutdown       = rtl_shutdown,

        .driver.pm        = RTL8169_PM_OPS,

};

.id_table成員是一個驅動程式支援的全部裝置列表。對於rtl8169_pci_driver,id_tabl就是b rtl8169_pci_tbl了,其內容如下。可見此驅動支援多種不同型號的網絡卡晶片。

static struct pci_device_idrtl8169_pci_tbl[] = {

        {PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8129),0, 0, RTL_CFG_0 },

        {PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8136),0, 0, RTL_CFG_2 },

        {PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8167),0, 0, RTL_CFG_0 },

        {PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168),0, 0, RTL_CFG_1 },

        {PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169),0, 0, RTL_CFG_0 },

        {PCI_DEVICE(PCI_VENDOR_ID_DLINK,       0x4300),0, 0, RTL_CFG_0 },

        {PCI_DEVICE(PCI_VENDOR_ID_AT,               0xc107),0, 0, RTL_CFG_0 },

        {PCI_DEVICE(0x16ec,                         0x0116),0, 0, RTL_CFG_0 },

        {PCI_VENDOR_ID_LINKSYS,  0x1032,   PCI_ANY_ID, 0x0024, 0, 0, RTL_CFG_0 },

        {0x0001,        0x8168,         PCI_ANY_ID, 0x2410, 0, 0, RTL_CFG_2 },

        {0,},

};

需要注意到,驅動中還有如下一行程式碼。

MODULE_DEVICE_TABLE(pci, rtl8169_pci_tbl);

這個巨集貌似是給rtl8169_pci_tbl變數起了一個別名__mod_pci_device_table。可見__mod_pci_device_table是pci裝置驅動中的一個統一的符號名。他包含了此驅動支援的全部裝置的列表。

這是幹什麼的呢?這時解釋一下。

如果此驅動被編譯到了核心中,或者此驅動已經被載入到核心中。那麼這一句話就沒什麼作用了。因為核心隨時可以根據rtl8169_pci_driver中的資訊,來判斷某一裝置是否匹配此驅動。程式碼見pci_match_device函式。

但是,如果此驅動被編譯成了一個模組檔案r8169.ko,並且沒有被載入到核心中(正常情況下,大量的裝置驅動都應該是被編譯成模組的,並且都是不載入到核心中的。機器上電時,根據掃描到的裝置,動態載入相應的驅動模組。不然的話,如果各種驅動都載入到核心中,那核心就太臃腫了)。此時,如果核心掃描到了一個pci裝置,就得載入相應的驅動模組檔案。但核心只掌握了此裝置的類似Vendorand device ID這樣的資訊,如何將這種資訊對應到具體的驅動模組檔案r8169.ko呢。這時候MODULE_DEVICE_TABLE這句話就發揮作用了。具體細節,可以參考udev與modprobe等相關知識。

這裡順便多說兩句,當一個pci驅動被載入到核心中時(見呼叫鏈pci_register_driver ->__pci_register_driver -> driver_register ->bus_add_driver ->driver_attach),或者當核心發現一個新裝置時(見呼叫鏈device_add->bus_probe_device->device_attach),都會做一次驅動與裝置的匹配操作。

probe一塊網絡卡rtl8169_init_one

當某一塊網絡卡匹配了rtl8169_pci_driver時,rtl8169_pci_driver. probe函式(即rtl8169_init_one)即被呼叫,此函式針對此網絡卡做一些初始化操作,然後此網絡卡就可用了。

這裡順便說一下,一個裝置,如何與業務流程關聯起來。不同的裝置,可能是不一樣的。

例如,有些裝置(如看門狗裝置,塊裝置),是在檔案系統中建立一個檔案(如/dev/ watchdog)。業務通過開啟裝置檔案,操作/讀/寫裝置檔案,就將裝置用起來了。

而網絡卡裝置,則不是這樣。網絡卡裝置是向核心註冊一個struct  net_device結構。註冊以後,ifconfig命令就能看到此網絡卡了。核心協議棧及路由系統也就與此net_device結構關聯起來了。struct  net_device結構,是核心對網路裝置的一種抽象,他使得核心可以用統一的方式操作一切網路裝置。

下面看看rtl8169_init_one的主要任務:

l  將網絡卡配置暫存器區間對映到核心虛存空間

l  執行硬體初始化

l  構建一個net_device結構,註冊到核心中

這裡需要多說的是net_device結構的構建。net_device結構類似於面向物件程式設計中的多型。前面說過,struct  net_device結構,是核心對網路裝置的一種抽象,他使得核心可以用統一的方式操作一切網路裝置。具體的網絡卡驅動,如何各自以不同的方法實現自己的功能呢。每個net_device結構上,除了通用的內容外,還有一片私有空間用於儲存各個網絡卡的私有資料。通過netdev_priv函式即可得到一個net_device結構的私有空間。R8169驅動就在這個私有空間中儲存了一個struct rtl8169_private結構,用於儲存R8169系列網絡卡的私有資料。這裡就不詳細說明了,但後面會根據需要提到其中的某些成分。

net_device結構中包含一個指標netdev_ops,指向一個struct net_device_ops結構,此結構中包含了指向網絡卡的各種操作的函式指標。這種設計使得核心可以對於任何網絡卡,看到一個統一的操作介面。不同的網絡卡驅動,將自己實現的各種操作的函式指標填到一個net_device_ops結構中,然後將此結構的地址填到net_device結構的netdev_ops指標中即可。

對於R8169驅動,這個net_device_ops結構就是rtl8169_netdev_ops。

開啟網絡卡rtl8169_open

當用戶執行ifconfig  eth0 up命令啟動一個網絡卡時,網絡卡對應的net_device的netdev_ops->ndo_open函式被呼叫(呼叫鏈:sys_ioctl->do_vfs_ioctl->vfs_ioctl->sock_ioctl->dev_ioctl->dev_ifsioc ->dev_change_flags->dev_open),對於R8169驅動來說就是rtl8169_open函式。

這裡為理解收包過程,列出rtl8169_open中的部分操作:

1)    申請一個struct RxDesc型別的陣列空間,地址儲存到rtl8169_private結構的RxDescArray成員中。

struct RxDesc結構用於描述一個buffer,主要是包含一個buffer的實體地址與長度。

rtl8169_private結構的RxDescArray成員就存放了RxDesc陣列的起始實體地址。接下來,程式碼會預先申請一些buffer(rtl8169_rx_fill函式中實現,最終是呼叫__alloc_skb分配的buffer。Tcp傳送資料時,最終也是呼叫__alloc_skb分配buffer的,可以參考tcp_sendmsg函式),然後將這些buffer的實體地址及長度記錄到RxDesc陣列中,以供硬體收包使用。

2)    申請一個struct sk_buff *型別的陣列空間,地址儲存到rtl8169_private.Rx_skbuff成員中。

上面提到的預先申請的那麼buffer,其核心態虛擬地址均記錄到此陣列中。這樣的話,硬體將報文輸出到buffer中後,驅動能夠獲取到相應的buffer地址,將報文傳入核心協議棧。從程式碼來看,buffer存放一個報文。

3)    註冊中斷處理函式rtl8169_interrupt

當網絡卡收到報文時,核心的框架程式碼最終會呼叫到這裡註冊的中斷處理函式。

4)    enable網絡卡的napi

5)    啟動網絡卡

這裡涉及諸多硬體操作,我們的主要關注點是,1)中提到的實體地址通過rtl_set_rx_tx_desc_registers函式(呼叫鏈rtl8169_open->rtl_hw_start->rtl_hw_start_8169->rtl_set_rx_tx_desc_registers)寫給了硬體。

這樣一來,這就等於通過RxDesc陣列,等於向硬體提供了一組buffer的資訊。從而讓硬體將收到的報文輸出到這些buffer中

中斷處理

當中斷髮生時,硬體已經將報文輸出到了前面所說的預先申請的buffer中了。此時,系統的中斷處理機制最終會呼叫rtl8169_interrupt進行中斷處理。

這裡為理解收包過程,列出rtl8169_interrupt所做的部分操作:

l  處理中斷硬體層面相關工作

l  呼叫__napi_schedule將網絡卡的rtl8169_private.napi結構掛入__get_cpu_var(softnet_data).poll_list連結串列。

l  呼叫__raise_softirq_irqoff(NET_RX_SOFTIRQ);讓軟中斷處理執行緒ksoftirqd被排程執行,此執行緒將負責完成報文的接收。

軟中斷處理執行緒ksoftirqd

前面說了,網絡卡中斷髮生後,會觸發軟中斷處理執行緒ksoftirqd被排程執行,而此執行緒將會負責完成報文的接收。那麼此執行緒是個什麼東東呢?這裡先簡單介紹一下。

每個核上,都會建立一個ksoftirqd執行緒,專門負責處理軟中斷。

如果沒有配置CONFIG_PREEMPT_SOFTIRQS,則ksoftirqd 執行緒是在cpu_callback中通過如下程式碼建立的。可見這種情況下,執行緒的處理函式就是ksoftirqd。

kthread_create(ksoftirqd, hcpu,"ksoftirqd/%d", hotcpu);

通過如下命令,可以檢視當前機器上ksoftirqd執行緒的建立情況。

[[email protected] VMB]# ps -ef | grep irq

root        4     2  0 May18 ?        00:00:00 [ksoftirqd/0]

root        9     2  0 May18 ?        00:00:00 [ksoftirqd/1]

ksoftirqd軟中斷處理執行緒並不是專門負責網絡卡裝置的軟中斷處理,他還負責其他各種裝置的軟中斷處理。

核心的各個子系統,通過open_softirq註冊相應的軟中斷處理條目。

下面是網路系統與塊裝置系統註冊軟中斷處理條目的程式碼。

open_softirq(BLOCK_IOPOLL_SOFTIRQ,blk_iopoll_softirq);

open_softirq(BLOCK_SOFTIRQ,blk_done_softirq);

open_softirq(NET_TX_SOFTIRQ,net_tx_action);

open_softirq(NET_RX_SOFTIRQ, net_rx_action);

open_softirq的程式碼如下。由此可見,每個條目,其實就是一個軟中斷處理函式。那麼網絡卡收包軟中斷就對應net_rx_action函數了。

void open_softirq(int nr, void(*action)(struct softirq_action *))

{

        softirq_vec[nr].action= action;

}

Ksoftirqd最終呼叫__do_softirq中完成各種軟中斷任務的處理。

__do_softirq 遍歷softirq_vec陣列,執行每個條目的action。

對網絡卡收包來說,action就是net_rx_action函數了。

網絡卡收包

前面說了,網絡卡中斷髮生後,會觸發軟中斷處理執行緒ksoftirqd被排程執行,而此執行緒將會負責完成報文的接收。具體如何接收呢,從前面的介紹可以知道,對ksoftirqd來說,其實就是呼叫net_rx_action函式而已。

下面看看net_rx_action函式的工作:

前面說過,中斷來了,網絡卡驅動將自己的napi結構掛到了__get_cpu_var(softnet_data).poll_list連結串列中。那麼net_rx_action的核心工作,就是從連結串列中一一取出其中的napi結構,執行napi結構中的poll成員所指向的函式。為什麼是一一取區呢?因為可能不止一塊網絡卡在發生了中斷後,將自己的napi結構掛進了連結串列。

對於R8169驅動來說,其napi結構poll成員指向的函式就是rtl8169_poll。這是在rtl8169_init_one中設定好的。實際上,rtl8169_poll中既做報文接收工作又做報文傳送完成後的善後工作。從程式碼來看,rtl8169_start_xmit負責傳送工作,程式碼中將要傳送的報文的buffer資訊填入rtl8169_private.TxDescArray陣列中,然後寫暫存器(RTL_W8(TxPoll, NPQ);)通知硬體發包。硬體完成傳送後,同樣會上報中斷。然後rtl8169_poll呼叫rtl8169_tx_interrupt對rtl8169_private.TxDescArray中的buffer描述資訊置空,以供未來新的報文傳送使用。因此,不管是收,還是發,最終都是產生中斷,然後由rtl8169_interrupt中斷處理將流程轉入軟中斷處理執行緒ksoftirqd,再由軟中斷進入rtl8169_poll函式處理。

這裡,我們只看接收相關的程式碼。很明顯,接收工作是由rtl8169_rx_interrupt函式完成的。

我們這裡不看硬體相關的程式碼,只分析純粹的收包相關的程式碼。

前面提到,為了收包,預先申請了一批buffer。這些buffer的資訊,存在瞭如下兩個陣列中。

第一個是給硬體看的,第二個是給驅動看的。

rtl8169_private.RxDescArray

rtl8169_private.Rx_skbuff

rtl8169_private.Rx_skbuff就是一個環型陣列,每個元素就是一個指向struct sk_buff結構的指標。rtl8169_rx_interrupt遍歷此陣列,取出其中的一個個報文,呼叫協議棧報文接收函式netif_receive_skb即可。

從程式碼實現來看,驅動總是先嚐試重新申請一個sk_buff,將硬體接收buffer中的報文拷出來。但是,如果拷貝失敗,那就不拷了,直接將硬體接收buffer中的報文轉入協議棧接收流程。程式碼這樣做,可能是不想重新申請buffer給硬體接收使用。

當拷貝失敗時,由於程式碼直接將硬體接收buffer中的報文轉入協議棧接收流程。這樣的話,這個buffer就不能再繼續用作硬體接收buffer了。因此對於這種情況,程式碼就將rtl8169_private.Rx_skbuff[idx]置成NULL。這樣的話,可用的硬體接收buffer就變少了。為了應對這種情況,rtl8169_rx_interrupt函式尾部會呼叫rtl8169_rx_fill嘗試重新將接收buffer補滿。

核心協議棧對報文的接收

前面看到,網絡卡驅動呼叫netif_receive_skb,將處理流程轉入核心協議棧。

netif_receive_skb先跳過一些簡單的和不用關心的程式碼,從下面的地方開始看。

可見,如果接收埠是一個bond的成員口,則skb中的接收埠skb->dev需要換成介面埠的master,即bond口。但也未必總是會換,因為有時候成員口還未起來,但是收到一些雜包,這時候這些雜包不屬於bond口的流量,因此不換。

        null_or_orig= NULL;

        orig_dev= skb->dev;

        if(orig_dev->master) {

                 if(skb_bond_should_drop(skb))

                         null_or_orig = orig_dev; /*deliver only exact match */

                 else

                         skb->dev= orig_dev->master;

        }

接下來,先通過如下程式碼將報文送達可能存在的raw socket(PF_PACKET協議族)。

        list_for_each_entry_rcu(ptype,&ptype_all, list) {

                 if(ptype->dev == null_or_orig || ptype->dev == skb->dev ||

                     ptype->dev == orig_dev) {

                         if(pt_prev)

                                  ret= deliver_skb(skb, pt_prev, orig_dev);

                         pt_prev= ptype;

                 }

        }

這些報文接收條目是通過dev_add_pack註冊的。

接下來,將報文傳遞給bridge處理。如果這裡一步返回了0,報文就不往下走了。

        skb= handle_bridge(skb, &pt_prev, &ret, orig_dev);

        if(!skb)

                 gotoout;

否則,通過如下程式碼,將報文傳達給各個協議處理。

        type= skb->protocol;

        list_for_each_entry_rcu(ptype,

                         &ptype_base[ntohs(type)& PTYPE_HASH_MASK], list) {

                 if(ptype->type == type &&

                     (ptype->dev == null_or_orig ||ptype->dev == skb->dev ||

                      ptype->dev == orig_dev)) {

                         if(pt_prev)

                                  ret= deliver_skb(skb, pt_prev, orig_dev);

                         pt_prev= ptype;

                 }

        }

這裡的各個接收條目,也是通過dev_add_pack註冊的。看看其程式碼,報文接收條目有兩種,一種是全接收,一種是單收。

void dev_add_pack(struct packet_type *pt)

{

        inthash;

        spin_lock_bh(&ptype_lock);

        if(pt->type == htons(ETH_P_ALL))

                 list_add_rcu(&pt->list,&ptype_all);

        else{

                 hash= ntohs(pt->type) & PTYPE_HASH_MASK;

                 list_add_rcu(&pt->list,&ptype_base[hash]);

        }

        spin_unlock_bh(&ptype_lock);

}

來看看IP協議的接收條目的定義:

static struct packet_type ip_packet_type__read_mostly = {

        .type= cpu_to_be16(ETH_P_IP),

        .func= ip_rcv,

        .gso_send_check= inet_gso_send_check,

        .gso_segment= inet_gso_segment,

        .gro_receive= inet_gro_receive,

        .gro_complete= inet_gro_complete,

};

順便也看看arp協議的接收條目定義(arp的學習就是通過arp_rcv完成的, arp的查詢則是通過neigh_lookup介面):

static struct packet_type arp_packet_type __read_mostly = {
.type = cpu_to_be16(ETH_P_ARP),
.func = arp_rcv,
};

從ip_packet_type可知,IP報文接收的入口是ip_rcv

如果是本機接收,主處理呼叫鏈如下:

ip_rcv->ip_rcv_finish-> dst_input->skb_dst(skb)->input (即ip_local_deliver)

ip_local_deliver主要是根據協議,選擇一個協議來處理。

hash = protocol & (MAX_INET_PROTOS -1);

ipprot =rcu_dereference(inet_protos[hash]);

ipprot->handler(skb);

這些協議是通過inet_add_protocol註冊的。例如,UDP協議的註冊通過如下程式碼。

inet_add_protocol(&udp_protocol,IPPROTO_UDP)

udp_protocol的定義如下:

static const struct net_protocoludp_protocol = {

        .handler=        udp_rcv,

        .err_handler= udp_err,

        .gso_send_check= udp4_ufo_send_check,

        .gso_segment= udp4_ufo_fragment,

        .no_policy=   1,

        .netns_ok=     1,

};

可見UDP的接收函式是udp_rcv

如果是一般的UDP,接收過程如下:

sock_queue_rcv_skb

呼叫udp_rcv ->__udp4_lib_rcv->udp_queue_rcv_skb ->__udp_queue_rcv_skb ->sock_queue_rcv_skb ->sk->sk_data_ready(即sock_def_readable)

最後一個函式sock_def_readable用於喚醒因讀取socket進入睡眠的執行緒。

相關推薦

程式碼學習-Linux核心過程(NAPI)

 本文通過學習RealTek8169/8168/8101網絡卡的驅動程式碼(drivers/net/r8169.c),梳理一下Linux下網絡卡的收包過程。 在下水平相當有限,有不當之處,還請大家斧正^_^ 驅動的初始化 如下的rtl8169_init_module函式是

linux核心--接收資料的函式呼叫關係

網絡卡中斷函式 e1000_intr()     -------->加入napi連結串列 __napi_schedule()-------->啟動napi軟中斷 __raise_softirq_irqoff()-------->軟中斷處理函式 net_rx_

Linux核心---驅動的詳細分析 讓你的飛起來!

 Linux核心學習筆記之網絡卡驅動的詳細分析(經典轉) 學習應該是一個先把問題簡單化,在把問題複雜化的過程。一開始就著手處理複雜的問題,難免讓 人有心驚膽顫,捉襟見肘的感覺。讀Linux網絡卡驅動也是一樣。那長長的原始碼夾雜著那些我們陌生的變數和符號,望而生畏便是理所當然

嘗試探索基於Linux C的過程

        其實想探究網絡卡抓包問題已經有好久了。前幾天找了時間算是基本上了解了那部分的一些基本東西,在這裡只是贅述罷了。 抓包首先便要知道經過網絡卡的資料其實都是通過底層的鏈路層(MAC),在Linux系統中我們獲取網絡卡的資料流量其實是直接從鏈路層收發資料幀。至於如

學習記錄——Linux繫結

1#檢視網絡卡連線資訊。 nmcli connection show 2#刪除網絡卡配置資訊。 nmcli connection delete 網絡卡名稱或者通用唯一識別符號(即UUID) 3#新增虛擬網絡卡bond。 nmcli connection ad

Linux 無線連線 WIFIWPA-PSK

Linux 無線網絡卡連線 WIFIWPA-PSK //編輯/etc/wpa_supplicant/wpa_supplicant.conf 檔案,內容如下 ctrl_interface=/var/run/wpa_supplicant ctrl_interface_group=whe

linux無法上網

一、測試環境說明 (1)使用工具:vbox(5.2.20 r125813), Centos7(CentOS-7-x86_64-Minimal-1804) (2)裝機時網路設定,如下圖: (3)虛機機配置檔案 cat /etc/sysconfig/network-scripts/ifcfg

Linuxbond、起子介面

適用場景 伺服器兩張網絡卡需要做bond,並且bond後網絡卡需配置不同網段的地址,用於走不同流量,這個時候就可以採用起子介面的方式。 實驗場景 裝置 伺服器:Server_A 核心交換機:Switch_A、Switch_B 交換機連線方式:堆疊 伺服器網絡

Linux修改地址(臨時/永久)

1.臨時修改(重啟服務或者重啟系統後失效): ifconfig eth0 192.168.1.1 netmask 255.255.255.0 縮略:ifconfig eth0 192.168.1.1 (eth0是第一個網絡卡,eth1是第二個)   2  永久修改

Debian-Linux配置網路方法

Debian不同於centos系統,網絡卡配置不是在/etc/sysconfig/network-scrip裡面,而是在/etc/network/interfaces裡面 1.Debian網路配置 配置網絡卡:修改vi /etc/network/interfaces 新增如下 auto eth

linux 確定介面

方法: ifconfig -a 執行一遍:ifconfig -a;  插上網線 介面會 running。         方法:ethtool  執行 # ethtool -p eth0 時,eth0對應的

linux 修改eth0的ip地址

1:臨時修改: 1.1:修改IP地址 ifconfig eth0 192.168.100.100 1.2:修改閘道器地址 route add default gw 192.168.100.1 dev eth0 1.3:修改DNS echo “nameserver 8.8.

l(轉)Linux DM9000驅動程式完全分析

[置頂] Linux DM9000網絡卡驅動程式完全分析 分類: Linux裝置驅動程式第三版學習筆記 2011-02-26 16:11 3513人閱讀 評論(34) 收藏 舉報 說明1:本文分析基於核心原始碼版本為linux-2

檢視linux伺服器頻寬

   centos/redhat伺服器的的網路配置主要在  /etc/sysconfig/network-scripts/ifcfg-xxx  至於是  ifcfg-xxx是那個需要檢視系統安裝時的配置,如果未知,可以執行ifconfig,檢視i

Linux 檢視全雙工 還是半雙工 以及設定為半雙工

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Linux繫結指令碼

linux運維及配置工作中,常常會用到雙網絡卡繫結,少數幾臺伺服器的配置還好,如果是需要配置幾十甚至上百臺,難免會枯燥乏味,易於出錯,我編寫了這個雙網絡卡繫結的輔助指令碼,可傻瓜式地完成linux雙網絡卡繫結工作,當然,該指令碼主要還是用於小批量的系統配置,如需配置大量的伺服器,可提取指令碼中的bonding

詳解Linux繫結之bond0

1、什麼是bond?    網絡卡bond是通過多張網絡卡繫結為一個邏輯網絡卡,實現本地網絡卡的冗餘,頻寬擴容和負載均衡,在生產場景中是一種常用的技術。Kernels 2.4.12及以後的版本均供bonding模組,以前的版本可以通過patch實現。 2、實現原理:  

02 Linux繫結

 Linux 多網絡卡繫結 網絡卡繫結mode共有七種(0~6) bond0、bond1、bond2、bond3、bond4、bond5、bond6 常用的有三種 mode=0:平衡負載模式,有自動備援,但需要”Switch”支援及設定。 mode=1:自

C/C++:Windows程式設計—程式碼獲取本地所有資訊(描述,IP地址,子掩碼,MAC地址)

先看效果 看程式碼 使用 GetAdaptersInfo 函式獲取網絡卡的所有資訊。 MSDN函式說明 https://docs.microsoft.com/en-us/windows/desktop/api/iphlpapi/nf-iphlpapi-getadapters

實時監控linux伺服器流量命令 iftop

iftop是類似於linux下面top的實時流量監控工具。 iftop可以用來監控網絡卡的實時流量(可以指定網段)、反向解析IP、顯示埠資訊等,詳細的將會在後面的使用引數中說明。 yum install -y iftop iftop 介面說明 介面頭部:   流量刻度