咱們知道不一樣的操做系統有各自的文件系統,這些文件系統又存在不少差別,而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)中篇
歡迎關注: