LoginSignup
4
4

More than 5 years have passed since last update.

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

Last updated at Posted at 2013-07-28

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"/>
4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4