咱们知道不一样的操做系统有各自的文件系统,这些文件系统又存在不少差别,而Java 由于是跨平台的,因此它必需要统一处理这些不一样平台文件系统之间的差别,才能往上提供统一的入口。java
JDK 里面抽象出了一个 FileSystem 来表示文件系统,不一样的操做系统经过继承该类实现各自的文件系统,好比 Windows NT/2000 操做系统则为 WinNTFileSystem,而 unix-like 操做系统为 UnixFileSystem。数组
须要注意的一点是,WinNTFileSystem类 和 UnixFileSystem类并非在同一个 JDK 里面,也就是说它们是分开的,你只能在 Windows 版本的 JDK 中找到 WinNTFileSystem,而在 unix-like 版本的 JDK 中找到 UnixFileSystem,一样地,其余操做系统也有本身的文件系统实现类。缓存
这里分红两个系列分析 JDK 对两种(Windows 和 unix-like )操做系统的文件系统的实现类,前面已经讲了 Windows操做系统,对应为 WinNTFileSystem 类。这里接着讲 unix-like 操做系统,对应为 UnixFileSystem 类。篇幅所限,分为上中下篇,此为下篇。安全
--java.lang.Object
--java.io.FileSystem
--java.io.UnixFileSystem
复制代码
class UnixFileSystem extends FileSystem
复制代码
private final char slash;
private final char colon;
private final String javaHome;
private ExpiringCache cache = new ExpiringCache();
private ExpiringCache javaHomePrefixCache = new ExpiringCache();
复制代码
该方法用于列出指定目录下的全部文件和目录,本地方法处理逻辑以下,bash
java/lang/String
类对象,并检查不能为NULL。private native boolean delete0(File f);
JNIEXPORT jobjectArray JNICALL
Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this,
jobject file)
{
DIR *dir = NULL;
struct dirent64 *ptr;
struct dirent64 *result;
int len, maxlen;
jobjectArray rv, old;
jclass str_class;
str_class = JNU_ClassString(env);
CHECK_NULL_RETURN(str_class, NULL);
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
dir = opendir(path);
} END_PLATFORM_STRING(env, path);
if (dir == NULL) return NULL;
ptr = malloc(sizeof(struct dirent64) + (PATH_MAX + 1));
if (ptr == NULL) {
JNU_ThrowOutOfMemoryError(env, "heap allocation failed");
closedir(dir);
return NULL;
}
len = 0;
maxlen = 16;
rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
if (rv == NULL) goto error;
while ((readdir64_r(dir, ptr, &result) == 0) && (result != NULL)) {
jstring name;
if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
continue;
if (len == maxlen) {
old = rv;
rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
if (rv == NULL) goto error;
if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
(*env)->DeleteLocalRef(env, old);
}
#ifdef MACOSX
name = newStringPlatform(env, ptr->d_name);
#else
name = JNU_NewStringPlatform(env, ptr->d_name);
#endif
if (name == NULL) goto error;
(*env)->SetObjectArrayElement(env, rv, len++, name);
(*env)->DeleteLocalRef(env, name);
}
closedir(dir);
free(ptr);
old = rv;
rv = (*env)->NewObjectArray(env, len, str_class, NULL);
if (rv == NULL) {
return NULL;
}
if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
return NULL;
}
return rv;
error:
closedir(dir);
free(ptr);
return NULL;
}
复制代码
该方法用来建立目录,本地方法很简单,就是获取 File 对象对应的路径,再经过 mkdir 函数建立目录。其中0777,表示文件全部者、文件全部者所在的组的用户、其余用户,都有权限进行读、写、执行的操做。并发
public native boolean createDirectory(File f);
JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_createDirectory(JNIEnv *env, jobject this,
jobject file)
{
jboolean rv = JNI_FALSE;
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
if (mkdir(path, 0777) == 0) {
rv = JNI_TRUE;
}
} END_PLATFORM_STRING(env, path);
return rv;
}
复制代码
该方法用于重命名文件,须要将标准路径缓存和标准路径前缀缓存都清掉,而后调用本地方法 rename0 执行重命名操做。机器学习
public boolean rename(File f1, File f2) {
cache.clear();
javaHomePrefixCache.clear();
return rename0(f1, f2);
}
private native boolean rename0(File f1, File f2);
复制代码
本地方法主要调用了 rename 函数,根据 Java 层传入的两个 File 对象对应的路径进行重命名。分布式
JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_rename0(JNIEnv *env, jobject this,
jobject from, jobject to)
{
jboolean rv = JNI_FALSE;
WITH_FIELD_PLATFORM_STRING(env, from, ids.path, fromPath) {
WITH_FIELD_PLATFORM_STRING(env, to, ids.path, toPath) {
if (rename(fromPath, toPath) == 0) {
rv = JNI_TRUE;
}
} END_PLATFORM_STRING(env, toPath);
} END_PLATFORM_STRING(env, fromPath);
return rv;
}
复制代码
该方法用来设置文件或目录的最后修改时间。本地方法是先获取 File 对象对应的路径,再用 stat64 函数获取指定文件或目录的属性,接着经过 st_atime 成员获得文件最后访问时间,这个时间不用改,要改的是最后修改时间,因此根据 Java 层传入的时间做为最后修改时间,最后经过 utimes 函数设置文件的最后修改时间。函数
public native boolean setLastModifiedTime(File f, long time);
JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
jobject file, jlong time)
{
jboolean rv = JNI_FALSE;
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
struct stat64 sb;
if (stat64(path, &sb) == 0) {
struct timeval tv[2];
tv[0].tv_sec = sb.st_atime;
tv[0].tv_usec = 0;
tv[1].tv_sec = time / 1000;
tv[1].tv_usec = (time % 1000) * 1000;
if (utimes(path, tv) == 0)
rv = JNI_TRUE;
}
} END_PLATFORM_STRING(env, path);
return rv;
}
复制代码
该方法用于将指定文件设置成只读。本地方法逻辑是先经过 statMode 函数获取文件的属性,再去掉 S_IWUSR、S_IWGRP 和 S_IWOTH 标识,分别表示用户写权限、用户组用户写权限和非全部者和用户组用户写权限。最后经过 chmod 函数完成文件只读属性设置。学习
public native boolean setReadOnly(File f);
JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_setReadOnly(JNIEnv *env, jobject this,
jobject file)
{
jboolean rv = JNI_FALSE;
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
int mode;
if (statMode(path, &mode)) {
if (chmod(path, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)) >= 0) {
rv = JNI_TRUE;
}
}
} END_PLATFORM_STRING(env, path);
return rv;
}
复制代码
该方法用于获取可用的文件系统的根文件对象的数组,对于 unix-like 来讲,也就是只有一个根目录了,这以前还会用安全管理器检查下是否有根目录的权限。
public File[] listRoots() {
try {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead("/");
}
return new File[] { new File("/") };
} catch (SecurityException x) {
return new File[0];
}
}
复制代码
该方法用于获取所挂载的文件系统(包含该方法指定的路径)的空间大小,包括总空间大小、空闲空间大小和可用空间大小,Java 层分别用 SPACE_TOTAL = 0
SPACE_FREE = 1
SPACE_USABLE = 2
标识。要查询某个文件的根目录的某某空间大小则将对应的标识传入,经过 getSpace0 本地方法得到。
能够看到本地方法使用了 statfs 或 statvfs64 函数来获取文件系统的信息,进而经过块数量乘以块大小获得总空间大小、空闲空间大小和可用空间大小。
至于为何分别用 statfs 或 statvfs64 函数,其中 statfs 函数属于特定系统的,而 statvfs64 函数符合 POSIX 标准。通常最好优先使用 statvfs,之前的 JDK(1.7) 实现也是经过statvfs,而这里用 #ifdef MACOSX
进行判断,应该是由于 MACOSX 系统对 statvfs64 函数支持很差,
JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getSpace(JNIEnv *env, jobject this,
jobject file, jint t)
{
jlong rv = 0L;
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
#ifdef MACOSX
struct statfs fsstat;
#else
struct statvfs64 fsstat;
#endif
memset(&fsstat, 0, sizeof(fsstat));
#ifdef MACOSX
if (statfs(path, &fsstat) == 0) {
switch(t) {
case java_io_FileSystem_SPACE_TOTAL:
rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
long_to_jlong(fsstat.f_blocks));
break;
case java_io_FileSystem_SPACE_FREE:
rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
long_to_jlong(fsstat.f_bfree));
break;
case java_io_FileSystem_SPACE_USABLE:
rv = jlong_mul(long_to_jlong(fsstat.f_bsize),
long_to_jlong(fsstat.f_bavail));
break;
default:
assert(0);
}
}
#else
if (statvfs64(path, &fsstat) == 0) {
switch(t) {
case java_io_FileSystem_SPACE_TOTAL:
rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
long_to_jlong(fsstat.f_blocks));
break;
case java_io_FileSystem_SPACE_FREE:
rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
long_to_jlong(fsstat.f_bfree));
break;
case java_io_FileSystem_SPACE_USABLE:
rv = jlong_mul(long_to_jlong(fsstat.f_frsize),
long_to_jlong(fsstat.f_bavail));
break;
default:
assert(0);
}
}
#endif
} END_PLATFORM_STRING(env, path);
return rv;
}
复制代码
该方法用于获取系统容许的最大文件名长度,直接经过 getNameMax0 本地获取最大长度,而后判断不能超过整型的最大值。
public int getNameMax(String path) {
long nameMax = getNameMax0(path);
if (nameMax > Integer.MAX_VALUE) {
nameMax = Integer.MAX_VALUE;
}
return (int)nameMax;
}
复制代码
本地方法经过 pathconf 函数来获取指定路径下的文件名长度限制值,传入 _PC_NAME_MAX 便可获得。
JNIEXPORT jlong JNICALL
Java_java_io_UnixFileSystem_getNameMax0(JNIEnv *env, jobject this,
jstring pathname)
{
jlong length = -1;
WITH_PLATFORM_STRING(env, pathname, path) {
length = (jlong)pathconf(path, _PC_NAME_MAX);
} END_PLATFORM_STRING(env, path);
return length != -1 ? length : (jlong)NAME_MAX;
}
复制代码
该方法用于比较两个 File 对象,其实就是直接比较路径字符串。
public int compare(File f1, File f2) {
return f1.getPath().compareTo(f2.getPath());
}
复制代码
该方法用于获取 File 对象的哈希值,获取 File对象路径,再将字符串变成小写,再调用字符串的 hashCode 方法,最后与 1234321 进行异或运算,获得的值即为该文件的哈希值。
public int hashCode(File f) {
return f.getPath().hashCode() ^ 1234321;
}
复制代码
=============广告时间===============
公众号的菜单已分为“分布式”、“机器学习”、“深度学习”、“NLP”、“Java深度”、“Java并发核心”、“JDK源码”、“Tomcat内核”等,可能有一款适合你的胃口。
鄙人的新书《Tomcat内核设计剖析》已经在京东销售了,有须要的朋友能够购买。感谢各位朋友。
=========================
相关阅读:
JDK不一样操做系统的FileSystem(Windows)上篇
JDK不一样操做系统的FileSystem(Windows)中篇
JDK不一样操做系统的FileSystem(Windows)下篇
JDK不一样操做系统的FileSystem(unix-like)上篇
JDK不一样操做系统的FileSystem(unix-like)中篇
欢迎关注: