java到c创建文件

530 阅读8分钟
  • 本文记录一下Android中从Java层代码的创建文件操作到c层代码的过程

API的那些事:

  • ISO C 是 International Standard for the C programming language 的缩写,又称 ANSI CStandard C。此标准明定了 C 语言的语法,标准 C 函式库应具备那些标头档、巨集定义、函式与物件 .... 等等,

    几乎在任何平台上的 C 语言 (包括非 UNIX 平台) 都支援此标准。

  • POSIX,Portable Operating System Interface 可移植操作系统接口,x表示是UNIX。是UNIX系统的一个设计标准, ISO C 的延伸。它是很多类UNIX系统也在支持兼容这个标准,如Linux。

    由于 glibc 是完全按照 POSIX 的标准制作的,同时搭配了符合 POSIX 标准的 Linux 核心,故在此环境下开发的程式可以做到完全符合 POSIX 的规格。

    遵循这个标准的好处是软件可以跨平台。说人话:类UNIX系统的接口标准,包含标准C。Java:一次编译到处运行。POSIX:相同代码,到处编译。

    只要是调用的POSIX标准的库函数,可以在任一系统上编译通过,然后运行。虽然是UNIX系统的标准,但是window也开始支持POSIX

  • glibc GNU C库,又名glibc,是GNU计划所实现的C标准库。 POSIX标准的代码实现,当然也包括了标准c,以及自身一些特性的不符合POSIX函数,为了代码可移植,尽量使用POSIX标准函数。

  • Bionic libc: Android使用 Bionic libc替代glibc。Bionic libc使用BSD协议。glibc 遵守的是GPL开源协议。GPL有个特点就是传染性:一旦系统中有软件使用了GPL授权协议,那么该系统相关代码必须开源。

    做了Android特性,及优化,提高性能。

  • Linux系统调用:Bionic libc/glibc 封装了linux系统调用,做成POSIX标准的函数库。系统调用是内核对外提供的标准的接口,内核另一种对外的接口是文件。

  • Java: 在涉及到系统调用的操作中,会使用 JNI调用native层,最终调用 libc 库的函数。libc的函数库,需要用的内核的地方,再对系统调用进行包装。

java层创建文件操作:

  • 使用 FileOutputStream fos = new FileOutputStream("myfile.txt");
    • 调用 FileOutputStream(String name) 构造函数时,若文件存在,则被新的覆盖,若不存在,则构建,但无法创建多级目录下的文件!!!!!!
    • 存在同名目录,或者不能被创建,或者因为其他原因无法打开抛出 FileNotFoundException
// libcore/ojluni/src/main/java/java/io/FileOutputStream.java
public class FileOutputStream extends OutputStream
{   
    public FileOutputStream(File file, boolean append)
        throws FileNotFoundException
    {
        String name = (file != null ? file.getPath() : null);
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkWrite(name);
        }
        if (name == null) {
            throw new NullPointerException();
        }
        if (file.isInvalid()) {
            throw new FileNotFoundException("Invalid file path");
        }
        // BEGIN Android-changed: Open files using IoBridge to share BlockGuard & StrictMode logic.
        // http://b/111268862
        // this.fd = new FileDescriptor();
        int flags = O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC);
        this.fd = IoBridge.open(name, flags);// 调用IoBridge.open 打开文件获取fd========关键代码===!!!
        // END Android-changed: Open files using IoBridge to share BlockGuard & StrictMode logic.

        // Android-changed: Tracking mechanism for FileDescriptor sharing.
        // fd.attach(this);
        this.isFdOwner = true;

        this.append = append;
        this.path = name;

        // Android-removed: Open files using IoBridge to share BlockGuard & StrictMode logic.
        // open(name, append);

        // Android-added: File descriptor ownership tracking.
        IoUtils.setFdOwner(this.fd, this);

        // Android-added: CloseGuard support.
        guard.open("close");
    }
    
        public void write(byte b[], int off, int len) throws IOException {
        // Android-added: close() check before I/O.
        if (closed && len > 0) {
            throw new IOException("Stream Closed");
        }

        // Android-added: Tracking of unbuffered I/O.
        tracker.trackIo(len);

        // Android-changed: Use IoBridge instead of calling native method.
        IoBridge.write(fd, b, off, len);
    }
}

