這是一個連載的博文系列,我將持續為大家提供儘可能透徹的Android原始碼分析 github連載地址
前言
前文講到虛擬機器建立後反射呼叫了ZygoteInit的main方法,說到虛擬機器,我們就不得不說下JNI,它是溝通Java和C++的橋樑。 JNI全稱是Java Native Interface,可以把它理解為一種介面程式設計方式,就像我們平常開發的C/S模式一樣, Client和Server要通訊,那就得用介面。JNI主要包括兩個方面的內容:
- C++呼叫Java
- Java呼叫C++
本文涉及到的檔案
platform/libnativehelper/include/nativehelper/jni.h
platform/art/runtime/java_vm_ext.cc
platform/art/runtime/jni_internal.cc
platform/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
platform/libcore/dalvik/src/main/java/dalvik/system/ZygoteHooks
platform/art/runtime/native/dalvik_system_ZygoteHooks.cc
platform/art/runtime/runtime.h
platform/libnativehelper/JNIHelp.cpp
platform/libcore/luni/src/main/java/android/system/Os.java
platform/libcore/luni/src/main/java/libcore/io/Libcore.java
platform/libcore/luni/src/main/java/libcore/io/BlockGuardOs.java
platform/libcore/luni/src/main/java/libcore/io/ForwardingOs.java
platform/libcore/luni/src/main/java/libcore/io/Linux.java
platform/libcore/luni/src/main/native/libcore_io_Linux.cpp
複製程式碼
一、C++呼叫Java
為什麼我先講C++呼叫Java呢?因為前文建立了虛擬機器後,首先是從C++呼叫了Java,所以我接著前文的例子來講, 我們回顧一下之前C++呼叫ZygoteInit的main函式的過程,我將分段一步步為大家解釋。
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
/*
* We want to call main() with a String array with arguments in it.
* At present we have two arguments, the class name and an option string.
* Create an array to hold them.
*/
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
for (size_t i = 0; i < options.size(); ++i) {
jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
assert(optionsStr != NULL);
env->SetObjectArrayElement(strArray, i + 1, optionsStr);
}
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
char* slashClassName = toSlashClassName(className);//將字元中的.轉換為/
jclass startClass = env->FindClass(slashClassName);//找到class
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);//呼叫main函式
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
free(slashClassName);
...
}
複製程式碼
1.1 Java中各型別在C++的對應關係
比如說我們Java中有常見的Class,String,int,short等,這些在C++中並不是叫原來的名字,而是另外取了個名字, 基本就是在原來的名字前加了個j,表示java. 下面是他們的對應關係
基本資料型別和void
Java型別 | C++型別 |
---|---|
boolean | jboolean |
byte | jbyte |
char | jchar |
short | jshort |
int | jint |
long | jlong |
float | jfloat |
double | jdouble |
void | void |
引用資料型別
Java型別 | C++型別 |
---|---|
All objects | jobject |
java.lang.Class例項 | jclass |
java.lang.String例項 | jstring |
java.lang.Throwable例項 | jthrowable |
Object[](包含Class,String,Throwable) | jobjectArray |
boolean[] | jbooleanArray |
byte[](其他基本資料型別類似) | jbyteArray |
那其實下面的程式碼就好理解了,就相當於定義了三個區域性變數,型別為Class,String[],String
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
複製程式碼
1.2 env->FindClass
我們再接著往下看,env->FindClass, env是虛擬機器的環境,可以類比為Android中無處不在的Context,
但是這個env是指特定執行緒的環境,也就是說一個執行緒對應一個env.
env有許多的函式,FindClass只是其中一個,作用就是根據ClassName找到對應的class, 用法是不是跟Java中反射獲取Class有點像,其實Java反射也是native方法,也得走到C++層,在實現上也是跟env->FindClass一樣.
我們來具體看看env->FindClass的實現,env的型別是JNIEnv,定義在platform/libnativehelper/include/nativehelper/jni.h中, 這個JNIEnv 在C環境和C++環境型別不一樣,在C環境中定義的是JNINativeInterface* , 而C++中定義的是_JNIEnv,_JNIEnv其實內部也是呼叫JNINativeInterface的對應函式,只是做了層代理, JNINativeInterface是個結構體,裡面就有我們要找的函式FindClass
#if defined(__cplusplus) //如果是C++
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else //如果是C
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif
struct _JNIEnv {
const struct JNINativeInterface* functions;
...
jclass FindClass(const char* name)
{ return functions->FindClass(this, name); }
...
}
struct JNINativeInterface {
...
jclass (*FindClass)(JNIEnv*, const char*);
...
}
複製程式碼
那這個結構體JNINativeInterface中FindClass的函式指標什麼時候賦值的呢?還記得上文中有個建立虛擬機器的函式JNI_CreateJavaVM, 裡面有個引數就是JNIEnv,其實也就是在建立虛擬機器的時候把函式指標賦值的,我們知道JNI_CreateJavaVM是載入libart.so時獲取的, 那我們就得找libart.so的原始碼,這個對應的原始碼在platform/art/runtime/java_vm_ext.cc,它會呼叫Runtime::Create函式去新建執行緒, 線上程新建的過程中會對JNIEnv進行賦值,JNI_CreateJavaVM函式最後會去呼叫執行緒的GetJniEnv得到JNIEnv的例項,將例項賦值給p_env.
(執行緒在新建過程中如何對JNIEnv進行賦值的,就不細講了,我提供幾個關鍵的函式,runtime.cc的Create和Init、thread.cc的Attach和Init、 jni_env_ext.cc的Create、jni_internal.cc的GetJniNativeInterface,涉及到的檔案我都放在AOSP專案中,有興趣的可以去看看. )
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
...
if (!Runtime::Create(options, ignore_unrecognized)) {
return JNI_ERR;
}
*p_env = Thread::Current()->GetJniEnv();
}
複製程式碼
GetJniEnv返回的是一個JNINativeInterface的例項,定義在/platform/art/runtime/jni_internal.cc,其中就有我們要找的FindClass
const JNINativeInterface gJniNativeInterface = {
nullptr, // reserved0.
nullptr, // reserved1.
nullptr, // reserved2.
nullptr, // reserved3.
JNI::GetVersion,
JNI::DefineClass,
JNI::FindClass,
}
複製程式碼
我們看到例項中FindClass對應的函式是JNI::FindClass,定義在當前檔案中,FindClass的工作是交給ClassLinker, ClassLinker內部的實現是通過ClassLoader獲取一個ClassTable物件,再通過ClassTable中的一個HashSet得到對應的Class, ClassLoader其實我們也比較熟悉,Java層中就有,我們apk中的dex檔案就是需要ClassLoader去載入,最終會將Class裝進一個HashSet中, 因此,我們FindClass也去這個HashSet中去找.
(ClassLinker內部的實現我就不細講了,我提供幾個關鍵的函式,class_linker.cc的FindClass和LookupClass、class_table.cc的Lookup ,涉及到的檔案我都放在AOSP專案中,有興趣同學可以去具體看看.)
static jclass FindClass(JNIEnv* env, const char* name) {
CHECK_NON_NULL_ARGUMENT(name);
Runtime* runtime = Runtime::Current();
ClassLinker* class_linker = runtime->GetClassLinker(); //獲取ClassLinker
std::string descriptor(NormalizeJniClassDescriptor(name));
ScopedObjectAccess soa(env);
mirror::Class* c = nullptr;
if (runtime->IsStarted()) {
StackHandleScope<1> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(GetClassLoader(soa)));
c = class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader); //查詢類
} else {
c = class_linker->FindSystemClass(soa.Self(), descriptor.c_str()); //查詢系統類
}
return soa.AddLocalReference<jclass>(c);
}
複製程式碼
說完env->FindClass,其實其他env->方式呼叫的函式也就大體知道原始碼在哪兒了,在接下來的分析中我就只說明下對應函式的作用,具體實現可以根據 自己的需要深入去看.
1.3 其他env函式
env函式特別多,我這裡只列舉一些我們常用的
新建例項,相當於Java中的new
函式名 | 作用 | 類比Java |
---|---|---|
NewObject | 新建Object | new Object |
NewStringUTF | 新建String字元 | new String() |
NewObjectArray | 新建Object陣列 | new Object[] |
New(Type)Array | 新建Type陣列,如NewByteArray | new byte[] |
獲取和設定成員變數和類變數,相當於Java中的獲取和設定變數,下面以A a=new A()為例子
函式名 | 作用 | 類比Java |
---|---|---|
GetFieldID | 獲取成員變數id,所有獲取成員變數的方法都要傳入這個值 | -- |
GetObjectField | 獲取Object型別的成員變數 | a.object |
Get(Type)Field | 獲取Type型別的成員變數,如GetBooleanField | bool b=a.bool |
Set(Type)Field | 設定Type型別的成員變數,如SetBooleanField | a.bool=b |
GetStaticFieldID | 獲取類變數id,所有獲取類變數的方法都要傳入這個值 | -- |
GetStaticObjectField | 獲取Object型別的類變數 | A.object |
GetStatic(Type)Field | 獲取Type型別的類變數,如GetStaticBooleanField | bool b=A.bool |
SetStatic(Type)Field | 設定Type型別的類變數,如SetStaticBooleanField | A.bool=b |
呼叫成員方法和類方法,相當於Java中的呼叫方法,下面以A a=new A()為例子
函式名 | 作用 | 類比Java |
---|---|---|
GetMethodID | 獲取成員方法id,所有獲取成員方法的方法都要傳入這個值 | -- |
CallObjectMethod | 呼叫返回值為Object型別的成員方法 | Object o=a.a() |
Call(Type)Method | 呼叫返回值為Type型別的成員方法,如CallBooleanMethod | bool b=a.b() |
GetStaticMethodID | 獲取類方法id,所有獲取類方法的方法都要傳入這個值 | -- |
CallStaticObjectMethod | 呼叫返回值為Object型別的類方法 | Object o=A.a() |
CallStatic(Type)Method | 呼叫返回值為Type型別的類方法,如CallStaticBooleanMethod | bool b=A.b() |
陣列相關操作,以bool[] bs=new bool[] 為例
函式名 | 作用 | 類比Java |
---|---|---|
Get(Type)ArrayElements | 獲取Type型別的陣列的某個元素 | bool b=bs[0] |
Set(Type)ArrayElements | 設定Type型別的陣列的某個元素 | bs[0]=b |
記憶體釋放相關,這個是C++獨有的,沒有Java相應的呼叫
函式名 | 作用 | 類比Java |
---|---|---|
ReleaseStringUTFChars | 釋放String | -- |
Release(Typge)ArrayElements | 釋放Type型別的陣列 | -- |
我這裡只是籠統地列舉了一些env函式的作用,對於引數及返回值並沒有細講,主要是這些屬於API範疇的東西,要用的時候再查也不遲
1.4 函式簽名
start函式最後會呼叫main函式,在獲取main函式時需要傳遞三個引數,第一個是函式所在的類,第二個是函式名稱,第三個就是函式簽名
jmethodID startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V");
複製程式碼
函式簽名其實就是對一個函式的引數及返回值的一種符號表示,表示形式是 (params)return 下面我列舉一下符號與Java型別的一一對應關係:
基本資料型別和void,我們可以看到除了boolean和long表示得不一樣外,其他都是以首字母進行表示,我想主要原因可能是B與byte衝突了,L與object衝突
符號 | Java型別 |
---|---|
B | byte |
C | char |
S | short |
I | int |
F | float |
D | double |
Z | boolean |
J | long |
V | void |
引用資料型別和陣列,引用資料型別以L開頭,後面接完整路徑,最後有個分號,這個分號一定不要忘記!一定不要忘記!一定不要忘記!陣列用 [ 表示
符號 | Java型別 |
---|---|
L/java/lang/String; | String |
[I | int[] |
[L/java/lang/object; | object[] |
我們回到剛才的例子 ([Ljava/lang/String;)V ,這個就表示main函式的引數是String[],返回值是void.
1.5 異常處理
我們在Java中經常用try catch來處理異常非常方便,我們在C++中呼叫Java函式時,也可以去捕獲異常,我們可以有兩種方式:
- ExceptionCheck
- ExceptionOccurred
我先講講 ExceptionCheck ,這個函式是會返回一個bool值,true表示有異常,false表示沒有異常
env->CallStaticVoidMethod(cls,mid);
if (env->ExceptionCheck()) { // 檢查JNI呼叫是否有引發異常
env->ExceptionDescribe(); //列印錯誤日誌堆疊資訊
env->ExceptionClear(); // 清除引發的異常
env->ThrowNew(env->FindClass(env,"java/lang/Exception"),"JNI丟擲的異常!"); //丟擲異常
}
複製程式碼
再看看ExceptionOccurred,這個用法其實跟ExceptionCheck差不多,只是它返回的不是bool值,而是當前異常的引用
jthrowable exc = NULL;
exc = env->ExceptionOccurred(); // 返回一個指向當前異常物件的引用
if (exc) {
env->ExceptionDescribe(); //列印錯誤日誌堆疊資訊
env->ExceptionClear(); // 清除引發的異常
env->ThrowNew(env->FindClass(env,"java/lang/Exception"),"JNI丟擲的異常!"); //丟擲異常
}
複製程式碼
start函式最後就用到了ExceptionCheck,因為呼叫Java的方法是可能引發異常的
二、Java呼叫C++
講完了C++呼叫Java,我們再看看Java如何呼叫C++,我們接著前面的講,之前通過 env->CallStaticVoidMethod(startClass, startMeth, strArray) 呼叫了ZygoteInit的 main 函式,我們就以main函式為例講解Java呼叫C++的過程。
2.1 main函式
main函式開頭有兩個方法呼叫 startZygoteNoThreadCreation和setpgid,這兩個其實都是native方法,接下來我就以這兩個為例子。
public static void main(String argv[]) {
...
ZygoteHooks.startZygoteNoThreadCreation(); //設定標記,不允許新建執行緒
try {
Os.setpgid(0, 0); //設定zygote程式組id為zygote的pid
} catch (ErrnoException ex) {
throw new RuntimeException("Failed to setpgid(0,0)", ex);
}
...
}
複製程式碼
startZygoteNoThreadCreation 定義在platform/libcore/dalvik/src/main/java/dalvik/system/ZygoteHooks中
/*
* Called by the zygote when starting up. It marks the point when any thread
* start should be an error, as only internal daemon threads are allowed there.
*/
public static native void startZygoteNoThreadCreation();
複製程式碼
2.2 native註冊
startZygoteNoThreadCreation 是一個native方法,我們知道native方法有兩種註冊方式,一種是靜態註冊,一種動態註冊。
所謂靜態註冊就是根據函式名稱和一些關鍵字就可以註冊, 比如 startZygoteNoThreadCreation 要靜態註冊的話,它對應的實現函式應該是
JNIEXPORT void JNICALL Java_dalvik_system_ZygoteHooks_startZygoteNoThreadCreation(JNIEnv *, jobject){
}
複製程式碼
也就是說首先得有JNIEXPORT,JNICALL這些關鍵字,其次函式名稱必須以Java開頭,後面接的是native函式所在類的完整路徑加native函式名, 最後引數及返回值要相同,引數會多出兩個:
- JNIEnv,表示JNI上下文,
- 一個是jobject,如果是static方法表示呼叫native函式的Class. 如果是普通方法表示呼叫native函式的物件
只要你按照這個規則寫,Java的native函式就會自動呼叫這個C++層的函式。這種靜態的註冊方式有個不好的地方就是函式名太長,書寫不方便,而且在首次呼叫時會有一個註冊過程, 影響效率,那有沒有其他方式呢?答案就是動態註冊
其實大多數frameworks層的native函式都是用動態方式註冊的,startZygoteNoThreadCreation函式也是
我們怎麼尋找startZygoteNoThreadCreation的實現呢?這裡有個規律,Google工程師喜歡以native所在類的完整路徑為C++的實現類名,比如 startZygoteNoThreadCreation所在類的完整路徑是dalvik.system.ZygoteHooks,我們嘗試搜尋dalvik_system_ZygoteHooks, 就會出現dalvik_system_ZygoteHooks.h和dalvik_system_ZygoteHooks.cc,我們看下dalvik_system_ZygoteHooks.cc
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(ZygoteHooks, nativePreFork, "()J"),
NATIVE_METHOD(ZygoteHooks, nativePostForkChild, "(JIZLjava/lang/String;)V"),
NATIVE_METHOD(ZygoteHooks, startZygoteNoThreadCreation, "()V"),
NATIVE_METHOD(ZygoteHooks, stopZygoteNoThreadCreation, "()V"),
};
void register_dalvik_system_ZygoteHooks(JNIEnv* env) {
REGISTER_NATIVE_METHODS("dalvik/system/ZygoteHooks");
}
複製程式碼
本來動態註冊是個很簡單的過程,直接呼叫 env->RegisterNatives ,將繫結資訊作為引數即可,但是這個原始碼裡寫得比較複雜,我一步步講吧
首先Java的native方法要呼叫到C++函式,肯定得有個鍵值對作為繫結資訊,也就是告訴虛擬機器哪個native該執行哪個C++函式,gMethods就是這樣一個角色
gMethods陣列的型別是JNINativeMethod,我們回顧下 JNINativeMethod ,它是一個結構體,name表示native函式名,signature表示用字串描述native函式的引數和返回值, fnPtr表示native指向的C++函式指標,這其實就是動態註冊的對映關係了,將native函式對應一個C++函式
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
複製程式碼
但是gMethods陣列中卻是NATIVE_METHOD,我們看看這個NATIVE_METHOD是什麼
#define NATIVE_METHOD(className, functionName, signature) \
{ #functionName, signature, (void*)(className ## _ ## functionName) }
複製程式碼
如何理解這個定義呢?#define是巨集定義,也就是說編譯期間要做巨集替換,這裡就是把NATIVE_METHOD替換成 {"","",(void*)()},具體怎麼替換呢?我們看到{}裡有些#、##,#表示字串化,相當於Java中的toString,##表示字串化拼接,相當於Java中的 String.format,以NATIVE_METHOD(ZygoteHooks, startZygoteNoThreadCreation, "()V")為例,替換後就是 {"startZygoteNoThreadCreation","()V",(void*)(ZygoteHooks_startZygoteNoThreadCreation) }
JNINativeMethod只是個結構體,真正註冊的函式是在 REGISTER_NATIVE_METHODS("dalvik/system/ZygoteHooks"),我們先看看 REGISTER_NATIVE_METHODS
#define REGISTER_NATIVE_METHODS(jni_class_name) \
RegisterNativeMethods(env, jni_class_name, gMethods, arraysize(gMethods))
複製程式碼
它也是一個巨集定義,指向的是RegisterNativeMethods,這個函式定義在platform/frameworks/base/core/jni/AndroidRuntime.cpp
/*
* Register native methods using JNI.
*/
/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
const char* className, const JNINativeMethod* gMethods, int numMethods)
{
return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}
複製程式碼
其實它呼叫的是jniRegisterNativeMethods,這個定義在platform/libnativehelper/JNIHelp.cpp, jniRegisterNativeMethods函式首先是將傳過來的類名字串找到對應的class,然後就是呼叫(*env)->RegisterNatives動態註冊JNI, 其實呼叫這麼多層,動態註冊最關鍵的就是構建一個結構體JNINativeMethod,然後呼叫(*env)->RegisterNatives,RegisterNatives屬於 虛擬機器內的函式了,今後講虛擬機器時我再具體去分析,這裡我們知道它的作用就行了.
extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods)
{
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
ALOGV("Registering %s's %d native methods...", className, numMethods);
scoped_local_ref<jclass> c(env, findClass(env, className)); //根據類名找到class
if (c.get() == NULL) {
char* tmp;
const char* msg;
if (asprintf(&tmp,
"Native registration unable to find class '%s'; aborting...",
className) == -1) {
// Allocation failed, print default warning.
msg = "Native registration unable to find class; aborting...";
} else {
msg = tmp;
}
e->FatalError(msg);
}
if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) { //動態註冊jni
char* tmp;
const char* msg;
if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) {
// Allocation failed, print default warning.
msg = "RegisterNatives failed; aborting...";
} else {
msg = tmp;
}
e->FatalError(msg);
}
return 0;
}
複製程式碼
我們接著上面的startZygoteNoThreadCreation函式講,由上可知這個native函式實際會呼叫ZygoteHooks_startZygoteNoThreadCreation, 它定義在platform/art/runtime/native/dalvik_system_ZygoteHooks.cc
static void ZygoteHooks_startZygoteNoThreadCreation(JNIEnv* env ATTRIBUTE_UNUSED,
jclass klass ATTRIBUTE_UNUSED) {
Runtime::Current()->SetZygoteNoThreadSection(true);
}
複製程式碼
其實它又是呼叫Runtime的SetZygoteNoThreadSection函式,這個定義在platform/art/runtime/runtime.h,這個函式的實現非常簡單, 就是將zygote_no_threads_這個bool值設定為想要的值
static Runtime* instance_;
// Whether zygote code is in a section that should not start threads.
bool zygote_no_threads_;
static Runtime* Current() {
return instance_;
}
void SetZygoteNoThreadSection(bool val) {
zygote_no_threads_ = val;
}
複製程式碼
由此我們可以看到startZygoteNoThreadCreation這個native函式經過層層呼叫,最終就是將一個bool變數設定為true. 講得是有點多了, 這裡主要是告訴大家如何去追蹤native函式的實現,因為這是閱讀frameworks層程式碼必備的技能. 這裡我還是再次推薦大家用Source Insight 來看程式碼,不管是函式跳轉還是全域性搜尋都是非常方便的,詳情請看我之前寫的如何閱讀Android原始碼
4.1.2 setpgid
定義在platform/libcore/luni/src/main/java/android/system/Os.java
這個Os.java類是比較特殊的一個類,這個類相當於一個代理類,所有的方法都是去呼叫Libcore.os類中相關的方法,
/**
* See <a href="http://man7.org/linux/man-pages/man2/setpgid.2.html">setpgid(2)</a>.
*/
/** @hide */ public static void setpgid(int pid, int pgid) throws ErrnoException { Libcore.os.setpgid(pid, pgid); }
複製程式碼
而Libcore.os的實現類是BlockGuardOs,BlockGuardOs的父類是ForwardingOs,ForwardingOs也是個代理類,裡面所有方法都是呼叫 Linux.java中的對應函式,也就是說Os.java中的函式最終呼叫的是Linux.java中的函式. 另外在BlockGuardOs類中有過載一些方法,做了一些 Policy許可權的檢查.
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 Os rawOs = new Linux();
/**
* Access to syscalls with helpful checks/guards.
*/
public static Os os = new BlockGuardOs(rawOs);
}
複製程式碼
我們再來看看Linux.java的實現是怎樣的
public final class Linux implements Os {
Linux() { }
...
public native void setpgid(int pid, int pgid) throws ErrnoException;
...
}
複製程式碼
沒錯,這裡面全是native函式,這些native的實現又在哪兒呢?老方法,找libcore_io_Linux,果然又找到了libcore_io_Linux.cpp
static JNINativeMethod gMethods[] = {
...
NATIVE_METHOD(Linux, setpgid, "(II)V"),
...
}
void register_libcore_io_Linux(JNIEnv* env) {
jniRegisterNativeMethods(env, "libcore/io/Linux", gMethods, NELEM(gMethods));
}
static void Linux_setpgid(JNIEnv* env, jobject, jint pid, int pgid) {
throwIfMinusOne(env, "setpgid", TEMP_FAILURE_RETRY(setpgid(pid, pgid)));
}
複製程式碼
註冊方式也是跟之前一樣,用jniRegisterNativeMethods,由此我們知道setpgid就是呼叫Linux的系統呼叫setgpid. 這個系統調的作用是設定程式組id,第一個引數pid是指設定哪個程式所屬的程式組,如果是0,就是當前程式所屬的程式組,第二個引數是設定的id值, 如果是0,那麼就把當前程式的pid作為程式組的id. 所以setgpid(0,0)的意思就是將zygote程式所在程式組id設定為zygote的pid
小結
作為進入Java世界的鋪墊,本篇講解了C++與Java之間的橋樑JNI,有了它,C++和Java就可以相互呼叫,本文只是講了一些皮毛的東西,要深入理解和使用JNI,請參考英文官方,中文手冊