論軟體, Dalvik 算小物,但也非一時三刻能說的完。前篇談到 Dalvik 建 gDvm ,至此算是完成初始化。可開始執行 bytecode。Dalvik 在功能劃分算是明顯, vm/Jni.c 透過 JavaVM 和 JNIEnv ,提供 user 功能介面,一方面則保全內部細節,不為外視。然而,別忘了初衷,我們欲了解 VM 的運作,至此只是摸清了外觀。而 VM 內部功能如銀河繁星,無法細數。必先擇一目標,集中分析,才不致於迷罔於程式碼間。對分析 VM 而言,我們最想知道,也最感興趣的,無非是執行 bytecode 。我們先直擊 bytecode interpreter。

Interpreter

在 gNativeInterface 裡列出了許多 CallXXXMethod() 之類的 function ,想必是通往 interpreter 的直達車。而在 main() function 裡,則是呼叫了(*evn)->CallStaticVoidMethod() 以執行 class 的 main。因此,直接朝 CallStaticVoidMethod() 下手也是合情合理。此卻發現,找不到 CallStaticVoidMethod() 在何處定義。這通常意謂著,一、CallStaticVoidMethod() 是透過巨集定義,二、由其它工具在 build 時產生。兩者都使的找不到定義 CallStaticVoidMethod() 的位置,這時可搜尋部分字串,即可發現其定義位置。

此列中,我發覺 CallXXXXMethod() 中的 XXXX 有好幾個版本。應該是有個集巨,以 XXXX 為參數,產生這些 function 。於是我挑了 StaticVoid 進行 grep,結果找不到。於是我又試了 Void 當作關 鍵字 ,終於發現 Jni.c 裡,有幾個地方呼叫了巨集,並以 Void 當作參數。於是發現了

001    CALL_STATIC(void
, Void, , , false);

應為定義 CallStaticVoidMethod() 的位置。又查

001    #define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref)               /
002     static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass clazz,     /
003         jmethodID methodID, ...)                                            /
004     {                                                                       /
005         JNI_ENTER();                                                        /
006         JValue result;                                                      /
007         va_list args;                                                       /
008         assert((ClassObject*) clazz == ((Method*)methodID)->clazz);         /
009         va_start(args, methodID);                                           /
010         dvmCallMethodV(_self, (Method*) methodID, NULL, &result, args);     /
011         va_end(args);                                                       /
012         if
(_isref)                                                         /
013             result.l = addLocalReference(result.l);                         /
014         JNI_EXIT();                                                         /
015         return
_retok;                                                      /
016     }                                                                       /
017 ....

確實定義了這些 function 。這是 C programmer 常用的手法,利用 macro 定義其它 function 。並以參數做為 function 名稱的一部分。其中 '##' 則是將字串分隔開來,在 expansion 之後,將結果組合在一起。如上例, _jname 若為 Void ,則第一行組出 CallStaticVoidMethod 名稱。

從字面上,我們能猜的出, dvmCallMethodV() 應該和執行 bytecode 關係密切。這時,在 Emacs 再按下 alt+. ,直接跳到 dvmCallMethodV() 的第一行,是在 vm/interp/Stack.c 檔案裡。看來這個 function 和 stack 有關。快速描視一次 function ,會發現幾個引人注意的字眼

  • dvmIsNativeMethod()

    • (*method->nativeFunc)(...)
    • dvmInterpret()
  • dvmPopFrame()

dvmIsNativeMethod() 分明就是判斷 method 是否為 native method (JNI),若是 native 則呼叫 method 的 nativeFunc()。若非 native 則直接呼叫 dvmInterpret(),在此眼冒火光, 「Interpret」 關 鍵字 頂上罩著一片祥雲。dvmIsNativeMethod() 暗示了這是一個狀態的測試,「IS」是一個常見的關 鍵字 ,一般我們會用 IsBusy() 、 IsClear() .... IsXXX() 命名測試物件、系統狀態的巨集、函數。其實,多看別人的 code 是一項非常重要的修練。雖然很多 programmer 並不看別人的 code ,但每個人都閉門造車,開發自已的習慣,那必定南轅北轍,如何能夠被了解。多看別人的 code ,潛移默化之下,自然能寫出更易於閱讀的程式碼。

dvmPopFrame() 暗示我們前面應該有個地方會造一個 frame。frame 是程式在呼叫函數的過程,在 stack 裡為該每次呼叫所保留的一塊空間,以儲存 function 狀態。這應該不用我多說,不知道可以去 search。search 還看不懂,表示要多讀點書。回到正題,往回一看,在function 的前面有一行

001        clazz = callPrep(self, method, obj, false);

call + Prep ,好像有準備 frame 的意涵,進去一看,在 callPrep() 頂頭的註解裡,明白寫了

 * Pushes a call frame on, advancing self->curFrame.                            

看來是猜的沒錯。但我們暫時對 frame 沒興趣,回到 dvmCallMethodV()。

於是我們直接看 dvmInterpret() 的內容,在 vm/interp/Interp.c 裡。我們注意到幾個發亮的大字

  • interpState
  • stdInterp
    • dvmMterpStd
    • dvmInterpretStd

InterpState 看起來就是準備 interpret 所需的資料,有

  • method
  • fp
  • pc
  • entryPoint

個個看起來皆是和 Interpret 執行的狀態相關。往下看,stdInterp 依據 gDvm 的內容,決定是 dvmMterpStd 或 dvmInterpretStd 。而在後面看到

001    change = (*stdInterp)(self, &interpState);

看來 stdInterp 應該就是 Interpret 的進入點。並且有兩種 Interpreter 可以選。另外還有一個 debug 的版本 dvmInterpretDbg() ,但目前對 debug 不感興趣,略過。

