帶你瞭解什麼是核心匯流排架構
當裝置或者驅動新增到連結串列時,會觸發匯流排的match函式。那麼,您有沒有深入去研究過核心匯流排呢?在本文中,我們來深入探討一下核心中的匯流排,主要涉及到以下問題:
1.核心中是如何部署匯流排的。
2.裝置和驅動是如何掛載到匯流排上的。
3.裝置和其對應的驅動是如何透過匯流排進行匹配的。
我們從函式start_kernel來分析匯流排的部署,實際上在函式start_kernel呼叫之前,會有彙編程式碼來處理啟動引數,啟動模式,建立核心空間頁表,準備好堆疊等。由於這些同匯流排部署關係不大,暫且就認為start_kernel就是核心的main函式。start_kernel內部會呼叫rest_init,rest_init函式內部建立核心執行緒kernel_init,而kernel_init中有如下的函式呼叫過程:
kernel_init-->do_basic_setup->driver_init—>buses_init和platform_bus_init
此處的buses_init和platform_bus_init就是匯流排的部署函式,也是本小節的重點,且buses_init必須在platform_bus_init前面呼叫。因為Platform匯流排是掛載在bus匯流排下的,接下來我們詳細分析下這兩個過程。
核心中所有的物件如bus,都是一個kobject,而把相同型別的kobject集合到一起就組成了一個kset,而函式buses_init內部就是透過bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL)來註冊bus匯流排對應的kset,其最後bus_kset如下圖1所示:
圖 1 bus_kset結構
至此算是準備好了bus_kset,我們繼續往下看一下其他型別的匯流排是如何和bus進行關聯的。
該函式主要完成兩個功能,其函式如下:
struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, .pm = &platform_dev_pm_ops, }; EXPORT_SYMBOL_GPL(platform_bus_type); int __init platform_bus_init(void) { int error; early_platform_cleanup(); error = device_register(&platform_bus); if (error) return error; error = bus_register(&platform_bus_type); if (error) device_unregister(&platform_bus); return error; }
device_register是用來註冊一個device,並新增到系統中,最後會在/sys/devices/目錄下建立 platform目錄對應的裝置物件,其路徑是/sys/devices/platform/。
bus_register是將Platform bus匯流排註冊進系統,其實內部就是建立了對應的kset和kobject等,且主要完成以下三項工作:
初始化必須的結構體,struct subsys_private 和對應的kobkect。
同bus匯流排建立關係,kobject.parent 設定為上一步已經建立好的bus_kset.kobj, kobject.kset設定為bus_kset,把對應的kobject.ktype設定為bus_ktype。
把對應的kobjet新增到對應的kset的連結串列中,對匯流排來說,就是新增到bus_kset中的連結串列中。
下面是bus_register函式的實現(刪除了建立失敗退出時free記憶體等的操作),且我在程式碼中增加了註釋,方便大家查閱:
/** * bus_register - register a bus with the system. * @bus: bus. * * Once we have that, we registered the bus with the kobject * infrastructure, then register the children subsystems it has: * the devices and drivers that belong to the bus. */ int bus_register(struct bus_type *bus) { int retval; //step:建立並分配,初始化struct subsys_private結構體指標 struct subsys_private *priv; priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); if (!priv) return -ENOMEM; priv->bus = bus; bus->p = priv; BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); if (retval) goto out; //step2:同上面建立的bus_kset進行關聯 //kset_register時,會設定對應priv->subsys。Kobject->parent = bus_kset.kobj priv->subsys.kobj.kset = bus_kset; priv->subsys.kobj.ktype = &bus_ktype; priv->drivers_autoprobe = 1; retval = kset_register(&priv->subsys); //一會新增到bus_kset連結串列中 if (retval) goto out; //step3:建立對應的屬性檔案 retval = bus_create_file(bus, &bus_attr_uevent); if (retval) goto bus_uevent_fail; priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj); if (!priv->devices_kset) { retval = -ENOMEM; goto bus_devices_fail; } priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj); if (!priv->drivers_kset) { retval = -ENOMEM; goto bus_drivers_fail; } //step4:初始化兩個比較重要的連結串列,後面內容中會提到這兩個連結串列 klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); klist_init(&priv->klist_drivers, NULL, NULL); //step5:新增探針檔案 retval = add_probe_files(bus); if (retval) goto bus_probe_files_fail; retval = bus_add_attrs(bus); if (retval) goto bus_attrs_fail; pr_debug("bus: '%s': registered\n", bus->name); return 0; …… return retval; } EXPORT_SYMBOL_GPL(bus_register);
對於其他匯流排(如IIC等),也是透過bus_register進行註冊的,比如bus_register(&i2c_bus_type)和bus_register(&mmc_bus_type)等,其原理同上面一樣,在此就不挨個介紹了。
透過上面的分析,我們知道了bus匯流排,且其他匯流排掛載在bus匯流排下,等匯流排部署完成後,不同裝置會掛載在對應的匯流排下面。對於SPI,IIC等裝置,他們都可以掛載在對應的匯流排下同CPU進行資料互動。但在嵌入式系統中,有些裝置是不屬於這些常見的匯流排,因此引入了虛擬的Platform匯流排,本小節正是透過虛擬的Platform匯流排來說明匯流排部署的。
我們依然採用Platform匯流排來說明裝置和驅動的掛載問題。
對於Platform匯流排來說,可以透過函式platform_device_register來掛載(有的地方稱之為註冊)裝置,也可以透過裝置樹來掛載,在核心啟動時,會進行裝置樹的解析,本文中不涉及裝置樹,主要介紹platform_device_register的方式。
該函式原型如下:
int platform_device_register(struct platform_device *pdev) { device_initialize(&pdev->dev); return platform_device_add(pdev); }
函式在執行的過程中,有如下呼叫關係:
platform_device_add---->設定struct platform_device中的匯流排型別及其他引數--->device_add--->bus_add_device---->klist_add_tail
這個呼叫過程省略了一些屬性和節點等的處理,我關注的重點在函式klist_add_tail,該函式是把當前裝置新增到platform_bus中的一個連結串列中,這個連結串列在Platform匯流排部署時就初始化完成了,其初始化函式就是函式bus_register中的step4,可以翻閱上一個小節來檢視。
對於Platform匯流排來說,可以透過函式platform_driver_register來掛載(有的地方稱之為註冊)裝置,其函式原型如下:
int platform_driver_register(struct platform_driver *drv) { drv->driver.bus = &platform_bus_type; if (drv->probe) drv->driver.probe = platform_drv_probe; if (drv->remove) drv->driver.remove = platform_drv_remove; if (drv->shutdown) drv->driver.shutdown = platform_drv_shutdown; return driver_register(&drv->driver); } EXPORT_SYMBOL_GPL(platform_driver_register);
函式在執行的過程中,有如下呼叫關係:
driver_register---> 設定對應的引數等---->driver_find---> bus_add_driver----> klist_add_tail
相對於裝置掛載,多了一個函式driver_find的呼叫,該函式主要目的就是判斷驅動是否已經掛載上了,其餘處理方式同裝置掛載相同。最為重要的依然是klist_add_tail,把該驅動新增到了platform_bus中的一個連結串列中。
其他型別的匯流排裝置和驅動相同,也會存在兩個連結串列,裝置和驅動均掛載到相應的連結串列中。
從第2小節中,我們知道Platform匯流排下有兩個連結串列,我採用下面的圖來具體化這兩個連結串列,圖左邊的裝置連結串列,圖中僅呈現3個裝置,實際上會有很多,圖右邊為驅動連結串列。不管是左邊的裝置還是右邊的驅動,均有name欄位(通常情況下是compatible),這是個非常重要的欄位,後面我們會用到它。
圖 2Platform匯流排的兩個連結串列
針對匹配問題,我依然採用Platform匯流排來闡述,我們已經知道在進行驅動掛載時,會呼叫函式bus_add_driver,該函式核心實際上還會呼叫一個函式driver_attach(針對設定drivers_autoprobe的情況),下面是函式driver_attach的呼叫情況:
driver_attach --->bus_for_each_dev(drv->bus, NULL, drv, __driver_attach) ---> klist_iter_init_node(&bus->p->klist_devices, &i, (start ? &start->p->knode_bus : NULL)); while ((dev = next_device(&i)) && !error) error = fn(dev, data); --->driver_match_device ---> drv->bus->match ---> platform_match
從上面程式碼過程可以看出,當掛載驅動時,會遍歷圖2中左邊的連結串列,最後呼叫Platform匯流排的match函式platform_match (match函式是在struct bus_type platform_bus_type中設定的,在匯流排部署時階段呼叫platform_bus_init就設定好了)來進行裝置和驅動的匹配。每個匯流排都會有自己的match函式,且match函式里面會透過多種方式匹配,如常見的compitable,name或者id_table,只要有一個能匹配上,則認為驅動和裝置匹配成功。
本文主要採用Platform來說明了核心中匯流排部署,裝置和驅動掛載,及裝置和驅動的匹配問題,實際上其他匯流排也是採用相同的方式,在我的描述過程中,重點在於匯流排,忽略了一些sysfs節點,引用計數,kobject,kset等,但這些在核心架構中也是比較重要的環節,希望大家在瞭解匯流排架構後,也能有時間去深入檢視核心匯流排的各個處理細節。
特別說明:不同的核心,可能使用到的函式,或者函式的實現同文章中介紹的存在出入,但其原理及架構相同,可以作為參考。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69901823/viewspace-2893297/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 一文帶你瞭解什麼是工控機?
- 微服務是什麼?帶你簡單瞭解微服務微服務
- 帶你深入瞭解什麼是商業資料分析
- DevOps是什麼?5分鐘帶你瞭解DevOpsdev
- Jazz 你瞭解是什麼技術
- 資料倉儲是什麼?它有什麼用?一文帶你全瞭解
- 你真正瞭解什麼是CloudNative嗎?Cloud
- 實在智慧RPA帶你瞭解什麼是RPA機器人機器人
- 機器學習到底是什麼?一篇文章帶你瞭解透徹機器學習
- 什麼是工藝流程圖?一篇文章帶你詳細瞭解流程圖
- 大資料是什麼?一文帶你瞭解大資料的概念!大資料
- Python是什麼?你對Python瞭解嗎?Python
- [譯] 帶你瞭解什麼是工程師和工程師的影響力工程師
- 什麼是雲連線?雲學院帶你瞭解華為雲連線知識
- 什麼是Python爬蟲?一篇文章帶你全面瞭解爬蟲Python爬蟲
- 瞭解什麼是微前端前端
- 8張圖帶你全面瞭解kafka的核心機制Kafka
- 帶你瞭解webpackWeb
- “十二問”讓你全面瞭解MES系統是什麼!
- Linux是什麼作業系統?你瞭解多少?Linux作業系統
- 什麼是計算機的控制匯流排計算機
- 你瞭解微服務架構麼?微服務架構
- 【智慧製造】簡單明瞭讓你瞭解什麼是柔性製造
- 瞭解什麼是遠端桌面
- 初步瞭解AQS是什麼(二)AQS
- 帶你瞭解營銷型網站的核心要素網站
- 江民科普漫畫第二期來啦~帶你瞭解什麼是病毒特徵庫!特徵
- 不看好小程式?那是你不瞭解什麼是小程式...
- 帶你快速瞭解HTMLHTML
- 什麼是計算機 cpu 的地址匯流排?計算機
- 什麼是JAVAEE? 2分鐘瞭解Java
- 微火資訊:共享WiFi專案是什麼,如何盈利你瞭解多少?WiFi
- 萬字帶你瞭解ChatGLM
- 什麼是 TCP 流?TCP
- ###什麼是Linux核心###什麼是MMULinux
- 華為“鴻蒙”所涉及的微核心到底是什麼?一文帶你認識微核心鴻蒙
- 資料結構與演算法(一):帶你瞭解時間複雜度和空間複雜度到底是什麼?資料結構演算法時間複雜度
- 對laravel框架你瞭解多少,為什麼說是優雅的框架Laravel框架