- 本文记录一下Android中从Java层代码的创建文件操作到c层代码的过程
API的那些事:
-
ISO C 是 International Standard for the C programming language 的缩写,又称 ANSI C、Standard 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