📄
字号:
[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 + -