Android 開發者須要知道的 Linux 知識

去年年末學習了張紹文的《Android開發高手課》,課程真的很是不錯,也學到了很多高級知識,好的東西天然是強烈推薦給你們。在《崩潰優化(下):應用崩潰了,你應該如何去分析?》一文中有這麼幾段話:java

  • 虛擬內存。虛擬內存能夠經過 /proc/self/status 獲得,經過 /proc/self/maps 文件能夠獲得具體的分佈狀況。有時候咱們通常不過重視虛擬內存,可是不少相似 OOM、tgkill 等問題都是虛擬內存不足致使的。linux

  • 文件句柄 fd。文件句柄的限制能夠經過 /proc/self/limits 得到,通常單個進程容許打開的最大文件句柄個數爲 1024。可是若是文件句柄超過 800 個就比較危險,須要將全部的 fd 以及對應的文件名輸出到日誌中,進一步排查是否出現了有文件或者線程的泄漏。android

什麼是虛擬內存,什麼又是物理內存?文件句柄 fd 是什麼?咱們只知道文件打開了必定要關閉,那麼不關閉會怎樣呢?文件未關閉對咱們應用的性能又有多大的影響?等等相似於這樣一系列的問題,不知咱們是否真的知曉呢?git

其實上面所提到的都是 linux 內核方面的知識,咱們日常寫代碼都是用 java 一把梭哈,可能以爲這些知識可有可無,有些不瞭解也是正常。咱們應該都知道 android 是基於 linux 內核的,所以去了解和學習這一塊的知識徹底是有必要的。再好比騰訊開源的 MMKV 用來代替 Android 的 SharedPreferences 咱們是否可以看得懂其具體的實現呢?MMKV.cpp 中用到了 mmap() 函數,咱們又是否瞭解其具體原理和實現?github

開頭說了這麼多,無非就是想表達一下,做爲一個合格的 Android 開發者,是有必要了解一些 linux 內核知識的。但咱們畢竟不是從事 linux 內核和 Android Framework 相關的工做,所以做爲一個 Android 應用開發者,只須要知道那些該知道的就差很少了。那哪些是該知道的呢?就是那些有利於開發和有利於性能優化的知識。推薦你們看兩本書《深刻理解LINUX內核》與《Linux內核源代碼情景分析》。若是咱們大學學過計算機組成原理和計算機操做系統就更好了,要是沒學過你們又感興趣,能夠去聽聽國內外的一些公開課,我本身就曾聽過清華大學的計算機操做系統公開課。面試

接下來想要跟你們分享的是,編譯流程,動靜態庫,物理內存,虛擬內存。如下內容都是筆者概括總結而得,也是看了一些資料和書籍,同時爲了便於你們理解,不免會有些通俗,也可能會有些紕漏,歡迎你們指出批評。性能優化

1. gcc 編譯四步驟

去年面試抖音 Android 開發崗位,被問到了你知道宏定義和 static 之間的區別嗎?內聯函數和普通函數有什麼區別?你知道 Java 的內聯嗎?其實不少面試碰到的問題,以及技術上的難點,或者說遇到的 Bug 。 本質上是沒啥區別的,當咱們瞭解了最基本的原理,一切即可以迎刃而解。下面是我寫的一段簡單的代碼:bash

#include <stdio.h>
#define TAG "Darren"
static char* tag="Jack";

int add(int a, int b){
  return a+b;
}

int main(){
  printf("Hello World!\n");
  printf("%s\n",TAG);
  printf("%s\n",tag);
  int sum = add(1, 2);
  return 0;
}
複製代碼

1.1 預處理階段函數

執行命令是 gcc -E -o hello.i hello.c ,該階段主要是將宏定義展開,所有的 #define 在這個階段都會被展開,包含 #if #ifdef 一類的命令展開 #include 的文件,像上面 hello world 中的 stdio.h , 把 stdio.h 中的所有代碼合併到 hello.c 中。具體查看文件是這樣的:工具

// 省略了一部分 ......
extern char *ctermid (char *__s) __attribute__ ((__nothrow__ , __leaf__));
# 912 "/usr/include/stdio.h" 3 4
extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));



extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;


extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# 942 "/usr/include/stdio.h" 3 4

# 2 "hello.c" 2



# 4 "hello.c"
static char* tag="Jack";

int add(int a, int b){
 return a+b;
}

int main(){
 printf("Hello World!\n");
 printf("%s\n","Darren");
 printf("%s\n",tag);
 int sum = add(1, 2);
 return 0;
}
複製代碼

1.2 預編譯階段