若查一下 dvmInterpretStd() 和 dvmInterpretStd() 的位置,會發現都在 vm/mterp/ 目錄之下。dvmInterpretStd() 使用 ctags 找不到,於是用 grep 試試,會發現

001    ./mterp/out/InterpC-portstd.c:#define INTERP_FUNC_NAME dvmInterpretStd
002 ./mterp/portable/portstd.c:#define INTERP_FUNC_NAME dvmInterpretStd

在 vm/mterp/ 下的兩個檔案,用巨集 alias 這個符號,應該是有其它地方使用 INTERP_FUNC_NAME 定義了 function 內容。

之前聽說了, Dalvik 針對不同的平台,有特別用 assembly 進行最佳化。 dvmInterpretStd 和 dvmMpretStd 兩個版本的 interpreter 應該和此有關。觀察一下 vm/mterp 目錄下,有

  • armv4
  • armv5te
  • x86
  • ...

等平台有關的字眼,看是所言不假。

在 trace code 時,其實用到了許多直覺。直覺有高低之分,差異來自於平常知識累積。只要 trace 時,保靈活的聯想力,特殊字眼自然會和腦中知識聯結,進而發生直覺。這些知識有時不是來自於書本,道聽途說也可能變成有用的知識。像是前面提到針對不同 CPU 進行 optimize 的傳言。

Dalvik分析(二)相关推荐

  1. 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 二 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  2. Android4.0图库Gallery2代码分析(二) 数据管理和数据加载

    Android4.0图库Gallery2代码分析(二) 数据管理和数据加载 2012-09-07 11:19 8152人阅读 评论(12) 收藏 举报 代码分析android相册优化工作 Androi ...

  3. 一些有用的javascript实例分析(二)

    一些有用的javascript实例分析(二) 原文:一些有用的javascript实例分析(二) 1 5 求出数组中所有数字的和 2 window.onload = function () 3 { 4 ...

  4. Android 系统(41)---Android7.0 PowerManagerService亮灭屏分析(二)

    Android7.0 PowerManagerService亮灭屏分析(二) 3029 在PowerManagerService中对各种状态进行判断后,将其数值封装进DisplayPowerReque ...

  5. SpringBoot源码分析(二)之自动装配demo

    SpringBoot源码分析(二)之自动装配demo 文章目录 SpringBoot源码分析(二)之自动装配demo 前言 一.创建RedissonTemplate的Maven服务 二.创建测试服务 ...

  6. Linux MMC子系统分析(二)——Host分析

    Linux MMC子系统分析(二)--Host分析 前言 通过前面对mmc子系统的模型分析,我们能够知道host是对应于硬件控制器的具体操作.本文将以sdhci-s3c.c为例对host进行简单的分析 ...

  7. gSOAP 源码分析(二)

    gSOAP 源码分析(二) 2012-5-24 flyfish 一 gSOAP XML介绍 Xml的全称是EXtensible Markup Language.可扩展标记语言.仅仅是一个纯文本.适合用 ...

  8. Android Q 10.1 KeyMaster源码分析(二) - 各家方案的实现

    写在之前 这两篇文章是我2021年3月初看KeyMaster的笔记,本来打算等分析完KeyMaster和KeyStore以后再一起做成一系列贴出来,后来KeyStore的分析中断了,这一系列的文章就变 ...

  9. 威海二职工业机器人专业_工业机器人专业介绍和前景分析二

    原标题:工业机器人专业介绍和前景分析二 人才需求最旺最热门专业-工业机器人技术专业 工业机器人技术专业是经教育部批准成立的热点技术专业,.专业以"国家高职高专精品专业.国家示范高职的重点建设 ...

最新文章

  1. 使用Fresco加载图片
  2. hihocoder 1061.Beautiful String
  3. Roller5.0.3安装配置部署 step by step
  4. js 数组移除_2020前端面试--常见的js面试题
  5. Mybatis Integer类型参数值为0时判断为空、空字符串不通过
  6. spring boot 整合 谷歌guava的EventBus 实现单机版的消息发布订阅
  7. creator 静态属性_CocosCreator cc.class声明类
  8. C#LeetCode刷题之#892-三维形体的表面积(Surface Area of 3D Shapes)
  9. C++复习总结(涵盖所有C++基本考点)!
  10. 【WPF】WPF DataGrid List数据源 双向绑定通知机制之ObservableCollection使用以及MultiBinding 的应用...
  11. Springboot+idea的一个bug(Unregistering JMX-exposed beans on shutdown)
  12. 原理图框图_GW1N9芯片测评之GW1N Zero原理图、3D模型及PCB设计(二)
  13. 15-07-06 定闹钟
  14. pytorch项目代码总结
  15. python中关于命名的例子_Python()-类命名空间和对象/实例命名空间
  16. MySQL DELETE语句和TRUNCATE TABLE语句的区别
  17. android最新adt下载地址,Android SDK和最新ADT下载地址
  18. 为计算机技术奉献一生语录,关于奉献精神的名言50句
  19. python爬虫实战——青果教务网系统,并用xpath提取成绩
  20. 在格式化字符串的边缘试探

热门文章

  1. mysql六:索引原理与慢查询优化
  2. PHP 依赖注入,从此不再考虑加载顺序
  3. 数学图形(1.42)拱形曲线
  4. iPhone客户端开发笔记(八)
  5. [转] 图解Seq2Seq模型、RNN结构、Encoder-Decoder模型 到 Attention
  6. vue.js 三种方式安装--npm安装
  7. ElementUI el-table 在flex下的宽度自适应问题
  8. 处理大并发的30条数据库规范
  9. android制作闪动的红心
  10. leetcode 之Single Number(13)