?? 漫談兼容內核之二:關于kernel-win32的對象管理.txt
字號:
[code][do_fork() > task_ornament_notify_fork()]
static __inline__ void task_ornament_notify_fork(struct task_struct *tsk,struct task_struct *child, unsigned long clone_flags)
{
/* only iterate through the list if there _is_ a list */
if (!list_empty(&tsk->ornaments))
__task_ornament_notify_fork(tsk,child,clone_flags);
}[/code]
實際的操作由另一個函數__task_ornament_notify_fork()完成。
[code][do_fork() > task_ornament_notify_fork() > __task_ornament_notify_fork()]
void __task_ornament_notify_fork(struct task_struct *tsk,struct task_struct *child, unsigned long clone_flags)
{
struct task_ornament buoy[2], *orn;
struct list_head *ptr;
atomic_set(&buoy[0].to_count, 0x3fffffff);
INIT_LIST_HEAD(&buoy[0].to_list);
buoy[0].to_ops = NULL;
atomic_set(&buoy[1].to_count, 0x3fffffff);
INIT_LIST_HEAD(&buoy[1].to_list);
buoy[1].to_ops = NULL;
/* loop through all task ornaments, but be careful in case the
* list is rearranged. The buoy is used as a marker between the current
* position and the next
*/
write_lock(&tsk->alloc_lock);
list_add_tail(&buoy[1].to_list,&tsk->ornaments);
ptr = tsk->ornaments.next;
for (;;) {
/* skip over buoys from other processors */
for (;;) {
if (ptr==&buoy[1].to_list)
goto end_of_list_reached;
orn = list_entry(ptr,struct task_ornament,to_list);
if (orn->to_ops)
break;
ptr = ptr->next;
}
/* we've found a real ornament */
ornget(orn);
/* stuff a buoy in the queue after it */
list_add(&buoy[0].to_list, ptr);
write_unlock(&tsk->alloc_lock);
/* call the operation */
orn->to_ops->fork(orn,tsk,child,clone_flags);
ornput(tsk,orn);
/* remove the buoy */
write_lock(&tsk->alloc_lock);
ptr = buoy[0].to_list.next;
list_del(&buoy[0].to_list);
}
end_of_list_reached:
list_del(&buoy[1].to_list);
write_unlock(&tsk->alloc_lock);
} /* end __task_ornament_notify_fork() */[/code]
前面講過,掛在ornaments隊列中的數據結構是task_ornament,但task_ornament又是WineThread數據結構中的一個成分,所以一般實際掛入隊列的是WineThread數據結構。但是,這并不排除把獨立的task_ornament結構掛入ornaments隊列。這里的兩個局部量buoy[2]就是如此。這個詞的原意是“浮標”,在這里就是作為分隔標志使用的。
首先是對兩個浮標的初始化,浮標的特點是它的to_ops指針為0。然后把浮標buoy[1]掛入父進程(線程)的ornaments隊列末尾。這樣,在浮標buoy[1]之前的所有task_ornament數據結構都是需要復制的。而若此后再有新的task_ornament數據結構掛入這個隊列,就不在應該復制之列了。接著使指針ptr指向隊列中的第一個task_ornament數據結構,下面就是對隊列中所有成員的for循環了。
這里有兩個嵌套的for循環。外層for循環是對于隊列中所有有效成員(浮標除外)的循環,而內層for循環則有兩個目的。其一是在隊列中碰到浮標buoy[1]時便跳轉到end_of_list_reached:,結束整個復制過程。其二是跳過隊列中別的浮標,即由別的線程所設置的浮標(特點是其to_ops指針為0)。
對于隊列中的每個有效成員,即每個WineThread數據結構,先在其后面再加上一個浮標buoy[0]作為分隔,接著就調用由相應task_ornament_operations數據結構提供的函數進行處理,即orn->to_ops->fork()。顯然,這就是ThreadOrnamentFork()。
[code][do_fork() > task_ornament_notify_fork() > __task_ornament_notify_fork()
> ThreadOrnamentFork()]
/*
* notification that fork/clone has set up the new process and
* is just about to dispatch it
* - no ornaments will have been copied by default
*/
static void ThreadOrnamentFork(struct task_ornament *ornament, struct task_struct *parent,struct task_struct *child, unsigned long clone_flags)
{
ktrace("%s(%p,%p,%p,%08lx)\n",
__FUNCTION__,ornament,parent,child,clone_flags);
} /* end ThreadOrnamentFork() */[/code]
出乎意外的是,這里只是調用了一下ktrace()、即printk()。函數代碼前面的注釋說“默認的操作是不復制ornaments”。我想,合理的解釋之一是開發還在進行中,還沒有來得及實現。但是,在fork()的時候是否真的應該為子進程(線程)復制父進程(線程)的ornaments隊列呢?如果是,那么Wine線程與其所落實的task_struct數據結構之間還是不是一一對應的關系呢?如果不是,那么從__task_ornament_notify_fork()開始的一系列操作豈不是無的放矢?
實際上ThreadOrnamentExecve()、ThreadOrnamentSignal()也是沒有實質性的操作。
還有個問題,就是一個Wine線程到底有幾個WineThread數據結構?如果只有一個的話,那么應該出現在誰的ornaments隊列里?我們不妨帶著這些問題到代碼中找找答案。
首先,task_ornament結構是由add_task_ornament()掛入ornaments隊列的:
[code]+/*
+ * add an ornament to a task
+ */
+void add_task_ornament(struct task_struct *tsk,struct task_ornament *orn)
+{
+ ornget(orn);
+ write_lock(&tsk->alloc_lock);
+ list_add_tail(&orn->to_list,&tsk->ornaments);
+ write_unlock(&tsk->alloc_lock);
+} /* end add_task_ornament() */[/code]
如前所述,task_ornament結構是WineThread數據結構中的一個成分,是“連接件”。所以說的是add_task_ornament(),實際上加入隊列的是WineThread數據結構(除前述的“浮標”以外)。那么是誰在調用這個函數呢?搜索的結果是唯一的,那就是ThreadConstructor():
[code]static int ThreadConstructor(Object *obj, void *data)
{
struct WineThreadConsData *wtcd = data;
struct WineProcess *process;
struct WineThread *thread;
. . . . . .
process = (struct WineProcess *) wtcd->wtcd_process->o_private;
. . . . . .
thread = (struct WineThread *) kmalloc(sizeof(struct WineThread), GFP_KERNEL);
. . . . . .
obj->o_private = thread;
. . . . . .
thread->wt_task = wtcd->wtcd_task;
. . . . . .
list_add(&thread->wt_list,&process->wp_threads);
. . . . . .
add_task_ornament(thread->wt_task,&thread->wt_ornament);
. . . . . .
return 0;
} /* end ThreadConstructor() */[/code]
這就是線程對象的構造函數。這個函數分配、構造了一個WineThread數據結構,并將它掛入兩個隊列。一個是該線程所屬Wine進程的wp_threads隊列,把屬于同一個Wine進程的線程都串在一起。另一個隊列就是某個task_struct結構的ornaments隊列。誰的task_struct結構?這是作為參數傳下來的。如果我們向上“順藤摸瓜”,就可以發現來源于InitialiseWin32(),而在那里這個task_struct結構指針的源頭是current,就是當前進程(線程)的task_struct結構。
InitialiseWin32()是kernel-win32用來實現系統調用Win32Init()的內核函數(其實Windows并沒有這么個系統調用,這是kernel-win32增添出來的),其用意是讓每個線程一開始時就調用一下這個系統調用。這在測試程序test/semaphore.c中可以看得很清楚,那里的main()一下子fork()出5個線程,都執行child(),而child()的第一個語句就是Win32Init()。由此可見,每個WineThread數據結構實際上只是掛入其對應task_struct結構的ornaments隊列。
再看另一個佐證。前面CreateSemaphoreA()的第一個調用參數是個WineThread結構指針,這是后面的處理所要求的,這一點事實上絕大多數系統調用都是一樣。顯然,這應該是當前線程的WineThread結構。然而Linux內核怎么找到這個WineThread結構呢?kernel-win32為此提供了一個函數task_ornament_find(),根據給定的task_struct結構找到其相應的WineThread結構。這個函數是在sys_win32()中調用的(在另一篇漫談中還要討論),調用時的參數是current、即當前進程(線程)的task_struct結構指針。顯然這是在當前進程(線程)的ornaments隊列中尋找:
[code]/*
* find an ornament of a particular type attached to a specific task
*/
struct task_ornament *task_ornament_find(struct task_struct *tsk,
struct task_ornament_operations *type)
{
struct task_ornament *orn;
struct list_head *ptr;
read_lock(&tsk->alloc_lock);
for (ptr=tsk->ornaments.next; ptr!=&tsk->ornaments; ptr=ptr->next) {
orn = list_entry(ptr,struct task_ornament,to_list);
if (orn->to_ops==type)
goto found;
}
read_unlock(&tsk->alloc_lock);
return NULL;
found:
ornget(orn);
read_unlock(&tsk->alloc_lock);
return orn;
} /* end task_ornament_find() */[/code]
這里的for循環搜索的確實是task_struct中的ornaments隊列,而且隊列中第一個符合條件的task_ornament數據結構、從而相應的WineThread數據結構,就是所要的結果。那么條件是什么呢?條件是結構中的task_ornament_operations指針相符。可是kernel-win32一共才定義了一種task_ornament_operations結構,那就是wineserver_ornament_ops。而且實際上也看不出再定義別的此類數據結構的必要。所以,所找到的必然就是由Win32Init()掛入該隊列的那個WineThread數據結構,而且是隊列中唯一的WineThread數據結構。
既然如此,那__task_ornament_notify_fork()又何必弄得那么復雜呢?再說,要是一個線程、例如child()、調用了Win32Init()兩次,那又會怎樣呢?既然實際上只有、也只需要一個WineThread數據結構,在task_struct結構中放上一個指針不是更好嗎?
所以,我看這不僅僅是具體的實現已經做到了哪一步的問題,實際上也反映了作者對整個方案的構思和設計是比較凌亂的。
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -