[TOC]
# 1MB-8KB的限制來源于哪里
[frameworks](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2F)/[native](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2Fnative%2F)/[libs](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2Fnative%2Flibs%2F)/[binder](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2Fnative%2Flibs%2Fbinder%2F)/[ProcessState.cpp](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2Fnative%2Flibs%2Fbinder%2FProcessState.cpp)
~~~cpp
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)//這里的限制是1MB-4KB*2
ProcessState::ProcessState(const char *driver)
{
if (mDriverFD >= 0) {
// mmap the binder, providing a chunk of virtual address space to receive transactions.
// 調用mmap接口向Binder驅動中申請內核空間的內存
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
if (mVMStart == MAP_FAILED) {
// *sigh*
ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
close(mDriverFD);
mDriverFD = -1;
mDriverName.clear();
}
}
}
~~~
如果一個進程使用ProcessState這個類來初始化Binder服務,這個進程的Binder內核內存上限就是BINDER\_VM\_SIZE,也就是1MB-8KB。
[frameworks](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2F)/[base](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2Fbase%2F)/[cmds](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2Fbase%2Fcmds%2F)/[app\_process](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2Fbase%2Fcmds%2Fapp_process%2F)/[app\_main.cpp](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2Fbase%2Fcmds%2Fapp_process%2Fapp_main.cpp)
~~~rust
virtual void onZygoteInit()
{
sp<ProcessState> proc = ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}
~~~
對于普通的APP來說,我們都是Zygote進程孵化出來的,Zygote進程的初始化Binder服務的時候提前調用了ProcessState這個類,所以普通的APP進程上限就是1MB-8KB。
# 問一下自己,能否不用ProcessState來初始化Binder服務,來突破1M-8KB的限制?
答案是當然可以了,Binder服務的初始化有兩步,open打開Binder驅動,mmap在Binder驅動中申請內核空間內存,所以我們只要手寫open,mmap就可以輕松突破這個限制。源碼中已經給了類似的例子。
[frameworks](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2F)/[native](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2Fnative%2F)/[cmds](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2Fnative%2Fcmds%2F)/[servicemanager](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2Fnative%2Fcmds%2Fservicemanager%2F)/[bctest.c](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2Fnative%2Fcmds%2Fservicemanager%2Fbctest.c)
~~~cpp
int main(int argc, char **argv)
{
struct binder_state *bs;
uint32_t svcmgr = BINDER_SERVICE_MANAGER;
uint32_t handle;
bs = binder_open("/dev/binder", 128*1024);//我們可以把這個數值改成2*1024*1024就可以突破這個限制了
if (!bs) {
fprintf(stderr, "failed to open binder driver\n");
return -1;
}
~~~
[frameworks](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2F)/[native](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2Fnative%2F)/[cmds](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2Fnative%2Fcmds%2F)/[servicemanager](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2Fnative%2Fcmds%2Fservicemanager%2F)/[binder.c](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2F9.0.0_r3%2Fxref%2Fframeworks%2Fnative%2Fcmds%2Fservicemanager%2Fbinder.c)
~~~rust
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
...//省略部分代碼
bs->fd = open(driver, O_RDWR | O_CLOEXEC);
....//省略部分代碼
bs->mapsize = mapsize;//這里mapsize=128*1024
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
....//省略部分代碼
}
~~~
# 難道Binder驅動不怕我們傳遞一個超級大的數字進去嗎?
其實是我們想多了,在Binder驅動中mmap的具體實現中還有一個4M的限制
/[drivers](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2Fkernel_3.18%2Fxref%2Fdrivers%2F)/[staging](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2Fkernel_3.18%2Fxref%2Fdrivers%2Fstaging%2F)/[android](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2Fkernel_3.18%2Fxref%2Fdrivers%2Fstaging%2Fandroid%2F)/[binder.c](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2Fkernel_3.18%2Fxref%2Fdrivers%2Fstaging%2Fandroid%2Fbinder.c)
~~~rust
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
struct vm_struct *area;
struct binder_proc *proc = filp->private_data;
const char *failure_string;
struct binder_buffer *buffer;
if (proc->tsk != current)
return -EINVAL;
if ((vma->vm_end - vma->vm_start) > SZ_4M)
vma->vm_end = vma->vm_start + SZ_4M;//如果申請的size大于4MB了,會在驅動中被修改成4MB
binder_debug(BINDER_DEBUG_OPEN_CLOSE,
"binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n",
proc->pid, vma->vm_start, vma->vm_end,
(vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
(unsigned long)pgprot_val(vma->vm_page_prot));
~~~
# 目前的結論
### 1.通過手寫open,mmap初始化Binder服務的限制是4MB
### 2.通過ProcessState初始化Binder服務的限制是1MB-8KB
# 再問一下自己,4M或1MB-8KB這個答案是不是正確?
我發現一處代碼
/[drivers](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2Fkernel_3.18%2Fxref%2Fdrivers%2F)/[staging](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2Fkernel_3.18%2Fxref%2Fdrivers%2Fstaging%2F)/[android](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2Fkernel_3.18%2Fxref%2Fdrivers%2Fstaging%2Fandroid%2F)/[binder.c](https://links.jianshu.com/go?to=http%3A%2F%2Fandroidxref.com%2Fkernel_3.18%2Fxref%2Fdrivers%2Fstaging%2Fandroid%2Fbinder.c)
~~~rust
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
//省內部分代碼
proc->free_async_space = proc->buffer_size / 2;//這個代碼引起我注意,async代碼異步的意思
barrier();
proc->files = get_files_struct(current);
proc->vma = vma;
proc->vma_vm_mm = vma->vm_mm;
~~~
~~~rust
static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc,
size_t data_size,
size_t offsets_size, int is_async)
{
//省略部分代碼
if (is_async &&
proc->free_async_space < size + sizeof(struct binder_buffer)) {
//對于oneway的Binder調用,可申請內核空間,最大上限是buffer_size的一半,也就是mmap時候傳遞的值的一半。
binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
"%d: binder_alloc_buf size %zd failed, no async space left\n",
proc->pid, size);
return NULL;
}
~~~
為什么要做這樣子的限制,我的猜想是Binder調用中同步調用優先級大于oneway(異步)的調用,為了充分滿足同步調用的內存需要,所以將oneway調用的內存限制到申請內存上限的一半。
# 問題:一次Binder通信最大可以傳輸多大的數據?

屏幕快照 2019-05-24 下午3.11.51.png
# 再問一下自己,自己寫的APP能否突破1M-8KB的限制
答案是理論上可以,但是不建議這樣子操作,因為Binder驅動中并沒有對open,mmap有調用次數的限制,App可以通過JNI調用open,mmap來突破這個限制,但是會對當前正在進行Binder調用的APP造成不可想象問題,當然可以先close Binder驅動。但是一旦這個APP沒有Binder通信了,這個APP就不能正常使用了,APP和其他應用,AMS,WMS的交互可都是依賴于Binder通信,所以還是那句話,無Binder無Android。
# 傳遞大量數據
## 共享內存
共享內存是進程間通信的一種方式,通過映射一塊公共內存到各自的進程空間來達到共享內存的目的。

](images/screenshot_1643253910596.png)
對于進程間需要傳遞大量數據的場景下,這種通信方式是十分高效的,但是共享內存并未提供同步機制,也就是說,在第一個進程結束對共享內存的寫操作之前,并無自動機制可以阻止第二個進程開始對它進行讀取,所以我們通常需要用其他的機制來同步對共享內存的訪問,例如信號量。
`Android`中的匿名共享內存(Ashmem)是基于`Linux`共享內存的,借助`Binder`+文件描述符(`FileDescriptor`)實現了共享內存的傳遞。它可以讓多個進程操作同一塊內存區域,并且除了物理內存限制,沒有其他大小限制。相對于`Linux`的共享內存,Ashmem對內存的管理更加精細化,并且添加了互斥鎖。`Java`層在使用時需要用到`MemoryFile`,它封裝了`native`代碼。`Android`平臺上共享內存通常的做法如下:
* 進程A通過`MemoryFile`創建共享內存,得到fd(`FileDescriptor`)
* 進程A通過fd將數據寫入共享內存
* 進程A將fd封裝成實現`Parcelable`接口的`ParcelFileDescriptor`對象,通過`Binder`將`ParcelFileDescriptor`對象發送給進程B
* 進程B獲從`ParcelFileDescriptor`對象中獲取fd,從fd中讀取數據
# 參考資料
[一次Binder通信最大可以傳輸多大的數據?](https://www.jianshu.com/p/ea4fc6aefaa8)
[一道面試題:使用AIDL實現跨進程傳輸一個2M大小的文件](https://juejin.cn/post/6990379493235884062)
- Android
- 四大組件
- Activity
- Fragment
- Service
- 序列化
- Handler
- Hander介紹
- MessageQueue詳細
- 啟動流程
- 系統啟動流程
- 應用啟動流程
- Activity啟動流程
- View
- view繪制
- view事件傳遞
- choreographer
- LayoutInflater
- UI渲染概念
- Binder
- Binder原理
- Binder最大數據
- Binder小結
- Android組件
- ListView原理
- RecyclerView原理
- SharePreferences
- AsyncTask
- Sqlite
- SQLCipher加密
- 遷移與修復
- Sqlite內核
- Sqlite優化v2
- sqlite索引
- sqlite之wal
- sqlite之鎖機制
- 網絡
- 基礎
- TCP
- HTTP
- HTTP1.1
- HTTP2.0
- HTTPS
- HTTP3.0
- HTTP進化圖
- HTTP小結
- 實踐
- 網絡優化
- Json
- ProtoBuffer
- 斷點續傳
- 性能
- 卡頓
- 卡頓監控
- ANR
- ANR監控
- 內存
- 內存問題與優化
- 圖片內存優化
- 線下內存監控
- 線上內存監控
- 啟動優化
- 死鎖監控
- 崩潰監控
- 包體積優化
- UI渲染優化
- UI常規優化
- I/O監控
- 電量監控
- 第三方框架
- 網絡框架
- Volley
- Okhttp
- 網絡框架n問
- OkHttp原理N問
- 設計模式
- EventBus
- Rxjava
- 圖片
- ImageWoker
- Gilde的優化
- APT
- 依賴注入
- APT
- ARouter
- ButterKnife
- MMKV
- Jetpack
- 協程
- MVI
- Startup
- DataBinder
- 黑科技
- hook
- 運行期Java-hook技術
- 編譯期hook
- ASM
- Transform增量編譯
- 運行期Native-hook技術
- 熱修復
- 插件化
- AAB
- Shadow
- 虛擬機
- 其他
- UI自動化
- JavaParser
- Android Line
- 編譯
- 疑難雜癥
- Android11滑動異常
- 方案
- 工業化
- 模塊化
- 隱私合規
- 動態化
- 項目管理
- 業務啟動優化
- 業務架構設計
- 性能優化case
- 性能優化-排查思路
- 性能優化-現有方案
- 登錄
- 搜索
- C++
- NDK入門
- 跨平臺
- H5
- Flutter
- Flutter 性能優化
- 數據跨平臺