首页 理论教育 文件系统和对象句柄管理

文件系统和对象句柄管理

时间:2023-11-17 理论教育 版权反馈
【摘要】:当进程不再使用某一对象句柄时,应当通过Close-Handle函数来向操作系统声明结束对该对象句柄的访问。相对而言,函数DeleteFile的映射关系就简单许多,DeleteFile函数用来删除给定的单个文件,删除文件实际上就是删除系统中对应的文件名。

文件系统和对象句柄管理

4.1.1.1 文件操作

(1)创建/打开文件

CreateFile函数的功能非常强大,可以用来创建或打开一个给定访问权限和属性的文件、目录、物理磁盘、控制台缓冲区、管道等,甚至用来打开Windows下的大部分底层设备。

在文件操作中,要对文件进行读写等操作,首先必须获得文件句柄,通过CreateFile函数就可以获得文件句柄。CreateFile函数可以映射到Linux中的open 函数,其中的dwDesiredAccess选项在Linux的open 中也有简单的对应,文件访问权限亦可在open 中设置。

这里涉及了Windows中一个非常重要的机制——句柄,它可以用来操作文件、对象等多种结构。句柄分为对象句柄和特殊句柄,其中对象句柄和Linux的文件描述符等同;而特殊句柄一般是地址或者伪句柄或者一些中间结果的操作接口,可以用一个FILE结构替代。

在Linux中,文件描述符的继承是通过fcntl函数来实现的。在Windows中,对象句柄可以被子进程继承,这一属性一般是在句柄创建时设置一次,然后在创建子进程时再临时确定需不需要继承先前设置过的句柄。在Linux中,可以在第一次指定时就将所有可继承的句柄列入一个表中,这样在创建子进程时就可以根据需要来设置表中的句柄。当进程不再使用某一对象句柄时,应当通过Close-Handle函数来向操作系统声明结束对该对象句柄的访问。

在Linux中,使用close函数便可以结束指定的文件描述符。

(2)读写文件

文件创建或打开后,就可以对文件进行读和写操作,读操作是将文件内容读入缓冲并指定读入的字节数,通过返回的BOOL值来告知用户是否成功读入了指定字节数的内容;写操作是将缓冲中的数据写入文件,同样也指定了写入的字节数,通过返回的BOOL值来告知用户是否成功写入了指定字节数的内容。

在Linux中,read 函数和write函数可以实现同样的功能。

(3)获取文件信息

函数GetFileSize用来获取文件大小,若函数执行成功,则返回文件大小的低双字;若lpFileSizeHigh 参数不是NULL,则函数执行后,将文件大小的高双字放入它指向的DWORD变量中。GetFileAttributes函数用来查看指定文件或者目录的属性,SetFileAttributes函数则是用来为指定文件设置dwFileAttributes属性。

这里,函数GetFileSize通过对象句柄来获取文件大小,在Linux中,函数fstat通过文件描述符来获取文件的大小,两者可以相互映射。

GetFileAttributes函数只考虑了文件的FILE_ATTRIBUTE_READONLY属性和FILE_ATTRIBUTE_NORMAL属性,在Linux中,stat函数可以返回详细的文件操作权限,若权限可写,就认为是满足FILE_ATTRIBUTE_NORMAL属性,否则,就认为是满足FILE_ATTRIBUTE_READONLY属性。

同样,SetFileAttributes函数也只考虑了文件的FILE_ATTRIBUTE_READONLY属性和FILE_ATTRIBUTE_NORMAL属性,对应Linux中,chmod 函数则将文件操作权限细化为十余种,由于S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH等模式都不能对文件执行写操作,因此认为它们可以等同FILE_ATTRIBUTE_READONLY属性,而其余S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH等模式则可以与FILE_ATTRIBUTE_NORMAL属性建立对应关系。

(4)文件拷贝、删除和移动

函数CopyFile可以将给定路径的文件以新名字复制到新路径中,如果参数bFailIfExists非零,则当文件存在时,函数执行失败,否则不会导致函数执行失败。

在Linux中,文件分为正规(Regular)和不正规(Irregular)两类。正规文件才是通常意义上的可复制文件。因此首先需要用stat函数来判断文件是否是正规文件。同时,如果新路径和老路径所指的文件(亦即inode节点)相同时,是不能进行复制操作的。当进行文件复制操作时,需要先通过read 函数将源文件中内容全部读出到缓存中,然后再用write函数将内容写入到新路径指定的文件中,最后调用chmod 函数将拷贝后生成的文件的权限设置为与源文件一样。由此可见,Windows中的CopyFile函数所完成的工作,在Linux中,需要由一组函数(stat→read→write→chmod)按一定顺序来完成。

相对而言,函数DeleteFile的映射关系就简单许多,DeleteFile函数用来删除给定的单个文件,删除文件实际上就是删除系统中对应的文件名。在Linux中,unlink 可以实现同等的操作功能。

函数MoveFile用于将指定路径的文件移动到新路径,或者将位于指定路径的文件夹中的文件移动到新路径。在Linux中,可以首先用stat函数来判断指定路径是否是文件夹,若不是文件夹,则调用link 函数将原文件名链接到新文件名上,最后通过unlink 删除原文件名;若是文件夹,则需要递归遍历整个目录子树,然后再依次调用stat、link、unlink 来移动文件。

(5)文件查找