執行命令是在 gcc -S -o hello.s hello.i 這個階段中,gcc 首先檢查代碼的規範性、是否有語法錯誤等,以肯定代碼的實際要作的工做,在檢查無誤後,gcc把代碼翻譯成彙編語言。

// 省略了一部分 ......
main:
.LFB1:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$16, %rsp
	movl	$.LC1, %edi
	call	puts
	movl	$.LC2, %edi
	call	puts
	movq	tag(%rip), %rax
	movq	%rax, %rdi
	call	puts
	movl	$2, %esi
	movl	$1, %edi
	call	add
	movl	%eax, -4(%rbp)
	movl	$0, %eax
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
// 省略了一部分 ......
複製代碼

1.3 彙編階段

命令是 gcc -c -o hello.o hello.s ,彙編階段把 hello.s 文件翻譯成二進制機器指令文件 hello.o , 文本打開亂碼,須要藉助工具才能打開。

// 省略了一部分 ......
0000000000000014 <main>:
  14:	55                   	push   %rbp
  15:	48 89 e5             	mov    %rsp,%rbp
  18:	48 83 ec 10          	sub    $0x10,%rsp
  1c:	bf 00 00 00 00       	mov    $0x0,%edi
  21:	e8 00 00 00 00       	callq  26 <main+0x12>
  26:	bf 00 00 00 00       	mov    $0x0,%edi
  2b:	e8 00 00 00 00       	callq  30 <main+0x1c>
  30:	48 8b 05 00 00 00 00 	mov    0x0(%rip),%rax        # 37 <main+0x23>
  37:	48 89 c7             	mov    %rax,%rdi
  3a:	e8 00 00 00 00       	callq  3f <main+0x2b>
  3f:	be 02 00 00 00       	mov    $0x2,%esi
  44:	bf 01 00 00 00       	mov    $0x1,%edi
  49:	e8 00 00 00 00       	callq  4e <main+0x3a>
  4e:	89 45 fc             	mov    %eax,-0x4(%rbp)
  51:	b8 00 00 00 00       	mov    $0x0,%eax
  56:	c9                   	leaveq 
  57:	c3                   	retq 
複製代碼

1.4 連接階段

命令是 gcc -c -o hello.o hello.s ,該階段主要作兩件事情,利用 objdump 咱們會看到以下代碼,該階段還會作一件事情就是合併數據段。

callq  400420 <__libc_start_main@plt+0x10>

callq  400526 <add>

callq  400420 <__libc_stdio_printf@plt+0x32>
複製代碼

2. 動態庫與靜態庫

連接時咱們能夠連接靜態庫和動態庫,在 android 開發中咱們通常用 .so 動態庫比較多,在下載 opencv 人臉識別 sdk 時,咱們也能看到以下的一些 .a 靜態庫:

靜態庫.a

靜態庫是編譯時拷貝了調用代碼,也就是說編譯連接後若是靜態庫不存在或者刪除了,運行也是不會報錯的。動態庫是運行時動態加載的,若是動態庫不存在了,運行時就會出錯,並且在生成 .o 文件時須要生成與位置無關的代碼。

3. 虛擬內存

虛擬內存是計算機系統內存管理的一種技術。它使得應用程序認爲它擁有連續的可用的內存一個連續完整的地址空間,而實際上,它一般是被分隔成多個物理內存碎片,還有部分暫時存儲在外部磁盤存儲器上,在須要時進行數據交換。目前,大多數操做系統都使用了虛擬內存,如 Windows 家族的「虛擬內存」;Linux 的「交換空間」等。

大體的虛擬內存佈局圖

最後給你們分享一下《Android開發高手課》,爲啥在課程完結了才分享給你們呢?首先筆者已經學習過了,真的很是不錯纔敢推薦給你們。最後仍是再囉囉嗦嗦一下,不少東西你們不要眼高手低,必定要動手實踐。好比在啓動優化那一章,你們能夠跟着一塊兒去優化本身項目的啓動流程,不要只是聽一遍,而是要用到本身的項目中。還有不少內容咱們也不必定能聽懂,只要是不影響學習進度你們能夠先過一遍,好的學習資料都是第一遍能夠學到不少東西第二遍有進步第三遍還能夠有提高。分享採起的是音頻加文章的方式,雖然紹文已經給你們總結了文章,但咱們仍然須要本身去作筆記總結和概括,讓其能真正變成咱們本身的知識。

Android開發高手課

視頻連接:pan.baidu.com/s/14ctKrevi…  視頻密碼:h5mj

相關文章
相關標籤/搜索