// libcore/luni/src/main/java/libcore/io/IoBridge.java
    /**
     * java.io only throws FileNotFoundException when opening files, regardless of what actually
     * went wrong. Additionally, java.io is more restrictive than POSIX when it comes to opening
     * directories: POSIX says read-only is okay, but java.io doesn't even allow that.
     */
    @libcore.api.CorePlatformApi
    public static FileDescriptor open(String path, int flags) throws FileNotFoundException {
        FileDescriptor fd = null;
        try {
            fd = Libcore.os.open(path, flags, 0666);//调用Libcore.os.open 打开文件获取fd======关键代码!!!
            // Posix open(2) fails with EISDIR only if you ask for write permission.
            // Java disallows reading directories too.
            if (S_ISDIR(Libcore.os.fstat(fd).st_mode)) {//跟踪文件打开过程中会发现两次进入内核,原因就在这里===!!!!!!!!!!
                throw new ErrnoException("open", EISDIR);
            }
            return fd;
        } catch (ErrnoException errnoException) {
            try {
                if (fd != null) {
                    closeAndSignalBlockedThreads(fd);
                }
            } catch (IOException ignored) {
            }
            FileNotFoundException ex = new FileNotFoundException(path + ": " + errnoException.getMessage());
            ex.initCause(errnoException);
            throw ex;
        }
    }
	// write
    public static void write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
        ArrayUtils.throwsIfOutOfBounds(bytes.length, byteOffset, byteCount);
        if (byteCount == 0) {
            return;
        }
        try {
            while (byteCount > 0) {
                int bytesWritten = Libcore.os.write(fd, bytes, byteOffset, byteCount);
                byteCount -= bytesWritten;
                byteOffset += bytesWritten;
            }
        } catch (ErrnoException errnoException) {
            throw errnoException.rethrowAsIOException();
        }
    }

// libcore/luni/src/main/java/libcore/io/Libcore.java
/** @hide */
public final class Libcore {
    private Libcore() { }

    /**
     * Direct access to syscalls. Code should strongly prefer using {@link #os}
     * unless it has a strong reason to bypass the helpful checks/guards that it
     * provides.
     */
    public static final Os rawOs = new Linux();//==========创建Os对象=======关键代码==============!!!
}

// libcore/luni/src/main/java/libcore/io/Linux.java
public final class Linux implements Os {
    //========调用JNI层的 open函数 ====核心代码==============!!!!!!!!!!!
    // JNI 注册函数过程见 "JNI so 库的加载" 章节
    public native FileDescriptor open(String path, int flags, int mode) throws ErrnoException;
    
    public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException {
        // This indirection isn't strictly necessary, but ensures that our public interface is type safe.
        return writeBytes(fd, bytes, byteOffset, byteCount);
    }
    //============对于write而言,是调用的JNI层的 writeBytes ====核心代码====!!
    private native int writeBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException, InterruptedIOException;
}

native 层对应的 JNI 代码:

native FileDescriptor open(String path, int flags, int mode) 函数怎么到 Linux_open 的过程见 "JNI so 库的加载" 章节

// libcore/luni/src/main/native/libcore_io_Linux.cpp

static jobject Linux_open(JNIEnv* env, jobject, jstring javaPath, jint flags, jint mode) {
    ScopedUtfChars path(env, javaPath);
    if (path.c_str() == NULL) {
        return NULL;
    }
    //此处 open(path.c_str(), flags, mode) 调用了libc中的 open 系统调用函数
    int fd = throwIfMinusOne(env, "open", TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));//核心代码
    return fd != -1 ? jniCreateFileDescriptor(env, fd) : NULL;
}


static jint Linux_writeBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount) {
    ScopedBytesRO bytes(env, javaBytes);
    if (bytes.get() == NULL) {
        return -1;
    }
    // 这里宏内部展开之后,也是直接使用libc中的系统调用 write
    // 具体可以查看 libcore/luni/src/main/native/libcore_io_Linux.cpp 中对 IO_FAILURE_RETRY 的宏定义
    return IO_FAILURE_RETRY(env, ssize_t, write, javaFd, bytes.get() + byteOffset, byteCount);//核心代码!!
}

