⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄

📁 兼容内核漫谈 适合想将Windows上的程序移植到其它平台上的朋友研究查看
💻
📖 第 1 页 / 共 3 页
字号:
[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 + -