1. 程式人生 > >xenomai核心解析---核心物件登錄檔—xnregistry(重要元件)

xenomai核心解析---核心物件登錄檔—xnregistry(重要元件)

## 1. 概述 上篇文章[xenomai核心解析--同步互斥機制(一)--優先順序倒置](https://www.cnblogs.com/wsg1100/p/13258107.html)講到,對於所有核心物件: `xnregistry`:儲存核心物件,提供核心物件儲存和快速檢索。 `xnsynch`:資源抽象,提供執行緒與資源的同步互斥管理機制。 舉個應用例子,有兩個xenoami任務,使用semaphore做互斥,任務1建立一個名為`/test-sem`的semaphore,任務2開啟這個semaphore,以這個過程為例,帶你瞭解xnregistry。 ```C /*任務1*/ sem_t *dome_sem; ..... dome_sem = sem_open("/test-sem", O_CREAT | O_EXCL, 0666, 0); if (dome_sem == SEM_FAILED) error(1, errno, "sem_open()"); ..... sem_wait(dome_sem); ..... sem_post(dome_sem); ..... ``` ```C /*任務2*/ sem_t *dome_sem; ..... dome_sem = sem_open("/test-sem", 0); if (dome_sem == SEM_FAILED) error(1, errno, "sem_open()"); ..... sem_wait(dome_sem); ..... sem_post(dome_sem); ..... ``` - 問題1:任務1建立的這個semaphore是如何管理的? - 問題2:任務2又是如何通過name找到它的? 本片文章解析xenomai核心中的xnregistry。至於xenomai semaphore具體的核心機制及建立流程,以後文章介紹,敬請關注。 ## 2. 命名核心物件管理 xnregistry用於儲存xenomai**全域性核心物件**。這些物件分為兩種,一種有name,常用於兩個及以上程序間,可以通過name來找到同一物件。另一種沒有name,常用於同一程序空間。 涉及通過字串name來查詢的核心物件稱為命名核心物件,xenomai核心中名核心物件有:有名訊號量(sem)、有名訊息佇列(mq)、程序間通過label相互通訊的xddp/bufp/iddp等。 建立命名物件的時候, 1. 先從`register_obj_solts`中分配一個xnobject; 2. 然後將name作為xnobject成員`key`,**具體物件(cobalt_sem)的地址**作為`value`儲存到xnobject的成員`objaddr`中。 3. 向xnregistry中儲存該xnobject時,會將name作為key經過hash運算,根據運算得到的hash值`s`,選擇合適的hash bucket,該bucket在xenomai中為`object_index[]`,然後將xnobject插入選定的object_index[s]連結串列中。 4. 當另一個執行緒open同一name的物件時,通過name可從`object_index[]`中快速得到該核心物件。 name只在建立和通過name查詢時使用,一個物件通過name查詢或建立後會儲存一個xnhandlet,後續操作使用xnhandlet代替,提高xnobjet的訪問速度。 建立時註冊cobalt_sem流程如下所示。 ![xnregister-semopen](https://wsg-blogs-pic.oss-cn-beijing.aliyuncs.com/xenomai/xnregister-semopen.png) - `register_obj_solts`用於儲存型號量這個xnobject,上面的問題1. - `object_index`用於檢索,上面的問題2。 我們接著來看建立後儲存的這個xnhandlet,cobalt_sem建立完成後會儲存xnhandlet到訊號量控制代碼sem_t中,並拷貝到使用者空間,我們可以來看一下libcobalt中的控制代碼sem_t的形式: ```C struct cobalt_sem_shadow { __u32 magic; __s32 state_offset; xnhandle_t handle; } shadow_sem; ``` 從上面圖中我們可以看到xnhandlet是一個偏移量,表示這個xnobject基於register_obj_solts的地址偏移,為什麼要直接儲存到控制代碼sem_t中呢?`sem_wait()/sem_post()`操作進入核心的時候就可以直接去獲取xnobject做相應的操作了。 另外想一下,一個執行在使用者態的實時應用,每次PV操作的時候都需要執行系統呼叫,對實時系統來說不太友好,畢竟系統呼叫也是需要花費時間的,xnhandlet只能解決核心裡定位xnobject的速度問題,我們能不能不要每次都執行系統呼叫呢?答案是肯定的,xenomai有相應的機制,請關注後續文章,呵呵~~。 ## 3. 未命名核心物件管理 上面說完了命名核心物件,下面來看未命名核心物件,即非跨程序共享的。 對於沒有name的核心物件,通過xnregistry提供的匿名介面來儲存。所謂的匿名儲存,key為NULL,**具體物件(cobalt_sem)的地址**作為value到一個分配的結構體xnobject後,不經hash運算,**直接計算xnobject基於某個固定地址的偏移量xnhandle_t,通常xnhandle_t會在使用者空間的物件結構體中儲存一份**,比如sem_t、pthread_mutex_t等;之後使用者空間對該物件發起系統呼叫時就可以通過xnhandle_t快速從xnregistry獲取該物件,使用匿名的核心物件有:程序間的互斥量mutex、未命名訊號量sem、條件變數condition variable、事件event、monitor。 同樣以未命名訊號量為例,核心物件cobalt_sem註冊流程如下。 ![xnregister-seminit](https://wsg-blogs-pic.oss-cn-beijing.aliyuncs.com/xenomai/xnregister-seminit.png) ## 4. xnregistry初始化流程 圖中`resitry_obj_slots[]`其大小核心構建時`CONFIG_XENO_OPT_REGISTRY_NRSLOTS`指定,預設大小512,具體記憶體在xenomai初始化時呼叫`xnregistry_init()`初始化xnregistry時分配。 ```C static int __init xenomai_init(void) ->sys_init() ->xnregistry_init() ``` `xnregistry_init()`具體流程如下。 ```C int xnregistry_init(void) { int n, ret __maybe_unused; registry_obj_slots = kmalloc(CONFIG_XENO_OPT_REGISTRY_NRSLOTS * sizeof(struct xnobject), GFP_KERNEL); ..... #ifdef CONFIG_XENO_OPT_VFILE ret = xnvfile_init_dir("registry", ®istry_vfroot, &cobalt_vfroot); ret = xnvfile_init_regular("usage", &usage_vfile, ®istry_vfroot); proc_apc = xnapc_alloc("registry_export", ®istry_proc_schedule, NULL); #endif /* CONFIG_XENO_OPT_VFILE */ next_object_stamp = 0; for (n = 0; n < CONFIG_XENO_OPT_REGISTRY_NRSLOTS; n++) { registry_obj_slots[n].objaddr = NULL; list_add_tail(®istry_obj_slots[n].link, &free_object_list); } /* Slot #0 is reserved/invalid. */ list_get_entry(&free_object_list, struct xnobject, link); nr_active_objects = 1; nr_object_entries = xnregistry_hash_size(); object_index = kmalloc(sizeof(*object_index) * nr_object_entries, GFP_KERNEL); for (n = 0; n < nr_object_entries; n++) INIT_HLIST_HEAD(&object_index[n]); xnsynch_init(®ister_synch, XNSYNCH_FIFO, NULL); return 0; } ``` 1.先分配`CONFIG_XENO_OPT_REGISTRY_NRSLOTS`個xnobject的空間,xenomai執行過程中的xnobject從registry_obj_slots中直接獲取,這樣就避免頻繁的記憶體申請影響實時性。struct xnobject結構如下: ```C struct xnobject { void *objaddr; const char *key; /* !< Hash key. May be NULL if anonynous. */ unsigned long cstamp; /* !< Creation stamp. */ #ifdef CONFIG_XENO_OPT_VFILE struct xnpnode *pnode; /* !< v-file information class. */ union { struct { struct xnvfile_rev_tag tag; struct xnvfile_snapshot file; } vfsnap; /* !< virtual snapshot file. */ struct xnvfile_regular vfreg; /* !< virtual regular file */ struct xnvfile_link link; /* !< virtual link. */ } vfile_u; struct xnvfile *vfilp; #endif /* CONFIG_XENO_OPT_VFILE */ struct hlist_node hlink; /* !< Link in h-table */ struct list_head link; }; ``` `objaddr`指向具體的核心物件,如`cobalt_sem`、`cobalt_mutex`等。 `*key` 物件的name或label,使用者程式可使用name來操作核心物件,具有name的核心物件會儲存到一個hash表中,方便通過name查詢。如果key為NULL,則不用。 `vfilp`、`vfile_u`、`pnode` 註冊到linux虛擬檔案系統常用變數。 `hlink`用於加入hash連結串列。 `link` 該物件如果未使用則用於加入空閒連結串列free_object_list,否則用於加入已使用連結串列busy_object_list。 2. 在linux虛擬檔案系統proc目錄下建立`registry`目錄,以及檔案`usage`,註冊後可通過`/proc/xenomai/registry/usage`檢視xnobject的使用情況。 ```sh $ cat /proc/xenomai/registry/usage 7/512 ``` 3. 將剛分配的空閒xnobject插入free_object_list連結串列。 4. 將free_object_list的第一個xnobject節點保留,記錄xnobject已使用數nr_active_objects。 5. 分配散列表object_index[]的空間,並初始化。 6. 初始化registry資源同步物件`register_synch`,`register_synch` ## 5. xnregistry總結 `xnregistry`:儲存核心物件,提供核心物件儲存和快速檢索。 `xnsynch`:資源抽象,提供執行緒與資源的同步互斥管理機制。 `xnsynch`、`xnregistry`是xenomai核心機制非常重要的元件,明白他們xenomai的資源管理機制就明白大