📄
字号:
int err;
*hObject = NULL;
/* retrieve the name */
err = fetch_oname(&oname,name);
if (err<0)
return ERR_PTR(err);
/* allocate an object */
obj = _AllocObject(clss,&oname,data);
if (oname.name) putname(oname.name);
if (IS_ERR(obj))
return obj;
/* find a handle slot */
process = GetWineProcess(thread);
epobj = &process->wp_handles[MAXHANDLES];
write_lock(&process->wp_lock);
for (ppobj=process->wp_handles; ppobj<epobj; ppobj++)
if (!*ppobj) goto found_handle;
write_unlock(&process->wp_lock);
objput(obj);
return ERR_PTR(-EMFILE);
found_handle:
/* make link to object */
objget(obj);
*ppobj = obj;
write_unlock(&process->wp_lock);
ppobj++; /* don't use the NULL handle */
*hObject = (HANDLE) ((char*)ppobj - (char*)process->wp_handles);
return obj;
} /* end CreateObject() */[/code]
这个函数的操作可以分成两大部分。第一部分是对_AllocObject()的调用,旨在创建具体的对象。第二部分是将指向所创建对象的指针“安装”在当前进程的“打开对象表”中,并将相应的下标转换成Handle。为便于阅读讨论,我们先假定第一部分的操作业已完成,_AllocObject()已经返回所创建的Object结构的指针,先看看对于“打开对象表”的操作,这是从注释行“/* find a handle slot */”开始的。至于putname()、objget ()、objput()一类的函数,那只是递增或递减数据结构中的引用计数(减到0就要释放其占用的存储空间),并不影响对于实质性操作的讨论。
“打开对象表”在WineProcess数据结构中,而从上面传下来的只是个WineThread指针。所以这里要通过GetWineProcess()找到当前线程所属的Wine进程,这其实只是从WineThread数据结构中获取其wt_process指针而已。
找到了所属进程的WineProcess数据结构以后,就通过一个for循环扫描其“打开对象表”,旨在找到一个空闲的位置,指针为0就表示空闲。这也说明了为什么0不能被用作handle的值。找到以后,就把新创建对象的obj指针填写到这个位置上。而handle数值的计算,则可以看出基本上是该指针在数组中的(字节)位移量加4,实际上就是下标加1后再乘4。注意handle的值是通过调用参数hObject返回的。
再回到第一部分,即对象的创建,这是由_AllocObject()完成的。
[code][CreateSemaphoreA() > CreateObject() > _AllocObject()]
static Object *_AllocObject(struct ObjectClass *clss, struct oname *name, void *data)
{
Object *obj;
. . . . . .
/* create and initialise an object */
obj = (Object *) kmalloc(sizeof(Object),GFP_KERNEL);
. . . . . .
atomic_set(&obj->o_count,1);
init_waitqueue_head(&obj->o_wait);
. . . . . .
/* name anonymous objects as "class:objaddr" if so requested */
if (!name->name && ~clss->oc_flags&OCF_DONT_NAME_ANON) {
. . . . . .
}
/* cut'n'paste the name from the caller's name buffer */
else {
obj->o_name.name = name->name;
obj->o_name.nhash = name->nhash;
name->name = NULL;
}
/* attach to appropriate object class list */
obj->o_class = clss;
if (obj->o_name.name)
list_add(&obj->o_objlist,
&clss->oc_nobjs[obj->o_name.nhash&OBJCLASSNOBJSMASK]);
else
list_add(&obj->o_objlist,&clss->oc_aobjs);
. . . . . .
err = clss->constructor(obj,data); /* call the object constructor */
if (err==0) goto cleanup_1;
. . . . . .
cleanup_1:
write_unlock(&clss->oc_lock);
cleanup_0:
return obj;
} /* end _AllocObject() */[/code]
首先是由kmalloc()为所创建的对象分配存储空间。然后是Object结构的初始化,包括把对象名(及其hash值)拷贝到Object结构中的o_name里面。
接着,如果有对象名,就根据其hash值把所创建的Object结构挂入所属对象类别的相应hash队列中,否则就挂入该类别的无名对象队列中。
下面就是实质性的操作了,这是通过所属类别提供的constructor函数完成的。对于“信号量”而言,该类对象的类型数据结构是semaphore_objclass。
[code]struct ObjectClass semaphore_objclass = {
oc_type: "SEMA ",
constructor: SemaphoreConstructor,
reconstructor: SemaphoreReconstructor,
destructor: SemaphoreDestructor,
poll: SemaphorePoll,
describe: SemaphoreDescribe
};[/code]
显然,其constructor函数是SemaphoreConstructor(),所以实际调用的就是这个函数。
[code][CreateSemaphoreA() > CreateObject() > _AllocObject() > SemaphoreConstructor()]
static int SemaphoreConstructor(Object *obj, void *data)
{
struct WiocCreateSemaphoreA *args = data;
struct WineSemaphore *semaphore;
. . . . . .
semaphore =
(struct WineSemaphore *) kmalloc(sizeof(struct WineSemaphore), GFP_KERNEL);
. . . . . .
obj->o_private = semaphore;
semaphore->ws_count = args->lInitialCount;
semaphore->ws_max = args->lMaximumCount;
return 0;
} /* end SemaphoreConstructor() */[/code]
程序很简单,先分配一个WineSemaphore数据结构所需的空间,这个数据结构才是真正意义上的具体的“对象”、一个信号量。当然,还要使Object结构中的指针o_private指向这个WineSemaphore数据结构。而对于这个信号量的初始化,则只不过是把作为参数传下来的lInitialCount和lMaximumCount填写进去。
显然,kernel-win32另行实现了一个信号量机制,而不是把Windows应用程序的信号量操作“嫁接”到Linux已有的信号量机制上。对于相对而言比较简单的信号量机制,这样当然也是可以的(也还值得推敲)。而对于比较复杂的机制、特别是文件操作,那就只能走嫁接这“华山一条路”了,以后我们还要通过文件操作看kernel-win32是如何实现这种嫁接的。
此外,前面曾经提到,task_ornament数据结构中有个指针to_ops,指向某个task_ornament_operations数据结构。这个数据结构的主体是一组函数指针,说明内核在close、exit、signal、execve、fork等操作时应该对上述种种附加的数据结构和对象做些什么。目前kernel-win32只定义了一种task_ornament_operations数据结构:
[code]static const struct task_ornament_operations wineserver_ornament_ops = {
name: "wineserver",
owner: THIS_MODULE,
close: ThreadOrnamentClose,
exit: ThreadOrnamentExit,
signal: ThreadOrnamentSignal,
execve: ThreadOrnamentExecve,
fork: ThreadOrnamentFork
};[/code]
那么怎样使用这个数据结构呢?与这些函数指针基本对应,kernel-win32的代码中还有下列几个函数:
[code] task_ornament_notify_exit()、
task_ornament_notify_signal()、
task_ornament_notify_execve()、
task_ornament_notify_fork()、[/code]
这些函数会根据相应的函数指针调用有关的程序。对这几个函数的调用则出现在kernel-win32对Linux内核所打的补丁中。以对于fork.c所打的补丁为例:
[code]do_fork(. . . . . .)
{
. . . . . .
p->p_cptr = NULL;
init_waitqueue_head(&p->wait_chldexit);
p->vfork_sem = NULL;
- spin_lock_init(&p->alloc_lock);
+ rwlock_init(&p->alloc_lock);
. . . . . .
current->counter >>= 1;
if (!current->counter)
current->need_resched = 1;
+ /*
+ * tell any ornaments to duplicate themselves
+ */
+ INIT_LIST_HEAD(&p->ornaments);
+ task_ornament_notify_fork(current,p,clone_flags);
. . . . . .
}[/code]
这里作的第一个修改是把spin_lock_init()换成rwlock_init()。下面实质性的修改则是将子进程(线程)的ornaments队列头加以初始化,然后调用task_ornament_notify_fork()。调用的目的,按注释所述,是复制当前进程(线程)的ornaments队列中的各个附件。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -