xenomai內核解析—內核對象註冊表—xnregistry(重要組件)
1. 概述
上篇文章xenomai內核解析–同步互斥機制(一)–優先順序倒置講到,對於所有內核對象:
xnregistry
:保存內核對象,提供內核對象存儲和快速檢索。
xnsynch
:資源抽象,提供執行緒與資源的同步互斥管理機制。
舉個應用例子,有兩個xenoami任務,使用semaphore做互斥,任務1創建一個名為/test-sem
的semaphore,任務2打開這個semaphore,以這個過程為例,帶你了解xnregistry。
/*任務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);
.....
/*任務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等。
創建命名對象的時候,
- 先從
register_obj_solts
中分配一個xnobject; - 然後將name作為xnobject成員
key
,具體對象(cobalt_sem)的地址作為value
保存到xnobject的成員objaddr
中。 - 向xnregistry中存儲該xnobject時,會將name作為key經過hash運算,根據運算得到的hash值
s
,選擇合適的hash bucket,該bucket在xenomai中為object_index[]
,然後將xnobject插入選定的object_index[s]鏈表中。 - 當另一個執行緒open同一name的對象時,通過name可從
object_index[]
中快速得到該內核對象。
name只在創建和通過name查找時使用,一個對象通過name查找或創建後會保存一個xnhandlet,後續操作使用xnhandlet代替,提高xnobjet的訪問速度。
創建時註冊cobalt_sem流程如下所示。
register_obj_solts
用於保存型號量這個xnobject,上面的問題1.object_index
用於檢索,上面的問題2。
我們接著來看創建後保存的這個xnhandlet,cobalt_sem創建完成後會保存xnhandlet到訊號量句柄sem_t中,並拷貝到用戶空間,我們可以來看一下libcobalt中的句柄sem_t的形式:
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註冊流程如下。
4. xnregistry初始化流程
圖中resitry_obj_slots[]
其大小內核構建時CONFIG_XENO_OPT_REGISTRY_NRSLOTS
指定,默認大小512,具體記憶體在xenomai初始化時調用xnregistry_init()
初始化xnregistry時分配。
static int __init xenomai_init(void)
->sys_init()
->xnregistry_init()
xnregistry_init()
具體流程如下。
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結構如下:
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。
- 在linux虛擬文件系統proc目錄下創建
registry
目錄,以及文件usage
,註冊後可通過/proc/xenomai/registry/usage
查看xnobject的使用情況。
$ cat /proc/xenomai/registry/usage
7/512
- 將剛分配的空閑xnobject插入free_object_list鏈表。
- 將free_object_list的第一個xnobject節點保留,記錄xnobject已使用數nr_active_objects。
- 分配散列表object_index[]的空間,並初始化。
- 初始化registry資源同步對象
register_synch
,register_synch
5. xnregistry總結
xnregistry
:保存內核對象,提供內核對象存儲和快速檢索。
xnsynch
:資源抽象,提供執行緒與資源的同步互斥管理機制。
xnsynch
、xnregistry
是xenomai內核機制非常重要的組件,明白他們xenomai的資源管理機制就明白大半了。