MAC地址是以太網二層使用的一個48bit(6字節十六進制數)的地址,用來標識設備位置。MAC地址分成兩部分,前24位是組織唯一標識符(OUI, Organizationally unique identifier),後24位由廠商自行分配。

    MAC地址有單播、組播、廣播之分。單播地址(unicast address)表示單一設備、節點,多播地址或者組播地址(multicast address、group address)表示一組設備、節點,廣播地址(broadcast address)是組播的特例,表示所有地址,用全F表示:FF-FF-FF-FF-FF-FF。當然,三層的IP地址也有單播、組播、廣播之分。

    48bit的MAC地址一般用6字節的十六進制來表示,如XX-XX-XX-XX-XX。IEEE 802.3規定:以太網的第48bit用於表示這個地址是組播地址還是單播地址。如果這一位是0,表示此MAC地址是單播地址,如果這位是1,表示此MAC地址是多播地址。見IEEE 802.3 3.2.3 Address fields: “The first bit (LSB) shall be used in the Destination Address field as an address type designation bit to identify the Destination Address either as an individual or as a group address. If this bit is 0, it shall indicate that the address field contains an individual address. If this bit is 1, it shall indicate that the address field contains a group address that identifies none, one or more, or all of the stations connected to the LAN. In the Source Address field, the first bit is reserved and set to 0.”

    因為以太網線路上按“Big Endian”字節序傳送報文(也就是最高字節先傳送,關於字節序請參考相關文檔),而比特序是”Little Endian”(也就是最低位先傳送)。所以有如下的圖(從最左邊開始傳送):

