先強調一點:在一切可能的場景,儘可能地使用前向聲明(Forward Declaration)。這符合信息隱蔽的原則。
那麼前向聲明究竟是個什麼鬼?在內核寫代碼和看代碼的童鞋,經常發現Linux內核裡面充斥著這樣的代碼,比如
include/vim linux/regulator/driver.h
文件中:
我們以regmap這個結構體為例,這個地方就是一個前向聲明,告訴後面的代碼regmap是個結構體,至於這個結構體裡面有什麼鬼,不知道!
Linux可以說滿世界都在使用這個結構體。滿世界都在使用聲明在include/linux/regmap.h中的regmap_write()、regmap_read()這樣的API,可以說無處不在,無處不用,比如drivers/rtc/rtc-at91sam9.c中的:
這樣做帶來的一個極大好處是,drivers/base/regmap/外部的世界根本不需要知道regmap結構體長成什麼樣子,因為沒人需要知道,它們都只是在訪問regmap的指針!
而drivers/base/regmap/內部無論怎麼修改regmap結構體的實現和成員本身,對外部的世界根本不可見,修改regmap結構體後,drivers/base/regmap/以外的模塊都不需要重新編譯!
相反,如果我們直接把regmap結構體的內部細節暴露在include/linux/regmap.h這個頭文件中,那麼由於這個頭文件滿世界都被引用,你只要修改regmap結構體本身,就會導致內核無數模塊的增量編譯!
include/linux/regmap.h中暴露了regmap_config結構體,這說明這個結構體的內容需要被regmap以外的模塊知道:
...
為什麼,它涉及到具體的寄存器是如何讀寫的callback以及具體的寄存器pattern,這肯定是一個API基本的東西,本身就應該是跨模塊的東西,所以它的長相出現在了include/linux/regmap.h這個頂級頭文件中。
對於一個外部模塊而言,它只需要能夠通過regmap.h公開暴露的小部分寄存器配置接口,來通過類似regmap_init_mmio()這個的API來填充regmap結構體的內部實現。比如drivers/rtc/rtc-at91sam9.c中的:
上述代碼中,rtc->gpbr是一個struct regmap指針,regmap_init_mmio()在內部填充了regmap的本身實現。之後drivers/rtc/rtc-at91sam9.c再調用regmap_write()、regmap_read()的時候,這些API從regmap模塊內部調用我們填充進去的reg_bits、val_bits、reg_stride這些寄存器pattern,幫忙完成寄存器的最終讀寫。
理清關係
永遠用高內聚和低耦合的思想設計代碼。Linux內核2000萬行的代碼,不這麼設計肯定要崩盤。寫代碼不是得過且過。尤其做單片機寫裸奔程序的童鞋要特別注意,你們往往覺得玩Linux的童鞋代碼一層層套很傻逼,這是完全不正確的理解。