Androidでmmapの限界値を取得する

#mmapとは
http://ja.wikipedia.org/wiki/Mmap

ファイルやデバイスのリソースをメモリ領域にマッピングするシステムコールですが、
たぶん、メモリーマップドファイルとかこれ呼んでるんじゃないでしょうか。

で、これが、「どれくらいまでマッピングできるか」がやってみないとわからないというのが現状です。

そこで、Androidでどのくらいmmapできるのかを測るコードを書きました。

ただAndroidでmmapの限界値を知りたい人がどれほどいるのか・・・
そもそもほかに方法があるような気もします・・・


#Androidでのmmap限界値の取得方法

NDKを使って直接システムコールを呼び出し、どれくらいマッピングするとエラーになるか調べます

簡単な処理内容ですが、

+ ダミーファイルをSDカード（もしくはストレージ）に生成
+ ページサイズを取得
+ ダミーファイルをページサイズ*2のサイズ分ファイルポインタを移動
+ mmapして、munmapする
+ 成功したらさらに増加分を倍にしてプラスし、実行
+ 失敗したら増加分を半分にして実行
+ 増加分がページサイズ以下になったら終了

というようなアルゴリズムです。

mmapの使い方については下記のサイトを参考にしました
http://www.ncad.co.jp/~komata/c-kouza20.htm


（システムコールを直接呼び出すコードは初めて書いたのですが、こんなもんでいいのでしょうか？）

#mmapの限界値をはかるコード(C++)

```cpp:CheckMMAPSize.cpp
/*
 *
 */
#include <string.h>
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
#include <string>
#include <android/log.h>

#define  LOG_TAG    "CheckMMAPSize"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

/*
 *コメント生成用
 */
std::string string_format(const std::string fmt, ...) {
    int size = 100;
    std::string str;
    va_list ap;
    while (1) {
        str.resize(size);
        va_start(ap, fmt);
        int n = vsnprintf((char *)str.c_str(), size, fmt.c_str(), ap);
        va_end(ap);
        if (n > -1 && n < size) {
            str.resize(n);
            return str;
        }
        if (n > -1)
            size = n + 1;
        else
            size *= 2;
    }
    return str;
}

std::string ERROR_STR;

enum MMAP_STATE
{
	MMAP_OK=0,
	E_LSEEK,//seekに失敗
	E_WRITE,//ファイル書き込みに失敗
	E_MMAP,//mmapに失敗
	E_MUNMAP,//munmapに失敗
};

/*
 * mmapしてmunmapする
 */
MMAP_STATE doMmap(unsigned long size,int fd)
{
	char *ptr;
	char c;
	if (lseek(fd,size,SEEK_SET) < 0){
		ERROR_STR = string_format("ERROR: lseek failed¥n");
		LOGE(ERROR_STR.c_str());
		return E_LSEEK;
	}

	if(read(fd,&c,sizeof(char))==-1){
		c='¥0';
	}

	if(write(fd,&c,sizeof(char))==-1){
		ERROR_STR = string_format("ERROR: write failed¥n");
		LOGE(ERROR_STR.c_str());
		return E_WRITE;
	}
	ptr = (char*)mmap(0,size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
	if((int)ptr == -1)
	{
		LOGE(string_format("ERROR: mmap failed¥n").c_str());
		return E_MMAP;
	}
	if(munmap(ptr,size)==1){
		ERROR_STR = string_format("ERROR: munmap failed¥n");
		LOGE(ERROR_STR.c_str());
		return E_MUNMAP;
	}
	return MMAP_OK;
}

/*
 * mmapの限界値を計測する
 */
std::string CheckMMapSize()
{

	int fd;

	remove("/sdcard/Download/Dummy.map");
	fd = open("/sdcard/Download/Dummy.map",O_RDWR|O_CREAT,0666);
	if (fd==-1)
	{
		std::string error=string_format("ERROR: open failed¥n");
		LOGE(error.c_str());
		return error;
	}
	//ページサイズ取得
	long psize =getpagesize();

	unsigned long maxsize = 4294967295-psize;

	unsigned long size = psize*2;
	int count=0;

	LOGI(string_format("size %d pagesize %d ",size,psize).c_str());
	
	unsigned long length=size;
	MMAP_STATE state = MMAP_OK;
	while(length > psize)
	{
		LOGI(string_format("check").c_str());
		state = doMmap(size,fd);
		if( state==MMAP_OK)
		{
			LOGI(string_format("Mmap True %d",length).c_str());
			length*=2;
			size += length;
		}
		else if(state == E_MMAP)
		{
			LOGI(string_format("Mmap False %d",length).c_str());
			size-=length;
			length/=2;
			size +=length;
		}
		else
		{
			break;
		}

		if(size > maxsize)
		{
			LOGI(string_format("MaxSize ").c_str());
			break;
		}
	}
	close(fd);
	remove("/sdcard/Download/Dummy.map");

	std::string result;
	if(state == E_LSEEK || state == E_WRITE || state == E_MUNMAP)
	{
		result = ERROR_STR;
	}
	else
	{
		result = string_format("Max MMAP:%dMB(%d byte),pagesize:%dbyte",size/1024/1024,size,psize);
	}
	return result;
}

/* 
 */
extern "C" jstring
Java_[ここはJNIの規則に従って]_stringFromCheckMMap( JNIEnv* env,
                                                  jobject thiz )
{
	std::string result = CheckMMapSize();
	return env->NewStringUTF(result.c_str());
}

```

```:Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := CheckMMAPSize
LOCAL_SRC_FILES := CheckMMAPSize.cpp

LOCAL_LDLIBS    := -llog

include $(BUILD_SHARED_LIBRARY)

```

```:Application.mk
APP_ABI := armeabi armeabi-v7a
APP_PLATFORM := android-8
APP_STL := stlport_static
```

以上、NDKのコードでした

#NDKを呼び出すJavaクラス
単純にネイティブの関数を呼び出して結果を表示するだけです。

```java:MainActivity.java
public class MainActivity extends Activity {

	public native String stringFromCheckMMap();//ネイティブの関数追加
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		TextView  tv = new TextView(this);
        tv.setText( stringFromCheckMMap() );
        setContentView(tv);
		//setContentView(R.layout.activity_main);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
	static {
        System.loadLibrary("CheckMMAPSize");//ライブラリの読み込み
    }

}
```

NDKでストレージへのアクセスをしているのでAndroidManifestに以下を追加

```
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
```