組播 add membership_#define

    注意圖上的第47bit,這一位表示MAC地址是全球唯一地址還是本地地址,0表示全球唯一地址,1表示本地唯一地址。這一位也叫G/L位。

    對於網絡設備上固化的MAC地址,因為它唯一標識這個設備,所以只能是單播地址,也就是MAC幀裏面的Source地址第48位只能0。    

    我們常説有2的48次方個MAC地址可供網絡設備使用,這些地址可以多到給地球上每一粒沙子分配一個地址,其實這個數量要打折扣的,因為MAC地址雖然有這麼多,但真正用在網卡上並且全球唯一的只有2的46次方個:第48bit一定是0,第47bit一定是0。

    這也就引出了一個有意思的現象:隨便找一台PC,觀察一下它的網卡地址,第1字節的十六進制數一般是4的倍數;查看一下IEEE分配的OUI(http://standards.ieee.org/develop/regauth/oui/oui.txt ),第1字節的十六進制數也一般是4的倍數(早期以太網沒有本地地址的概念,所以分配的OUI裏面G/L bit也可能是1),這種情況下就不是4的倍數了,但肯定是2的倍數,因為第48位只能是0。

    關於組播地址,有這麼個誤解:MAC地址第1字節必須是0x01才表示組播地址,連TCP/IP詳解上也這麼説(見中文版12.4.2第一段)。IEEE 802.3裏面已經明確説明了只要第48bit是1就表示組播地址,所以無論MAC地址第1字節是0x01、0xC1或者是0x33都表示這個MAC地址是組播地址(以0x33開頭的表示IPV6對應的二層組播地址)。之所以有這樣的誤解,是因為到目前為止,大部分組播MAC地址的第1字節都是0x01。如:

01-80-C2-00-00-00(STP協議使用)

01-80-C2-00-00-01(MAC Control的PAUSE幀使用)

01-80-C2-00-00-02(Slow Protocol: 802.3ah OAM/ LACP 協議都用這個地址,這個地址很有故事,有多少軟件處理這個地址會出問題啊!)

01-00-5E-xx-xx-xx(IP組播地址對應的二層組播地址)。

完整的列表見http://standards.ieee.org/develop/regauth/grpmac/public.html

    之所以大部分組播地址都以01-80-C2和01-00-5E開頭,那是因為使用這些組播地址的協議都是帶頭大哥IEEE和IANA名下的,它們的OUI分別是00-80-C2和00-00-5E是,變成組播地址就是01-80-C2和01-00-5E了,當然,除了帶頭大哥霸佔的這些組播地址,還有01-00-0C-CC-CC-CC這樣的地址,這個地址是Cisco霸佔的,Cisco的OUI是00-00-0C。

處理模型


Linux kernel 的啓動包括很多組件的初始化和相關配置,這些配置參數一般是通過command line 進行配置的。在進行後續分析之前,先來理解一下command line


要處理的對象是一個字符串,其中包含了各種配置信息,通常各個配置之間通過空格進行分離,每個配置的表達形式是如:param=value1,value2或者很簡單就是一個rw


那麼kernel 就需要提供對這些參數進行處理的處理函數列表。根據參數的作用以及執行期的先後不同,這些處理函數被定義到不同的段中。針對每一個參數,Kernel


1 配置格式


常見的配置格式如:




console=ttySAC0,115200 root=nfs nfsroot=192.168.1.9:/source/rootfs initrd=0x10800000,0x14af47



2 配置方式

2.1 Bootloader動態配置


由bootloader 進行參數配置,command line 將做為atag_list 的一個節點傳遞到Kernel。


2.2 Kernel 靜態配置


通過make menuconfig 進行配置:運行後配置boot options->Default kernel command string 。該配置將被靜態編譯到Kernel中,通過變量default_command_line


3  解析配置

3.1 相關定義


根據執行的先後順序,可以將處理函數分為三個大類,他們分別存在於下面三個段中(參考top/arch/arm/kernel/vmlinux.lds ):




__setup_start = .; *(.init.setup) __setup_end = .;






__early_begin = .; *(.early_param.init) __early_end = .;





__start___param = .; *(__param) __stop___param = .;





這三個段內存儲的不是參數,而是command line


3.1.1 .early_param.init段


“.early_param.init ” 所定義的處理相對靠前一些,它所處理的參數例如:initrd= ,cachepolicy= ,nocache, nowb , ecc= , vmalloc= , mem=


這些處理函數是通過__early_param宏來定義的,例如:


static void __init early_initrd(char **p)
 { …… }
 __early_param("initrd=", early_initrd);


對於宏__early_param,可以在top/arch/arm/include/asm/Setup.h




struct early_params {
     const char *arg;
     void (*fn)(char **p);
 };
 #define __early_param(name,fn) \
 static struct early_params __early_##fn __used \
 __attribute__((__section__(".early_param.init"))) = { name, fn }





3.1.2 .init.setup



“.init.setup ”定義的處理則要靠後一些,它所處理的參數例如:nfsroot= , ip=


這些處理函數是通過__setup宏來定義的,例如:



static int __init nfs_root_setup(char *line)
 { …… }
 __setup("nfsroot=", nfs_root_setup);



對於宏__setup,可以在top/include/linux/Init.h




#define __setup_param(str, unique_id, fn, early) \
     static char __setup_str_##unique_id[] __initdata __aligned(1) = str; \
     static struct obs_kernel_param __setup_##unique_id \
            __used __section(.init.setup) \
            __attribute__((aligned((sizeof(long))))) \
            = { __setup_str_##unique_id, fn, early }

 #define __setup(str, fn) \
     __setup_param(str, fn, fn, 0)

/* NOTE: fn is as per module_param, not __setup! Emits warning if fn
 * returns non-zero. */
 #define early_param(str, fn) \
     __setup_param(str, fn, fn, 1)



注意看的話,可以看到還有一個宏early_param,它與宏__setup的定義相似,只不過最後一個宏參數是1 而不是0 。1表示需要提前處理的參數。


3.1.3 __param段


這個段中保存的是build-in 類型module


3.2  解析

3.2.1 相關變量


相關的變量包括:


default_command_line


保存memuconfig 配置的參數,如果bootloader


boot_command_line


存在於.init.data 段。最初是default_command_line


command_line


存在於.init.data 段。在parse_cmdline() 中被賦值,數據來源是default_command_line


saved_command_line


用於保存沒有處理過的命令行參數,是boot_caommand_line


static_command_line


是command_line


3.2.2 主要函數


函數名稱:parse_cmdline()


操作數據:default_command_line


函數列表: .early_param.init 段(在__early_begin 和__early_end


函數功能: 依據函數列表對default_command_line




函數名稱:parse_early_param()


操作數據:boot_command_line


函數列表: .init.setup 段中(__setup_start 和__setup_end 之間),主要是通過宏early_param定義的部分。


函數功能: 依據函數列表對boot_command_line


注意parse_one() 的第四個入參是0 ,而且第五個參數是NULL 。這裏沒有給出參數隊列,不會對boot_command_line的每個參數在參數隊列中進行對比查找,而是直接在do_early_param()




if ((p->early && strcmp(param, p->str) == 0) ||
                   (strcmp(param, "console") == 0 &&
                    strcmp(p->str, "earlycon") == 0)
               )



函數名稱:parse_args()


操作數據:static_command_line


函數列表: __param 段(__start___param 和__stop___param


函數功能: 該操作將依據函數列表,對static_command_line 中的參數進行相應的操作。這個操作在parse_one()





for (i = 0; i < num_params; i++) {
           if (parameq(param, params[i].name)) {
                  DEBUGP("They are equal! Calling %p\n",
                         params[i].set);
                  return params[i].set(val, ¶ms[i]);
           }
    }


接下來對於不被這個列表所支持的參數,將在unknown_bootoption() 中進行處理。在unknown_bootoption() 中主要是obsolete_checksetup()的操作。




函數名稱:obsolete_checksetup()


操作數據:static_command_line


函數列表: .init.setup 段中(__setup_start 和__setup_end 之間),主要是通過宏__setup定義的部分。


函數功能: 該操作將依據函數列表,對static_command_line 中的參數進行相應的操作。如果是在parse_early_param()


3.2.3圖示

組播 add membership_組播_02