// open write 这些函数,再往底层分析就是 Linux的系统调用--->Linux虚拟文件系统--->驱动

JNI so 库的加载

// art/runtime/runtime.cc
// art虚拟机启动过程中加载JNI函数过程:
bool Runtime::Start() {
    ...
    // InitNativeMethods needs to be after started_ so that the classes
    // it touches will have methods linked to the oat file if necessary.
   {
     ScopedTrace trace2("InitNativeMethods");
     InitNativeMethods();
   }
    ...
}
void Runtime::InitNativeMethods() {
  {
    std::string error_msg;
     //=======================================关键代码=============加载 libjavacore.so 库
    if (!java_vm_->LoadNativeLibrary(
          env, "libjavacore.so", nullptr, WellKnownClasses::java_lang_Object, &error_msg)) {
      LOG(FATAL) << "LoadNativeLibrary failed for \"libjavacore.so\": " << error_msg;
    }
  }
}

// LoadNativeLibrary会调用so库的 JNI_OnLoad 方法:
// libcore/luni/src/main/native/Register.cpp
// DalvikVM calls this on startup, so we can statically register all our native methods.
jint JNI_OnLoad(JavaVM* vm, void*) {
    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        ALOGE("JavaVM::GetEnv() failed");
        abort();
    }

    ScopedLocalFrame localFrame(env);

#define REGISTER(FN) extern void FN(JNIEnv*); FN(env) //REGISTER 这个宏表示声明这个函数,并调用
    REGISTER(register_android_system_OsConstants);
    //    REGISTER(register_java_lang_StringToReal);
    REGISTER(register_java_lang_invoke_MethodHandle);
    REGISTER(register_java_lang_invoke_VarHandle);
    REGISTER(register_java_math_NativeBN);
    REGISTER(register_libcore_icu_ICU);
    REGISTER(register_libcore_icu_TimeZoneNames);
    REGISTER(register_libcore_io_AsynchronousCloseMonitor);
    REGISTER(register_libcore_io_Linux);//调用 register_libcore_io_Linux 函数 ====关键代码==========!!!
    REGISTER(register_libcore_io_Memory);
    REGISTER(register_libcore_util_NativeAllocationRegistry);
    REGISTER(register_org_apache_harmony_dalvik_NativeTestTarget);
    REGISTER(register_org_apache_harmony_xml_ExpatParser);
    REGISTER(register_sun_misc_Unsafe);
#undef REGISTER

    JniConstants::Initialize(env);
    return JNI_VERSION_1_6;
}   

// libcore/luni/src/main/native/libcore_io_Linux.cpp
void register_libcore_io_Linux(JNIEnv* env) {
    // Note: it is safe to only cache the fields as boot classpath classes are never
    //       unloaded.
    ScopedLocalRef<jclass> int32RefClass(env, env->FindClass("android/system/Int32Ref"));
    CHECK(int32RefClass != nullptr);
    int32RefValueFid = env->GetFieldID(int32RefClass.get(), "value", "I");
    CHECK(int32RefValueFid != nullptr);

    ScopedLocalRef<jclass> int64RefClass(env, env->FindClass("android/system/Int64Ref"));
    CHECK(int64RefClass != nullptr);
    int64RefValueFid = env->GetFieldID(int64RefClass.get(), "value", "J");
    CHECK(int64RefValueFid != nullptr);

    // 注册包名为 libcore.io.Linux 的JNI方法
    jniRegisterNativeMethods(env, "libcore/io/Linux", gMethods, NELEM(gMethods));//关键代码========!!!
}

// JNINativeMethod 结构体数组
static JNINativeMethod gMethods[] = {
    NATIVE_METHOD(Linux, getpid, "()I"),
    // 把 Linux_open 和 java层的native方法 open 关联
    // NATIVE_METHOD 见 "NATIVE_METHOD 宏展开过程" 章节
    // NATIVE_METHOD 宏就是定义一个 JNINativeMethod 结构体
    NATIVE_METHOD(Linux, open, "(Ljava/lang/String;II)Ljava/io/FileDescriptor;"),//关键代码===========!!!
    NATIVE_METHOD(Linux, writeBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;II)I"),//关键代码====!!!
    NATIVE_METHOD(Linux, remove, "(Ljava/lang/String;)V"),
    NATIVE_METHOD(Linux, setuid, "(I)V"),
    NATIVE_METHOD(Linux, unlink, "(Ljava/lang/String;)V"),
}

