📄
字号:
漫谈兼容内核之三:Kernel-win32的文件操作
[align=center][b][size=4]漫谈兼容内核之三:Kernel-win32的文件操作[/size][/b][/align]
[align=center][i]毛德操[/i][/align]
上一篇漫谈中分析/介绍了Kernel-win32的对象管理机制。在各种对象类型中,文件显然是最重要、最复杂(就其操作而言)的类型。如果说像“信号量”之类的简单对象还有可能独立地加以实现,那么像文件这样的复杂对象这就不太现实,并且实际上也不合适了。所以,本文的目的就是通过几个典型文件操作的实现介绍kernel-win32是怎样把Windows用于文件操作的系统调用“嫁接”到Linux内核中的有关成分上。
文件的创建
如前文所述,所有的对象都有个Object数据结构,里面有个ObjectClass指针,它指向什么ObjectClass数据结构,这个对象就是什么类型。“文件”就是一种对象类型,它的数据结构是file_objclass:
[code]struct ObjectClass file_objclass = {
oc_type: "FILE ",
constructor: FileConstructor,
reconstructor: FileReconstructor,
destructor: FileDestructor,
poll: FilePoll,
describe: FileDescribe,
oc_flags: OCF_DONT_NAME_ANON /* names shared from FCTRL class */
};[/code]
需要创建一个文件时、或者说需要创建一个类型为文件的对象时,函数_AllocObject()将调用这个结构所提供的函数FileConstructor(),以构造出代表着这个具体对象的数据结构,并且创建并/或打开具体的文件。
另一方面,Object数据结构中还有个指针o_private,指向由上述构造函数生成、代表着具体类别对象的数据结构。对于文件类对象,这个数据结构是WineFile:
[code]/*
* process-access file object definition
* - can't be shared, since they have to have separate attributes for the file
* access
*/
struct WineFile {
struct list_head wf_ctllist; /* file control list next */
WineFileControl *wf_control; /* the file control object */
struct file *wf_file; /* the Linux file */
__u32 wf_access; /* file access mode */
__u32 wf_sharing; /* sharing mode */
__u32 wf_attrs; /* file open attributes */
};[/code]
这个数据结构代表着目标文件的一次“打开”。也即一个文件访问上下文。至于没有被打开的文件,则只是存在于磁盘上、或本来就是个外设,在内存中一般不存在相应的数据结构。
注意这里的指针wf_file,这是一个struct file指针。我们知道,struct file就是Linux内核中代表着已打开文件的数据结构,存在于每个进程的“打开文件表”中的就是struct file指针。由于struct file数据结构所代表的是目标文件的一次打开,所以可以断定WineFile所代表的也是一次打开、即一个上下文(例如可以通过lseek()取得的当前读/写位置),而不是目标文件本身。
显然,对于已经打开的文件,除了代表着文件访问上下文的数据结构以外,还应该有代表着目标文件本身的数据结构。这就是“文件控制类”的对象,其数据结构是WineFileControl:
[code]struct ObjectClass file_control_objclass = {
oc_type: "FCTRL",
constructor: FileControlConstructor,
reconstructor: FileControlReconstructor,
destructor: FileControlDestructor,
poll: FileControlPoll,
describe: FileControlDescribe
};[/code]
文件控制类对象的o_private数据结构则是WineFileControl:
[code]/*
* file-control object definition
* - links together WineFile objects that use the same file
*/
struct WineFileControl {
struct list_head wfc_accessors; /* who's accessing this file */
spinlock_t wfc_lock; /* govern access to list */
Object *wfc_myself; /* my own object */
};[/code]
这里的wfc_myself指向其本身的Object数据结构。队列头wfc_accessors用来维持一个“访问者”队列,这是一个WineFile数据结构的队列。WineFile数据结构通过其队列头wf_ctllist挂入这个队列。WineFileControl与WineFile之间是一对多的关系。只要一个文件被打开,内存中就得有一个(并且只有一个)代表着这个文件的WineFileControl数据结构,而WineFile数据结构的数量则取决于对此文件同时共存的上下文个数。在某种意义上,WineFile是对(Linux的)struct file数据结构的包装和扩充,而WineFileControl是对struct dentry数据结构的的补充。但是,应该在WineFileControl中加以补充的Windows文件所独有(有别于Linux文件属性)的属性有很多,而这里却什么也没有。所以目前这个数据结构的定义只是提供了一个空的骨架,还有待充实。
还有值得一提的是,打开目标文件以后的struct file指针wf_file保存在WineFile数据结构中,而不是保存在Linux进程(线程)的打开文件表中。这意味着设计者的意图是另起炉灶、完全由自己来管理Windows进程所打开的文件,而与Linux的有关机制分道扬镳。可是这是有可能带来问题的。举个例子来说明这一点:假定一个Windows进程在运行中发生了问题,或者被别的进程kill,从而执行了do_exit()。在退出运行的过程中,do_exit()会扫描该进程的打开文件表,根据表中的struct file指针关闭所有已打开文件、并释放资源。可是现在Windows进程的struct file指针不在打开文件表中,因而就享受不到这个待遇。设计者显然意识到这一点,所以在有关的内核代码上打了补丁,让它调用一个外挂函数(见前一篇漫谈)。然而,我们却看到有关的外挂函数尚未实现。对于诸如此类的问题,是尽量利用Linux内核原有的代码较好?还是另起炉灶较好?应该说是各有利弊,具体的情况需要具体分析,总之是值得推敲的。
介绍了这些背景以后,我们可以阅读kernel-win32所实现的代码了。这里看三个系统调用的代码,分别是CreateFileA()、ReadFile()、和CloseHandle()。了解了这三种操作,对于kernel-win32文件操作的实现就知道了个大概,再进一步阅读其它有关的代码也就不难了。这三个系统调用的函数名与一般Windows文献中所见有所不同,但是这没有什么关系,因为这些函数是内核中不向外界开放(导出)的函数,叫什么都可以。
先看CreateFileA(),这个函数根据给定的路径名打开一个文件,如果这个文件尚不存在就加以创建、并且打开。这跟Linux的sys_open()基本上是一致的,只是函数名sys_open()突出了“打开”而CreateFileA()突出了“创建”。不过,除CreateFile()外,Windows另有一个系统调用OpenFile(),这很容易让人误以为前者是只创建不打开,而后者是只打开不创建。
还有,函数名CreateFileA()中的“A”表示ASCII,意思是所涉及的字符串(例如文件名)是8位ASCII码。与之相对的是“W”,表示16位“宽”字符,或Unicode。
下面就来看代码。
[code]/*
* open a file object, maybe creating if non-existent
*/
int CreateFileA(struct WineThread *thread, struct WiocCreateFileA *args)
{
HANDLE hFile;
Object *obj;
/* must have a name... */
if (!args->lpFilename)
return -EINVAL;
/* ...but create the object _without_ a name and attach one later
* (need a separate file-access object for each CreateFileA
*/
obj = CreateObject(thread, &file_objclass, NULL, args, &hFile);
if (IS_ERR(obj))
return PTR_ERR(obj);
ktrace("*** [%d] CreateFileA(%p) = %p\n",current->pid,obj,hFile);
objput(obj);
return (int) hFile;
} /* end CreateFileA() */[/code]
Kernel-win32所实现的系统调用通过数据结构传递参数,这是与Windows不同、而带有Linux风格的做法;原因就是Kernel-win32通过Linux系统调用来“搭载”实现Windows系统调用。对于CreateFileA(),这个数据结构是struct WiocCreateFileA。
[code]struct WiocCreateFileA {
LPCSTR __pad__ lpFilename;
DWORD __pad__ dwDesiredAccess;
DWORD __pad__ dwShareMode;
LPSECURITY_ATTRIBUTES __pad__ lpSecurityAttributes;
DWORD __pad__ dwCreationDisposition;
DWORD __pad__ dwFlagsAndAttributes;
HANDLE __pad__ hTemplateFile;
};[/code]
可见与open()的参数有相当大的不同,这里限于篇幅不作介绍了,读者可参阅有关的Windows文献和资料。
程序的主体就是CreateObject()。如我在上一篇漫谈中所示,这个函数先调用_AllocObject()创建具体的Object数据结构(并完成实际目标的创建和/或打开操作),然后把指向该数据结构的指针安装在所属进程的“打开对象表”中,并返回实质上是数组下标的Handle。而_AllocObject()则进一步细化,通过由file_objclass数据结构所提供的函数指针调用FileConstructor(),这才是实质性的操作。
[code][CreateFileA() > CreateObject() > _AllocObject() > FileConstructor()]
/*
* construct a file access object (allocate its private data)
* - called by CreateObject if the file does not already exists
* - called with the object class lock held
*/
static int FileConstructor(Object *obj, void *data)
{
struct WiocCreateFileA *args = data;
struct WineFile *wf;
struct Object *fco;
int err, flags, mode;
ktrace("FileConstructor(%p)\n",obj);
/* construct the file information record */
wf = (struct WineFile *) kmalloc(sizeof(struct WineFile),GFP_KERNEL);
if (!wf)
return -ENOMEM;
obj->o_private = wf;
wf->wf_access = args->dwDesiredAccess;
wf->wf_sharing = args->dwShareMode;
wf->wf_attrs = args->dwFlagsAndAttributes;
/* gain access to the file control object */
fco = AllocObject(&file_control_objclass, args->lpFilename, data);
if (IS_ERR(fco)) {
err = PTR_ERR(fco);
goto cleanup_priv;
}
wf->wf_control = fco->o_private;
spin_lock(&wf->wf_control->wfc_lock);
/* make use of the name stored in the file control object */
obj->o_name.name = fco->o_name.name;
/* check that we can share access */
err = FileCheckSharing(args, wf->wf_control);
if (err<0)
goto cleanup_fco;
/* determine Linux file open parameters */
switch (args->dwCreationDisposition) {
case CREATE_NEW: flags = O_CREAT | O_EXCL; break;
case CREATE_ALWAYS: flags = O_CREAT | O_TRUNC; break;
case OPEN_ALWAYS: flags = O_CREAT; break;
case TRUNCATE_EXISTING: flags = O_TRUNC; break;
case OPEN_EXISTING: flags = 0; break;
default:
err = -EINVAL;
goto cleanup_fco;
}
switch (args->dwDesiredAccess & (GENERIC_READ|GENERIC_WRITE)) {
case 0: break;
case GENERIC_READ: flags |= O_RDONLY; break;
case GENERIC_WRITE: flags |= O_WRONLY; break;
case GENERIC_READ|GENERIC_WRITE: flags |= O_RDWR; break;
}
mode = (args->dwFlagsAndAttributes & FILE_ATTRIBUTE_READONLY)? 0444 : 0666;
/* open the Linux file */
wf->wf_file = filp_open(obj->o_name.name, flags, mode);
if (IS_ERR(wf->wf_file)) {
err = PTR_ERR(wf->wf_file);
goto cleanup_fco;
}
/* don't permit a directory to be opened */
if (S_ISDIR(wf->wf_file->f_dentry->d_inode->i_mode)) {
err = -EACCES;
goto cleanup_file;
}
list_add(&wf->wf_ctllist, &wf->wf_control->wfc_accessors);
spin_unlock(&wf->wf_control->wfc_lock);
kdebug("FileConstructor: f_count=%d i_count=%d i_wc=%d\n",
atomic_read(&wf->wf_file->f_count),
atomic_read(&wf->wf_file->f_dentry->d_inode->i_count),
atomic_read(&wf->wf_file->f_dentry->d_inode->i_writecount));
return 0;
/* clean up on error */
cleanup_file:
fput(wf->wf_file);
cleanup_fco:
obj->o_name.name = NULL;
spin_unlock(&wf->wf_control->wfc_lock);
objput(wf->wf_control->wfc_myself);
cleanup_priv:
poison(wf,sizeof(struct WineFile));
kfree(wf);
return err;
} /* end FileConstructor() */[/code]
简而言之,这个函数的操作主要有这么一些:
1. 通过kmalloc()分配一块空间用于WineFile数据结构。
2. 将调用参数纪录在WineFile数据结构中。
3. 通过AllocObject()找到或创建代表着目标文件的“文件控制”对象。
4. 通过FileCheckSharing()检查是否允许调用参数所要求的访问模式。
5. 将调用参数所要求的操作模式(CREATE_NEW、CREATE_ALWAYS、OPEN_ALWAYS等等)和访问模式(GENERIC_READ、GENERIC_WRITE等)换算成Linux的相应标志位。
6. 调用Linux内核函数filp_open(),以打开目标文件,并将已打开文件的struct file结构指针填写在WineFile数据结构中。
7. 通过list_add()将前面创建的WineFile数据结构挂入相应WineFileControl数据结构的wfc_accessors队列中。
上面AllocObject()的主体就是对_AllocObject()的调用。而_AllocObject(),则先试图根据类型(在这里是file_control_objclass)和对象名找到目标对象,找不到才加以创建。所以“文件控制”对象与“打开文件”对象之间是一对多的关系。如果目标文件尚未被打开,因此需要创建“文件控制”对象的话,就会调用他的构建函数FileControlConstructor():
[code][CreateFileA() > CreateObject() > AllocObject() > _AllocObject() > FileControlConstructor()]
/*
* construct a file control object (allocate its private data)
* - called by AllocObject if the file does not already exists
* - called with the object class lock held
*/
static int FileControlConstructor(Object *obj, void *data)
{
struct WineFileControl *wfc;
ktrace("FileControlConstructor(%p)\n",obj);
/* construct the file information record */
wfc = (struct WineFileControl *) kmalloc(sizeof(struct WineFileControl),GFP_KERNEL);
if (!wfc)
return -ENOMEM;
obj->o_private = wfc;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -