1. 程式人生 > >Android 匿名共享記憶體Java介面分析

Android 匿名共享記憶體Java介面分析

public MemoryFile(String name, int length) throws IOException {
	mLength = length;
	//開啟"/dev/ashmem"裝置檔案
	mFD = native_open(name, length);
	if (length > 0) {
		//將開啟的"/dev/ashmem"裝置檔案對映到程序虛擬地址空間中
		mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
	} else {
		mAddress = 0;
	}
}
native_open函式是一個本地函式,通過JNI實現在C++層,程式碼位於frameworks\base\core\jni\android_os_MemoryFile.cpp 
static jobject android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length)
{
	//字串轉換
    const char* namestr = (name ? env->GetStringUTFChars(name, NULL) : NULL);
    //開啟裝置檔案"/dev/ashmem",並修改裝置檔名稱及共享記憶體大小
    int result = ashmem_create_region(namestr, length);

    if (name)
        env->ReleaseStringUTFChars(name, namestr);

    if (result < 0) {
        jniThrowException(env, "java/io/IOException", "ashmem_create_region failed");
        return NULL;
    }
	//裝置檔案控制代碼轉換
    return jniCreateFileDescriptor(env, result);
}

函式首先將Java層傳過來的你們共享記憶體名稱轉換為C++層的字串,然後呼叫ashmem_create_region函式建立一個名為dev/ashmem/的匿名共享記憶體,並且修改該共享記憶體的名稱及大小,然後將建立的匿名共享記憶體裝置檔案控制代碼值返回到Java空間中。函式ashmem_create_region在Android 匿名共享記憶體C介面分析中有詳細分析,該介面函式就是用於建立一塊匿名共享記憶體。

在Java空間構造MemoryFile物件時,首先開啟/dev/ashmem裝置檔案並在核心空間建立一個ashmem_area,接著需要將核心空間分配的共享記憶體地址對映到程序虛擬地址空間中來,對映過程是通過native_mmap函式來完成的。
static jint android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jobject fileDescriptor,
        jint length, jint prot)
{
    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
    jint result = (jint)mmap(NULL, length, prot, MAP_SHARED, fd, 0);
    if (!result)
        jniThrowException(env, "java/io/IOException", "mmap failed");
    return result;
}
該函式直接呼叫mmap來實現地址空間對映,注意標誌位MAP_SHARED,表示該緩衝區以共享方式對映。對映過程是由Ashmem驅動來完成,Android 匿名共享記憶體驅動原始碼分析詳細分析了Android匿名共享記憶體的實現過程。在構造MemoryFile物件時完成了匿名共享記憶體的建立及地址空間的對映過程,將建立的匿名共享記憶體的大小儲存到MemoryFile的成員變數mLength中,成員變數mFD儲存建立的匿名共享記憶體的檔案描述符,成員變數mAddress儲存匿名共享記憶體對映到程序地址空間的起始地址。有了這些資訊後,就可以直接使用該匿名共享記憶體了。

匿名共享記憶體讀

對匿名共享記憶體的讀取操作,在Java空間被封裝成MemoryInputStream來完成,該類繼承於輸入流InputStream,並對外提供了read方法,定義如下:
@Override
public int read() throws IOException {
	if (mSingleByte == null) {
		mSingleByte = new byte[1];
	}
	int result = read(mSingleByte, 0, 1);
	if (result != 1) {
		return -1;
	}
	return mSingleByte[0];
}

@Override
public int read(byte buffer[], int offset, int count) throws IOException {
	if (offset < 0 || count < 0 || offset + count > buffer.length) {
		// readBytes() also does this check, but we need to do it before
		// changing count.
		throw new IndexOutOfBoundsException();
	}
	count = Math.min(count, available());
	if (count < 1) {
		return -1;
	}
	int result = readBytes(buffer, mOffset, offset, count);
	if (result > 0) {
		mOffset += result;
	}
	return result;
}
MemoryInputStream類提供了兩個read過載方法,第一個無參read方法呼叫有參read方法來讀取1位元組的資料,而有參read方法的資料讀取過程是呼叫MemoryInputStream的外部類MemoryFile的readBytes方法來實現匿名共享記憶體資料的讀取過程。
public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count)
		throws IOException {
	if (isDeactivated()) {
		throw new IOException("Can't read from deactivated memory file.");
	}
	if (destOffset < 0 || destOffset > buffer.length || count < 0
			|| count > buffer.length - destOffset
			|| srcOffset < 0 || srcOffset > mLength
			|| count > mLength - srcOffset) {
		throw new IndexOutOfBoundsException();
	}
	return native_read(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
}
該函式也僅僅作了一些判斷,然後直接呼叫本地方法native_read在C++空間完成資料讀取,在構造MemoryFile物件時,已經開啟並映射了dev/ashmem裝置檔案,因此在這裡直接將開啟該裝置檔案得到的檔案控制代碼值傳到C++空間,以正確讀取指定的匿名共享記憶體中的內容,mAddress為匿名共享記憶體對映到程序地址空間中的起始地址。
static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz,
        jobject fileDescriptor, jint address, jbyteArray buffer, jint srcOffset, jint destOffset,
        jint count, jboolean unpinned)
{
    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
    if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
        ashmem_unpin_region(fd, 0, 0);
        jniThrowException(env, "java/io/IOException", "ashmem region was purged");
        return -1;
    }

    env->SetByteArrayRegion(buffer, destOffset, count, (const jbyte *)address + srcOffset);

    if (unpinned) {
        ashmem_unpin_region(fd, 0, 0);
    }
    return count;
}


匿名共享記憶體寫

將指定資料寫入到匿名共享記憶體中,對匿名共享記憶體的寫操作使用MemoryOutputStream來封裝,該類提供了兩個過載的write方法,一個用於向匿名共享記憶體寫入多位元組資料,另一個則只寫入一個位元組資料。這裡簡單介紹多位元組資料寫入過程:
public void write(byte buffer[], int offset, int count) throws IOException {
	writeBytes(buffer, offset, mOffset, count);
	mOffset += count;
}

引數buffer是指寫入匿名共享記憶體中的位元組陣列,offset指定資料buffer開始寫的偏移量,引數count指定寫入匿名共享記憶體的位元組長度,函式呼叫MemoryFile的writeBytes函式來完成資料寫入。

public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)
		throws IOException {
	if (isDeactivated()) {
		throw new IOException("Can't write to deactivated memory file.");
	}
	if (srcOffset < 0 || srcOffset > buffer.length || count < 0
			|| count > buffer.length - srcOffset
			|| destOffset < 0 || destOffset > mLength
			|| count > mLength - destOffset) {
		throw new IndexOutOfBoundsException();
	}
	native_write(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
}
該函式首先檢驗引數的正確性,然後呼叫native方法native_write通過JNI轉入C++完成資料寫入,第一個引數是匿名共享記憶體的檔案描述符,第二個引數是匿名共享記憶體對映到程序地址空間的基地值,後面三個引數上面已經介紹了,最後一個引數mAllowPurging表示是否允許記憶體回收

上圖描述了匿名共享記憶體的寫入過程,本質上就是將buffer中指定位置開始的資料拷貝到匿名共享記憶體指定的偏移位置

frameworks\base\core\jni\android_os_MemoryFile.cpp

static jint android_os_MemoryFile_write(JNIEnv* env, jobject clazz,
        jobject fileDescriptor, jint address, jbyteArray buffer, jint srcOffset, jint destOffset,
        jint count, jboolean unpinned)
{
    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
    if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
        ashmem_unpin_region(fd, 0, 0);
        jniThrowException(env, "java/io/IOException", "ashmem region was purged");
        return -1;
    }
    env->GetByteArrayRegion(buffer, srcOffset, count, (jbyte *)address + destOffset);
    if (unpinned) {
        ashmem_unpin_region(fd, 0, 0);
    }
    return count;
}
資料寫入過程是通過JNI函式GetByteArrayRegion完成資料的拷貝操作。

相關推薦

Android 匿名共享記憶體Java介面分析

public MemoryFile(String name, int length) throws IOException { mLength = length; //開啟"/dev/ashmem"裝置檔案 mFD = native_open(name, length); if (length &g

Android Binder 分析——匿名共享記憶體(好文)

前面分析了 binder 中用來打包、傳遞資料的 Parcel,一般用來傳遞 IPC 中的小型引數和返回值。binder 目前每個程序 mmap 接收資料的記憶體是 1M,所以就算你不考慮效率問題用 Parcel 來傳,也無法傳過去。只要超過 1M 就會報錯(binder 無法分配接收空間)。所以 a

Android系統匿名共享記憶體Ashmem(Anonymous Shared Memory)簡要介紹和學習計劃

                        在Android系統中,提供了獨特的匿名共享記憶體子系統Ashmem(Anonymous Shared Memory),它以驅動程式的形式實現在核心空間中。它有兩個特點,一是能夠輔助記憶體管理系統來有效地管理不再使用的記憶體塊,二是它通過Binder程序間通訊機

android TV 系統記憶體使用情況分析(系統預設分配記憶體小)

系統記憶體申請分為預設記憶體和最大限度使用記憶體,一般沒有在AndroidMenifest中設定LargeHeap為true的話,使用的是預設記憶體,有些記憶體分配的很小比如48M,設定了之後就可以使用分配的最大記憶體空間了。下面是可以檢測記憶體使用量的計算方式。 // l

[Java][Android][Process] Process 創建+控制+分析 經驗淺談

send 實現 亦或 想要 unable github stringbu data- 滿了 不管是Android亦或者Java中或多或少須要調用底層的一些命令。運行一些參數; 此時我們須要用到Java的Process來創建一個子進程。之所以是子進程是由於此進程依賴於發起

Android測試,Selenium3自動化測試,Python3 測試,Java介面測試

谷歌的Android生態系統正在不斷地迅速擴張。有證據表明,新的移動OEM正在攻陷世界的每一個角落,不同的螢幕尺寸、ROM /韌體、晶片組以及等等等等,層出不窮。於是乎,對於Android開發人員而言,處理儲存碎片變得越來越困窘。 不過幸運的是,Android(還有iOS)開發人員可以無限制地訪問一些先進的

Linux:程序間通訊(匿名管道命名管道)(共享記憶體,訊息佇列,訊號量)

目錄 程序間通訊的介紹 管道 匿名管道 原理: 程式碼實現 匿名管道特性 實現管道符 |  命名管道 命名管道特性 程式碼實現 管道讀寫規則 作業系統中ipc的相關命令 共享記憶體(重點) 生命週期: 程式碼實現 程式碼實現獲

Java原始碼分析——Class類、ClassLoader類解析(二) 類的識別、Modifier類、TypeVariable、GenericDeclaration介面

    在類的載入與例項化的時候,如何識別類、介面、註解以及陣列是個值得思考的問題,不僅是這些常用的引用類,還包括類、介面等的public、private、defalut、static等修飾符,以及識別一個泛型類或者介面。   

Android記憶體洩漏問題分析及解決方案

大家新年好,由於工作繁忙原因,有好一段時間沒有更新博文了(當然Github是一直都有更新的),趁著年底有點放假時間,我覺得抽空更新下部落格,總結一下工作中最常見記憶體洩漏問題,也是自己之前踩過的坑,為了讓大家少走彎路,系統全面總結一下記憶體洩漏問題分析原因及尋找解決方案。 概念 首

Java原始碼分析——java.util工具包解析(五)——UUID、Base64、內建觀察者模式Observer介面、EventListener、RandomAccess

UUID     關於UUID,我們需要知道它最重要的一點,就是它會生成全地球唯一的一個id,它可以作為資料庫的主鍵存在,標識各個元組。 UUID保證對在同一時空中的所有機器都是唯一的,利用機器的當前日期和時間、時鐘序列、全域性唯一的IEEE機

Java服務記憶體OOM原因分析

1、出現問題的可能原因 對於應用來說記憶體分配太少 物件建立太多,又沒有釋放,造成記憶體洩漏嚴重,導致記憶體耗盡 申請太多的系統資源,系統資源耗盡。例如:不斷建立執行緒,不斷髮起網路連線 2、如何定位問題(可直接對dump檔案分析)      

Java動態編譯優化——ZipFileIndex記憶體洩漏問題分析解決

一、前言: 前幾天解決了URLClassLoader記憶體洩漏的問題,但是解決問題就像剝洋蔥,剝去了外層,內層 問題又暴露出來了。當URLClassLoader記憶體洩漏解決, 需要解決的就是ZipFileIndex記憶體洩漏的問題了,而且這個問題折騰了我2天半的時間。 URLClass

Android中使用Handler造成記憶體洩露的分析和解決

Java使用有向圖機制,通過GC自動檢查記憶體中的物件(什麼時候檢查由虛擬機器決定),如果GC發現一個或一組物件為不可到達狀態,則將該物件從記憶體中回收。也就是說,一個物件不被任何引用所指向,則該物件會在被GC發現的時候被回收;另外,如果一組物件中只包含互相的引用,而沒有來自它們外部的引用(例如有兩個物件A和

java記憶體洩漏的分析方法

這幾天,一直在為Java的“記憶體洩露”問題糾結。Java應用程式佔用的記憶體在不斷的、有規律的上漲,最終超過了監控閾值。福爾摩 斯不得不出手了!   記憶體溢位 out of memory,是指程式在申請記憶體時,沒有足夠的記憶體空間供其使用,出現out of memory;比如

Android呼叫C++實現共享記憶體(Native層)

MemoryFile是java層封裝的介面,它實現共享記憶體主要呼叫瞭如下函式: int fd = open("/dev/ashmem",O_RDWR); ioctl(fd, ASHMEM_SET_NAME,name); ioctl(fd,ASHMEM_SET

Android應用程式元件Content Provider在應用程式之間共享資料的原理分析

                        在Android系統中,不同的應用程式是不能直接讀寫對方的資料檔案的,如果它們想共享資料的話,只能通過Content Provider元件來實現。那麼,Content Provider元件又是如何突破應用程式邊界許可權控制來實現在不同的應用程式之間共享資料的呢?

Android程序的記憶體管理分析

尊重原創作者,轉載請註明出處: 最近在網上看了不少Android記憶體管理方面的博文,但是文章大多都是就單個方面去介紹記憶體管理,沒有能全域性把握,缺乏系統性闡述,而且有些觀點有誤。 這樣對Android記憶體管理進行區域性性介紹,很難使讀者建立系統性概念,無法真正理解記

java 介面、抽象類、具體類、內部類、匿名內部類的區別及它們之間的關係

其實java內部類也是一個類,與其他類不同的是內部類是類中類,它的作用範圍只能在這個類中。 java 匿名內部類: 匿名內部類的定義語法如下: new 父類構造器(引數列表)|實現介面() { //匿名內部類的類體部分 } 雖然看上去很簡單,但是還有一

Java設計模式之從[歡迎介面]分析模板方法(Template Method)模式

  模板方法是在抽象類中最常用的模式了(應該沒有之一),它定義一個操作中演算法的骨架,而將一些步驟延遲到子類中,使得子類可以不改變一個演算法的結構即可重新定義演算法的某些步驟。   例如我們要編寫一個歡迎介面,如果使用者是第一次開啟本軟體,則彈出一個歡迎的提示。為了能夠實現

java 介面不是不能new嗎?- java:使用匿名類直接new介面

java 介面不是不能new嗎? 這叫匿名內部類,你後面的大括號實際上就是一個Runnable的實現類了 只不過這個類名和介面名稱一樣,所以是匿名的,別人地方也用不了 這種寫法相當於先建立了一個匿名類,實現了這個介面,然後new一個這個匿名類的物件 所以可以使用. 這個是等於建