mmapとは
ファイルやデバイスのリソースをメモリ領域にマッピングするシステムコールですが、
たぶん、メモリーマップドファイルとかこれ呼んでるんじゃないでしょうか。
で、これが、「どれくらいまでマッピングできるか」がやってみないとわからないというのが現状です。
そこで、Androidでどのくらいmmapできるのかを測るコードを書きました。
ただAndroidでmmapの限界値を知りたい人がどれほどいるのか・・・
そもそもほかに方法があるような気もします・・・
Androidでのmmap限界値の取得方法
NDKを使って直接システムコールを呼び出し、どれくらいマッピングするとエラーになるか調べます
簡単な処理内容ですが、
- ダミーファイルをSDカード(もしくはストレージ)に生成
- ページサイズを取得
- ダミーファイルをページサイズ*2のサイズ分ファイルポインタを移動
- mmapして、munmapする
- 成功したらさらに増加分を倍にしてプラスし、実行
- 失敗したら増加分を半分にして実行
- 増加分がページサイズ以下になったら終了
というようなアルゴリズムです。
mmapの使い方については下記のサイトを参考にしました
http://www.ncad.co.jp/~komata/c-kouza20.htm
(システムコールを直接呼び出すコードは初めて書いたのですが、こんなもんでいいのでしょうか?)
mmapの限界値をはかるコード(C++)
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クラス
単純にネイティブの関数を呼び出して結果を表示するだけです。
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"/>