FindFirstFile函数用于到一个文件夹(包括子文件夹)中去搜索指定文件,函数中的类型为LPWIN32_FIND_DATA的参数lpFindFileData指向一个用于保存文件信息的结构体,函数调用成功后,其返回值可以用作FindNextFile函数的参数,以继续查找下一个文件。

在Linux,要实现FindFirstFile函数的功能,可以先使用函数readdir和opendir找到第一个文件,再使用stat函数获取文件权限、最近访问时间、文件大小信息,并存储在一个FILE结构体中,返回指向FILE结构体的句柄,该句柄中包含有用于下次寻找的记录信息。若要继续查找下一个文件,则读取句柄中记录的信息,再调用readdir函数即可。

(6)创建文件夹

函数CreateDirectory用于在指定位置创建一个新文件夹,在Linux中,对应于函数mkdir。(www.xing528.com)

(7)获取/设置工作路径

由于常常都需要读取当前目录下的配置参数文件,或者运行当前目录下的某个程序,因此就需要获取当前进程的目录位置,这就需要使用函数GetCurrentDirectory,该函数可以用来获取当前进程所在的目录。同时也可以使用SetCurrent-Directory函数来改变进程的当前目录。

在Linux中,使用getcwd 函数可以取得当前进程的工作路径,使用函数chdir可以设置调用进程的工作路径。

(8)时间转换

Windows中,函数FileTimeToLocalFileTime用于将文件时间转换成本地时间,函数FileTimeToSystemTime则用于将文件时间转换成系统时间。

在Linux中,要将文件时间转换成本地时间,可以先通过函数localtime来获得时区信息,然后对文件时间做偏移。而要将文件时间转换成系统时间,需要先将文件时间转成Linux中的绝对时间,然后再使用函数gmtime从绝对时间中提取年月日等信息。

在进行时间的转换中,由于Windows的绝对时间是从1601.1.1 12:00:00(UTC)起,以10-7 s为单位计的,而Linux的系统时间是从1970.1.1 00:00:00(UTC)起,以1 s为单位计的[115][116]。因此存在如下的转换关系:

WindowsTime=LinuxTime·10000000 +116444736000000000

4.1.1.2 系统操作

(1)驱动器查找

Windows中,用FindFirstVolume函数来查询计算机的一个驱动器的名字,调用后,会开始扫描一台计算机上的所有驱动器。此函数打开一个驱动器的句柄,并返回在此计算机上找到的第一个驱动器的信息。函数执行后返回的句柄可以作为函数FindNextVolume和FindVolumeClose的参数,根据返回的句柄FindNextVolume函数可以继续查找下一个驱动器。也可以在不再需要的时候,通过Find-VolumeClose函数来关闭该句柄。

UUID(Universally Unique Identifier)和GUID(Global Unique Identifier)分别是Linux和Windows中对设备的唯一标识的称呼[115][116]。在Windows中,只有一种格式,XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX,而在Linux中除了这种格式外,还有多种格式,在必要的时候,需要做格式转换。

在Linux中,所有的UUID都存储在目录/dev/disk/by-uuid 下,要获得第一个驱动器的信息,可以通过函数readdir和opendir找出位于该目录下的第一个文件。句柄指向一个FILE结构,对应文件中已经将上述目录中所有UUID文件名输出,最后只需要转换一下字符串格式即可,具体的转换过程,就不在此赘述了。

这样,当要继续查找下一个驱动器时,则只需要读取句柄指向的FILE结构所对应文件中的记录信息即可。

最后,当不再需要使用返回的句柄时,调用fclose函数即可关闭FindFirstFile返回的句柄。

(2)驱动器挂载点查找

FindFirstVolumeMountPoint函数是根据驱动器的GUID来查询该驱动器的第一个挂载点,函数执行后返回的句柄可以作为函数FindNextVolumeMountPoint和FindVolumeMountPointClose的参数,根据返回的句柄FindNextVolumeMountPoint函数可以继续查找下一个驱动器的挂载点。也可以在不再需要的时候,通过Find-VolumeMountPointClose函数来关闭该句柄。

如前文所述,在Linux中,所有的UUID都以文件的形式存放在目录/dev/disk/by-uuid 下,每个UUID文件链接到其对应设备的设备文件。而在文件/etc/mtab 中则给出了所有设备的设备文件和其挂载点的对应关系。因此通过函数readdir和opendir可以得到设备UUID和其挂载点的对应关系。当找到驱动器的第一个挂载点时,便将其所有挂载点记录到返回句柄指向的FILE结构对应的文件中。

这样,当要继续查找下一个挂载点时,就只需要读取句柄指向的FILE结构所对应的文件中的记录信息即可。

最后,当不再需要使用返回的句柄时,调用fclose函数即可关闭FindFirstVolumeMountPoint返回的句柄。

(3)其他操作

Windows中,不仅可以根据GUID获得驱动器挂载点,还可以反向根据驱动器挂载点得到GUID,函数GetVolumeNameForVolumeMountPoint便提供了这一功能。同样,还可以通过SetVolumeMountPoint函数将指定的GUID所对应的驱动器挂载到指定的路径上。

Linux中,要根据驱动器挂载点得到GUID,可以先通过函数readdir和opendir从文件/etc/mtab 中找到挂载点对应的设备文件。接下来再一次通过函数readdir和opendir从文件/dev/disk/by-uuid 中找到设备文件对应的UUID文件,从而找到驱动器对应的UUID,最后通过字符串转换,将其转换为Windows中的GUID格式。

若要将给定GUID对应的驱动器挂载到给定路径上,则可以根据GUID找到其设备文件,然后再调用mount函数。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