// libcore/luni/src/main/native/libcore_io_Linux.cpp
// 宏展开后 open 对应的函数是 Linux_open
static jobject Linux_open(JNIEnv* env, jobject, jstring javaPath, jint flags, jint mode) {
    ScopedUtfChars path(env, javaPath);
    if (path.c_str() == NULL) {
        return NULL;
    }
    //============核心代码==============!!!!!!!!!!!
    //此处 open(path.c_str(), flags, mode) 调用了libc中的 open 系统调用函数
    int fd = throwIfMinusOne(env, "open", TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));
    return fd != -1 ? jniCreateFileDescriptor(env, fd) : NULL;
}

// 宏展开后 writeBytes 对应的函数是 Linux_writeBytes
static jint Linux_writeBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount) {
    ScopedBytesRO bytes(env, javaBytes);
    if (bytes.get() == NULL) {
        return -1;
    }
    // 这里宏内部展开之后,也是直接使用libc中的系统调用 write
    return IO_FAILURE_RETRY(env, ssize_t, write, javaFd, bytes.get() + byteOffset, byteCount);//核心代码!!!
}

NATIVE_METHOD 宏展开过程

// libnativehelper/platform_include/nativehelper/jni_macros.h

#define NATIVE_METHOD(className, functionName, signature)                \
  MAKE_JNI_NATIVE_METHOD(#functionName, signature, className ## _ ## functionName)

#define MAKE_JNI_NATIVE_METHOD(name, signature, function)                      \
  _NATIVEHELPER_JNI_MAKE_METHOD(kNormalNative, name, signature, function)


// 非 c++14 版本的使用下面的宏。暂不研究c++14的宏:MAKE_CHECKED_JNI_NATIVE_METHOD
// Older versions of C++ or C code get the regular macro that's unchecked.
// Expands to a compound expression whose type is JNINativeMethod.
#define _NATIVEHELPER_JNI_MAKE_METHOD(kind, name, sig, fn)         \
  _NATIVEHELPER_JNI_MAKE_METHOD_OLD(kind, name, sig, fn)

// Expands to an expression whose type is JNINativeMethod.
// This is for older versions of C++ or C, so it has no compile-time checking.
#define _NATIVEHELPER_JNI_MAKE_METHOD_OLD(kind, name, sig, fn)     \
  (                                                                \
    (JNINativeMethod) {                                            \
        (name),                                                    \
        (sig),                                                     \
        _NATIVEHELPER_JNI_MACRO_CAST(reinterpret_cast, void *)(fn) \
    }                                                              \
  )

// C-style cast for C, C++-style cast for C++ to avoid warnings/errors.
#if defined(__cplusplus)
#define _NATIVEHELPER_JNI_MACRO_CAST(which_cast, to) \
    which_cast<to>
#else
#define _NATIVEHELPER_JNI_MACRO_CAST(which_cast, to) \
    (to)
#endif


// libnativehelper/platform_include/nativehelper/detail/signature_checker.h
// What kind of JNI does this type belong to?
enum NativeKind {
  kNotJni,        // Illegal parameter used inside of a function type.
  kNormalJniCallingConventionParameter,
  kNormalNative,
  kFastNative,      // Also valid in normal.
  kCriticalNative,  // Also valid in fast/normal.
};


// 宏展开过程:
NATIVE_METHOD(Linux, open, "(Ljava/lang/String;II)Ljava/io/FileDescriptor;")
--> MAKE_JNI_NATIVE_METHOD("open", "(Ljava/lang/String;II)Ljava/io/FileDescriptor;", Linux_open)
--> _NATIVEHELPER_JNI_MAKE_METHOD(2, "open", "(Ljava/lang/String;II)Ljava/io/FileDescriptor;", Linux_open)
-->
#define _NATIVEHELPER_JNI_MAKE_METHOD_OLD(kind, name, sig, fn)     \
  (                                                                \
    (JNINativeMethod) {                                            \
        (name),                                                    \
        (sig),                                                     \
        _NATIVEHELPER_JNI_MACRO_CAST(reinterpret_cast, void *)(fn) \
    }                                                              \
  )
//展开就是个 JNINativeMethod  结构体:    
  ((JNINativeMethod) {("open"),("(Ljava/lang/String;II)Ljava/io/FileDescriptor;"),_NATIVEHELPER_JNI_MACRO_CAST(reinterpret_cast, void *)(Linux_open)})
--> 
  (
    (JNINativeMethod) {("open"), ("(Ljava/lang/String;II)Ljava/io/FileDescriptor;"), reinterpret_cast<void *>(Linux_open) }
   )  

JNINativeMethod 结构体

// libnativehelper/include_jni/jni.h
typedef struct {
    const char* name;
    const char* signature;
    void*       fnPtr;
} JNINativeMethod;

编译

编译模块


// libcore/luni/src/main/native/Android.bp
filegroup {
    name: "luni_native_srcs",  // libcore_io_Linux.cpp 所在的 filegroup 为 luni_native_srcs
    visibility: [
        "//libcore",
    ],
    srcs: [
        "ExecStrings.cpp",
        "IcuUtilities.cpp",
        "JniConstants.cpp",
        "JniException.cpp",
        "NetworkUtilities.cpp",
        "Register.cpp",
        "ZipUtilities.cpp",
        "android_system_OsConstants.cpp",
        "cbigint.cpp",
        "java_lang_StringToReal.cpp",
        "java_lang_invoke_MethodHandle.cpp",
        "java_lang_invoke_VarHandle.cpp",
        "java_math_NativeBN.cpp",
        "libcore_icu_ICU.cpp",
        "libcore_icu_TimeZoneNames.cpp",
        "libcore_io_AsynchronousCloseMonitor.cpp",
        "libcore_io_Linux.cpp",//=== Linux_open所在的源码文件 ==========核心代码==============!!!!!!!!!!!
        "libcore_io_Memory.cpp",
        "libcore_util_NativeAllocationRegistry.cpp",
        "org_apache_harmony_xml_ExpatParser.cpp",
        "sun_misc_Unsafe.cpp",
        "valueOf.cpp",
    ],
}

// libcore/NativeCode.bp    
cc_library_shared {
    name: "libjavacore",//== libcore_io_Linux.cpp 最终编译进了 libjavacore.so 库 ===========核心代码=========!!!!!!!!!!!
    visibility: [
        "//art/build/apex",
    ],
    apex_available: [
        "com.android.art.release",
        "com.android.art.debug",
    ],
    defaults: [
        "core_native_default_flags",
        "core_native_default_libs",
    ],
    srcs: [
        ":luni_native_srcs",//========= libjavacore 编译使用了 luni_native_srcs filegroup ========核心代码=======!!!!!!!!!!!
        "dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp",
    ],
    shared_libs: [
        "libandroidio",
        "libbase",
        "libcrypto",
        "libexpat",
        "libicuuc",
        "libicui18n",
        "libnativehelper",
        "libz",
    ],
    static_libs: [
        "libandroidicuinit",
        "libziparchive",
    ],
    target: {
        android: {
            cflags: [
                // -DANDROID_LINK_SHARED_ICU4C to enable access to the full ICU4C.
                // See external/icu/android_icu4c/include/uconfig_local.h
                // for more information.
                "-DANDROID_LINK_SHARED_ICU4C",
            ],
        },
    },
}

手机上 libjavacore.so 库所在位置:

/apex/com.android.art/lib64/libjavacore.so
/apex/com.android.art/lib/libjavacore.so

编译 libjavacore.so:

libjavacore.so 在 com.android.art 中,apex 文件在手机目录 /system/apex/com.android.art.capex

编译目标为 com.android.art

time prebuilts/build-tools/linux-x86/bin/ninja -j32 -f out/combined-XXX.ninja  com.android.art



#注 如果需要新增类的话,需要在此bp文件中添加相应类文件: libcore\openjdk_java_files.bp