내 LGT 갤탭은 3g가 아닌 wifi로만 쓰고 있어서 막 테스트하고 있다. 

xda에 떴던 갤탭 아이스크림 이미지( ICS+P1000-+alpha+4 )를 LGT에 함 구워봤다. 안된다. 삼성 로고 나오고 바로 로고 이미지 사라진다. 아마도 커널 패닉 난 것 같다. 


로그로 봐서는 /cache 디렉토리 권한이랑 연관되어 있는 것 같다.  mount도 안되구..  
그리고, 삼성 이미지를 다운로드 하라고 했지만, Odin으로 TI09이미로 복구했다. 

소스에서 일부 이슈있는 것들을 걷어내면 괜찮을까 싶어서.. 컴파일해야겠다는 생각이 든다. 쩝..
과연 내가 할 수 있을까?

아래 내용을 바탕으로 컴파일해보고, 계속 테스트해봐야지. 그때까지 나의 갤택이 벽돌이 안되길..
http://forum.xda-developers.com/showthread.php?t=1385153 

 
Posted by '김용환'
,

 

아트로봇에서 tmp36을 구매해서 아래 링크를 참조로 해서 회로도를 구성했다.

http://artrobot.co.kr/front/php/product.php?product_no=529&main_cate_no=&display_group=
http://www.ladyada.net/learn/sensors/tmp36.html
http://printk.tistory.com/42
http://www.ladyada.net/learn/sensors/tmp36.html




(이미지 참조 : http://www.ladyada.net/learn/sensors/tmp36.html)

내가 원하는 정보는 섭씨 온도인데, 잘 나온다.

int sensorPin = 0;

void setup() {
Serial.begin(9600);
}

void loop() {
int reading = analogRead(sensorPin); 
float voltage = reading * 5.0 / 1024.0;

float temperatureC = (voltage - 0.5) * 100 ;
Serial.println(temperatureC);
delay(1000);   
  
}

float getVoltage(int pin){
  return (analogRead(pin) * .004882814);
}

 

다만, 이 수식이 전류의 흐름이 불확실해지면 정보는 정확하지 않게 나오는 단점이 있다.

Posted by '김용환'
,

 

불쌍한 내 LGT 갤탭에 생명력을 불어넣기 위해서 공부를 하기 시작한 안드로이드..^^
하도 테그라크 커널을 칭찬하는 글이 많아서 함 설치해 본다. 써보니 훌륭하다!! 짱~

bootloader쪽과 안드로이드를 잘 아시네..

따라해봐야지~


<설치>

아래 본문을 참조해서 따라해 봤다.
http://pspmaster.tistory.com/120

 

1. 갤럭시 탭 초기화

이미지를 잘못구워서 무한 로고 반복되는 현상을 보여서, 초기화를 해야 했다.

갤럭시 탭 초기화하는 방법
http://blog.naver.com/jolith25/100120050336

 

2. 최신 펌웨어 다운로드

삼성 Kies 어플 다운로드
http://www.samsung.com/sec/sppt/pcApplication.do

 

삼성 Kies 어플을 이용해서 갤럭시탭을 최신 펌웨어로 업그레이드한다.

image

갤택의 빌드번호를 확인한다. “환경설정>시스템 정보>빌드 번호”

 

http://pspmaster.tistory.com/120 에 있는 tar 파일을 다운받는다.  빌드번호와 동일한 것임을 확인한다.

image

 

3. 갤럭시탭 다운로드 모드 변경

홈키 + 볼륨 다운키 + 전원키 를 동시에 눌러 Downling 화면이 나오게 한다.

 

4. Odin 을 이용해서 테그라크 커널 다운로드한다.

삼성 Kies 어플을 종료하고, Odin 1.85 다운로드 받고 실행한다.
http://jnstory.net/1640

Serial Com 포트가 연결되어 있음을 확인해야 한다.(COM4)

image

 

다운받은 테그라크 커널을  pda Files에 넣고, start를 버튼을 눌러준다.

image

 

Message가 다음과 같이 출력된다. Setup Connection에서 멈추어져 있으면, 갤럭시탭과 Odin과의 통신이 된 것이 아니므로 Odin을 다시 실행하거나, 갤럭시탭의 downloading 상태로 다시 실행한다.

잘 되면, 다음과 같은 로그가 출력된다.


<ID:0/005> Added!!
<ID:0/005> Odin v.3 engine (ID:5)..
<ID:0/005> File analysis..
<ID:0/005> SetupConnection..
<ID:0/005> Initialzation..
<ID:0/005> Get PIT for mapping..
<ID:0/005> Firmware update start..
<ID:0/005> zImage
<ID:0/005> NAND Write Start!!
<ID:0/005> RQT_CLOSE !!
<ID:0/005> RES OK !!
<ID:0/005> Completed..
<OSM> All threads completed. (succeed 1 / failed 0)
<ID:0/005> Removed!!
<ID:0/004> Added!!

잘되면 zImage 파일이 다운되면서 PASS 라는 파일이 뜬다.

image

 

부팅하면서 Tegrak Kernel 정보가 뜬다.  build 30

 

5. Wifi 설정을 한다.

 

6. 테그라크 애플리케이션을 마켓에서 다운받는다.

테그라크 커널, 테그라크 오버클럭, 맛클 핵펀치

 

7. 테그라크 커널 애플리케이션을 실행한다.

관련정보를 모두 확인하고 테스트해본다.

 

<루팅>

테그라크 커널 애플리케이션에서 ‘Enable 루팅’, ‘Disable 루팅’을 선택한다.

 

<오버 클러킹>

‘Enable 루팅’한 상태에서 테그라크 오버클럭 어플리케이션을 실행한다.

오버클럭 모듈 로드를 먼저 실행한후, CPU 최대 클럭 변경을을 선택한다. 1.3Ghz로 선택했다. 엄청 빠르다..

 

<삼성 Kies 연동>

테그라크 커널 모듈을 설치해도 삼성 Kies와 연동이 잘 되는 것 같다.

 

<백업>

백업을 잘 해놓자. recovery mode로 가서 system과 data 디렉토리를 백업한다.

http://pspmaster.tistory.com/120

http://cafe.naver.com/tendog.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=70966&

http://cafe.naver.com/sejongs0.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=219&

Posted by '김용환'
,

 

구글에서 bigtable을 관리하면서 하드디스크가 깨지는 현상이 있어서 SMART 라는 툴을 써서 어떻게 하면 좋을지 통계도 내고 하드디스크 폴트를 조금이나마 예측하려는 시도에 대한 논문 비스므리한 글을 읽고 정리한 적이 있었다.

Failure Trends in a Large Disk Drive Population(http://knight76.tistory.com/entry/Failure-Trends-in-a-Large-Disk-Drive-Population)

나도 운영하면서 하드디스크가 2년 만에 디스크 폴트가 자주 일어나서, 내가 관리하는 서버들만 그런 줄 알았더니. 구글도 똑같구나 하는 생각을 했었다.

이 프로그램이 당시에는 없었던 것 같은데. ubuntu 용으로 프로그램이 나온 것 같다. GUI용으로 gsmartcontrol, CMI로는 smartmontools 라는 프로그램이다.


System->Admin->Synaptic Package Manager 어플을 실행한 후, Quick Filter에 설치 어플리케이션의 이름을 입력하면 다음과 같은 화면이 뜨고 설치할 수 있다.

image

 

Application->System Tools –> GSmartContol  어플을 실행시킨다.

image

 

만약 VM 가상드라이브의 경우에는 자세한 정보가 보이지 않는다. 그러나 만약 리얼이면 아래와 같은 화면이 보인다.

GSmartControl main window GSmartControl attribute list

 

자세한 정보는 아래 페이지에서 확인하면 될 것 같다. 시스템 엔지니어나 운영자에게 도움이 될 것 같다. 이 정보를 근거로 좀 더 예측 가능한 상황이 될 수 있을 것 같다.

https://help.ubuntu.com/community/Smartmontools

Posted by '김용환'
,

 

시작하며..

리눅스 RAM 디스크에서 자바 컴파일(ant/ivy)하면 얼마나 성능을 향상시킬 수 있을까 고민을 해봤다.
빌드할 때 file io 를 줄이고, jvm 구동시간을 줄이면 어떨까 하는 생각으로 시도해보았다.

램디스크를 쓰면, 일반 하드디스크를 쓰는 것에 비해서 성능이 20배이상 좋다는 블로그 (http://forums.bukkit.org/threads/harddrive-vs-ramdisk-vs-tmpfs-benchmark.2710/) 와  10분 걸렸던 빌드가 5분이면 되었다는 블로그(어딘지 못찾고 있음)을 보았다.

10초 내 외의  빌드 시간이 짧은 프로젝트들이 수백 개가 있는 상황에서 램디스크를 활용하면 속도가 향상될지 테스트해보았따.

 

본론

리눅스에서 RAM 디스크를 사용하는 방법이 두가지이다. ram 디스크과 tmpfs 이다.

 

1. 램디스크 이용하기 (ramdisk)

/dev/ram* 을 이용해서  9M짜리 램디스크 잡아본다. (9216 = 9*1024M)

]# mkfs -t ext3 -q /dev/ram1 9216
]# mkdir -p /ramdisk
]# mount /dev/ram1 /ramdisk
]# df -H | grep ramdisk

]# df -H
Filesystem             Size   Used  Avail Use% Mounted on
/dev/sda1               11G   3.5G   7.0G  34% /
/dev/sda3              278G   2.7G   261G   1% /home
tmpfs                  6.3G      0   6.3G   0% /dev/shm
/dev/ram1              8.2M   1.2M   6.7M  15% /ramdisk

영구히 쓰려면, 커널을 수정 하거나 grub 설정을 변경해야 한다. 많이 귀찮은 작업이 있다. 잘못하면 OS 부팅이 안되고, 함부로 리눅스 서버를 리부팅할 수 없는 상황이라 간단하게 mount 방식을 이용했다.

]# mkdir –p /ramfs

]# mount -t ramfs -o size=1G,mode=777 ramfs /ramfs

 

 

2. tmpfs 를 이용

tmpfs는 임시적으로 파일을 ram을 이용하는 방식이다. 내가 사용하는 centos 5.3 64비트에는 다행히 tmpfs를 지원한다.

]# grep tmpfs /proc/filesystems
nodev   tmpfs

]# mkdir /tmpfs

]# mount -t tmpfs -o size=1G,mode=777 tmpfs /tmpfs

]# df -H
Filesystem             Size   Used  Avail Use% Mounted on
/dev/sda1               11G   3.5G   7.0G  34% /
/dev/sda3              278G   2.7G   261G   1% /home
tmpfs                  6.3G      0   6.3G   0% /dev/shm
/tmpfs                 1.1G      0   1.1G   0% /tmpfs

 

 

3. 비교

빌드에 대한 객관성을 유지하기 위해서 캐쉬가 된 부분을 모두 포함한 것이다. ant와 java는 모두 하나의 파일시스템안에 넣었다. 테스트 결과는 다음과 같다.

  디스크 tmpfs
ram disk (mount방식)
xeon 5148  2.33GHz 4개, ram : 8G A 소스 : 14 s 
B 소스 : 11s
A 소스 : 18 s 
B 소스 : 11s
X
xeon L5640 2.27Ghz] 12개, ram :12G A 소스 : 14 s 
B 소스 : 9s
A 소스 : 14 s 
B 소스 : 9s
A 소스 : 14 s 
B 소스 : 9s

* 특이한 것은 램에서 돌리는 것이 가장 빠른 것은 아닌 것 같았다. 18초가 나오는 경우도 있었다. 
정확한 이유는 알 수 없지만… (어디선가 바틀렉이 있는것 같다.. 잘 찾아봐야지.)

 

마치며..

시간을 체크해보 전체 빌드 시간 중 java 컴파일에 해당되는 시간은 상당히 적었다. 연동이나 ivy 연동부분이나 javascript 작업등으로 인해서 길었던 부분이 있었다. 따라서 file io 이슈나 java 시작 시간은 큰 문제가 아니다. 오히려 컴파일 이슈가 큰 녀석이나 해당되는 것 같았다.

확실히 몇 분 단위의 빌드시간이 되어야 RAM 디스크를 이용 하는게 나을 것 같다. 빌드시간이 10초 내외의 수백개의 프로젝트를 빌드할 때는 병렬처리 및 클러스터링 빌드 방식이 더 나은 것 같다. 

Posted by '김용환'
,

 

나의 서버의 현재 메모리는 시스템적으로 돌아가는 프로세스만 동작 중이다. 그래서 펑펑 놀고 있는 서버이다.

image

 

top명령으로 보았을 때, 정보를 보면 약간 이상하다. 전체 메모리 2G에 사용된 (used) 메모리 1.2G이다.  결국 8G 의 free 메모리만 있다. buffers와 cached 메모리에 많은 메모리가 할당되어 있다.

image

 

그 이유는 리눅스 커널에서는 프로세스가 반납한 메모리를 바로 free로 돌리지 않는다.

Cached memory는 실행중인 프로그램이 메모리가 필요하다면 바로 대체될 수 있는 free memory인 셈이다. 기존에 실행했던 프로그램이 이 영역을 사용했었고, 또 필요하면 이 영역을 준다고 하면 될 것 같다.

buffers 메모리는 data structure, chunk의 개념이 들어가 있는 메모리인데 반해 cached는 패킷 단위의 메모리 로 보면 된다.

리눅스 커널은 ram에 대해서 buffers, cached, free 이렇게 3단계로 구분해서 메모리를 구분하고 있다.

 

top 명령어를 통해서 본 메모리를 본다.

image

VIRT는 프로세스가 전에 사용했던 가상 메모리의 전체 크기를 말한다. code, data, shared library 등을 포함한다. top 명령어는 각 프로세스의 /proc/#pid/state 의 VmSize 의 내용을 가지고 이 정보를 보여준다. swap + res 영역을 더한 값이다. 이 값이 크면, 얼마나 많은 메모리가 접근하고 있는지를 알 수 있는 지표가 된다.

RES는 프로세스가 사용중인 RAM 에 올라간 물리 메모리 (swap 제외) 를 말한다.

SHR는 프로세스가 사용중인 공유 메모리의 합이다.

 

나머지 정보는 man top 을 통해서 볼 수 있다.

'c or linux' 카테고리의 다른 글

[ubuntu] 디스크 용량 확인  (0) 2012.01.18
Ubuntu의 SMART 프로그램  (0) 2012.01.09
Swap 메모리  (2) 2012.01.05
리눅스 운영체제의32bit 또는 64bit 여부 확인  (0) 2011.12.30
ubuntu 패스워드 변경  (0) 2011.11.23
Posted by '김용환'
,

 

 

2012년 1월 5일 lol 홈피.

http://www.leagueoflegends.co.kr/

 

image

 

나름 top 10안에 들어가는 게임인데.. 고생이 많네…

Posted by '김용환'
,

 

시작하며..

0.6.4 버전의 카산드라(Cassandra)를 패치한 0.6.5의 큰 변화는 리눅스 운영체제에 동작하는 java 프로세스가 mmap을 사용하면서 swap 메모리가 증가되는 부분을 막기 위함이다.

아래 내용은 이 글을 이해하기 위한 작은 정보이다.

http://knight76.tistory.com/entry/Swap-메모리
http://knight76.tistory.com/entry/자바-RandomAccessFile-클래스의-map-메서드-내부-구조-분석

 

본론..

<이슈와 결과 내용>

0.6.4버전의 카산드라에서 read 성능이 너무 낮아졌고, swap 메모리가 많아지면서 성능이 떨어졌다고 한다.

(https://issues.apache.org/jira/browse/CASSANDRA-1214)

아래와 같이 성능이 어느 정도 나오지 못하고 들쑥날쑥 하고 있다.

JVM의 full gc 시간이 항상 일어나지 않는다. native heap 의 영역의 메모리가 많아져서 swap memory로 갔다가 gc time 때 swap으로부터 메모리를 읽은 후 gc 데이터를 모두 날리는 작업을 하면서 성능이 떨어지는 것이다.

gc 시점 때 all stop + swap in/out이 일어나면서 계속 성능이 저하되는 현상이 생긴 것이다.
(마치 gc 그래프와 흡사한 성능 그래프가 증명하고 있다.)

0.6.5 버전에서는 0.6.5 버전의 패치를 통해서 13%의 성능 향상이 일어났다.

 

<자세한 내용>

카산드라 0.6.4 버전에서는 SSTable read 성능을 높이기 위해서 NIO를 사용했다. 내부 데이터를 MappedByteBuffer 클래스로 읽는다. 이 때 mmap을 사용한다.

storage-conf.xml 설정 파일의 DisAccessMode 속성의 값은 auto이다.

<DiskAccessMode>auto</DiskAccessMode>

 

DatabaseDescritor 클래스의 static 블럭에서 파일을 읽고 있으며 64비트 머신인 경우에는 mmap이 디폴트값으로 정해진다.

String modeRaw = xmlUtils.getNodeValue("/Storage/DiskAccessMode");
diskAccessMode = DiskAccessMode.valueOf(modeRaw);
if (diskAccessMode == DiskAccessMode.auto) {
          diskAccessMode = System.getProperty("os.arch").contains("64") ?
          DiskAccessMode.mmap
: DiskAccessMode.standard;
          indexAccessMode = diskAccessMode;
}

 

disk access mode 의 값이 standard인 경우는 index buffer 자체가 없기 때문에 큰 성능을 기대할 수는 없지만, mmap 모드인 경우에는 index buffer를 메모리에 사용할 수 있다.

SSTableReader 클래스 생성자 에서는 mmap disk access의 경우 mmap을 사용할 수 있도록 되어 있다.

    long indexLength = new File(indexFilename()).length();
    int bufferCount = 1 + (int) (indexLength / BUFFER_SIZE);
    indexBuffers = new MappedByteBuffer[bufferCount];
    long remaining = indexLength;
    for (int i = 0; i < bufferCount; i++)
    {
        indexBuffers[i] = mmap(indexFilename(), i * BUFFER_SIZE,
                                 (int) Math.min(remaining, BUFFER_SIZE));
        remaining -= BUFFER_SIZE;
    }

 

mmap 메서드는 다음과 같이 정의되어 있다.

private static MappedByteBuffer mmap(String filename, long start, int size) throws IOException
{
    RandomAccessFile raf;
    try
    {
        raf = new RandomAccessFile(filename, "r");
    }
    catch (FileNotFoundException e)
    {
        throw new IOError(e);
    }

    try
    {
        return raf.getChannel().map(FileChannel.MapMode.READ_ONLY, start, size);
    }
    finally
    {
        raf.close();
    }
}


RandromAccessFile 클래스의 map 메서드는 내부적으로 clib의 공유할 수 있는 mmap64 시스템콜을 호출하여 native heap 영역의 메모리를 공유한다. (참고 : http://knight76.tistory.com/entry/자바-RandomAccessFile-클래스의-map-메서드-내부-구조-분석)

 

<버그질라 이슈에 대한 설명>

리눅스 커널의 메모리 관리자는 swap 메모리로 언제든지 데이터를 옮길 수 있다.
리눅스 커널의 vm.swappiness 파라미터가 0 이상이면 메모리가 어느 정도 부족하다고 판단하면 swap 메모리로 옮겨놨다가 필요할 때 램에 적재하게 한다.

mmap을 쓰다보니 native heap 영역에 있는 데이터들의 gc 가 되는 시점이 중요해진다.  java의 heap 영역을 청소하는 full gc 가 호출 될 때 unmap이 호출이 되는 구조로 되어 있다. (참고 : http://knight76.tistory.com/entry/자바-RandomAccessFile-클래스의-map-메서드-내부-구조-분석). unmap이 되는 클래스가 PhantomReference를 상속하였는데, 이 의미는 정말 사용 중이지 않을 때 gc 대상이 된다. (전문 용어로 reachable 하지 않거나 referent 객체로부터 로부터 mark되지 않은 상태). soft reference나 weak reference 보다도 최대한 끝까지 살아남는 놈들이다.  (자바 스펙에는 모호하게 적혀 있고, java.lang.ref api와 소스에서 그 의미를 찾아볼 수 있다.)

full gc가 일어나는 시점에서 본다면, native heap 영역이 가득차거나 java heap 영역이 가득찰 때 일어난다. (사실 정확하게 찬다는 의미보다는 gc가 적당한 비율로 메모리를 찼다고 판단하면 gc한다.) 결국은 많은 메모리를 사용 중에 일어날 수 있다.

리눅스 커널은 mmap으로 할당받은 데이터인 SSTable의 정보를 swap 메모리로 이동한다. 무식한 이 데이터는 full gc에도 차곡차곡 쌓이다가 정말 확실히 gc의 대상이 될 때, gc가 된다.
이 때, gc 시점에 정리를 하는데, swap 영역에 있는 녀석이 gc가 될 수 없다. 즉 swap in(ram으로 불러들임)을 한후, gc가 된다. 만약 swap 영역의 메모리가 ram 메모리의 크기보다 많이 크다면 성능은 전혀 기대할 수 없는 수준으로 내려갈 수 밖에 없다.

따라서, 만약 적당한 크기의 swap 영역을 지정할 필요가 있다.  이 영역을 제대로 잡는 것은 계속적인 테스트를 통해서만 할 수 있다.

이런 부분 때문에 카산드라에서는 swaping 셋팅을 전혀 안하는 것이 최고의 선택(best value)라고 하는 배경이다.

http://wiki.apache.org/cassandra/MemtableThresholds

Virtual Memory and Swap

On a dedicated cassandra machine, the best value for your swap settings is no swap at all -- it's better to have the OS kill the java process (taking the node down but leaving your monitoring, etc. up) than to have the system go into swap death (and become entirely unreachable).

Linux users should understand fully and then consider adjusting the system values for swappiness, overcommit_memory and overcommit_ratio.

 

http://wiki.apache.org/cassandra/FAQ#mmap

Why does top report that Cassandra is using a lot more memory than the Java heap max?

Cassandra uses mmap to do zero-copy reads. That is, we use the operating system's virtual memory system to map the sstable data files into the Cassandra process' address space. This will "use" virtual memory; i.e. address space, and will be reported by tools like top accordingly, but on 64 bit systems virtual address space is effectively unlimited so you should not worry about that.

What matters from the perspective of "memory use" in the sense as it is normally meant, is the amount of data allocated on brk() or mmap'd /dev/zero, which represent real memory used. The key issue is that for a mmap'd file, there is never a need to retain the data resident in physical memory. Thus, whatever you do keep resident in physical memory is essentially just there as a cache, in the same way as normal I/O will cause the kernel page cache to retain data that you read/write.

The difference between normal I/O and mmap() is that in the mmap() case the memory is actually mapped to the process, thus affecting the virtual size as reported by top. The main argument for using mmap() instead of standard I/O is the fact that reading entails just touching memory - in the case of the memory being resident, you just read it - you don't even take a page fault (so no overhead in entering the kernel and doing a semi-context switch). This is covered in more detail here.

 

그럼에도 불구하고, 최근에 릴리즈된 1.0.2소스를 보니 DiskAccessMode의 디폴트값이 auto로 되어 있다. 이는니눅스  64 비트 운영체제를 사용하는 신규 개발자에게는 재앙이 될 수 있다는 얘기도 된다.

1.0.2에서는 약간 구조를 바꾸어서 쓰고 있으며, Table .getDataFileLocation() 호출시 적당한 시점에 System.gc()도 및 unmap를 통해서 메모리가 적당히 있을 수 있도록 수정되었다.  (정확히 말하면, 언제부터 바뀐지는 잘 모르지만, 많이 노력하고 있다는 느낌이다.

 

<mlockall의 사용>

0.6.5에서 바뀐 부분은 mlockall 메서드를 사용할 수 있도록 한 부분이다. 이를 위해서 JNA를 사용하였다. (참조 : http://knight76.tistory.com/entry/jni-vs-jna)

ivy.xml 설정 파일에 jna library가 추가되었다.

<dependency org="net.java.dev.jna" name="jna" rev="3.2.7"/>

ivysettings 설정 파일이 추가되었다.

<ivysettings>
  <settings defaultResolver="ibiblio"/>
  <resolvers>
    <chain name="chain" dual="true">
      <ibiblio name="java.net2" root="http://download.java.net/maven/2/" m2compatible="true"/>
      <ibiblio name="ibiblio" m2compatible="true" />
    </chain>
  </resolvers>
  <modules>
    <module organisation="net.java.dev.jna" name="jna" resolver="chain" />
  </modules>
</ivysettings>

 

CassandraDaemon 클래스의 setup 메서드안에서  FBUtilities.tryMlockall(); 을 호출한다. tryMlockall() 에서는 clib의 mlockAll() 시스템 콜을 호출한다. 즉 처음부터 nio 로 만든 객체들을 swap 메모리로 가지 않게 하고, ram에서만 사용할 수 있도록 바뀐 것이다.


public static void tryMlockall()
{
    int errno = Integer.MIN_VALUE;
    try
    {
       int result = CLibrary.mlockall(CLibrary.MCL_CURRENT);
        if (result != 0)
            errno = Native.getLastError();
    }
    catch (UnsatisfiedLinkError e)
    {
        // this will have already been logged by CLibrary, no need to repeat it
        return;
    }
    catch (Exception e)
    {
        logger_.debug("Unable to mlockall", e);
        // skipping mlockall doesn't seem to be a Big Deal except on Linux.  See CASSANDRA-1214
        if (System.getProperty("os.name").toLowerCase().contains("linux"))
        {
            logger_.warn("Unable to lock JVM memory (" + e.getMessage() + ")."
                         + " This can result in part of the JVM being swapped out, especially with mmapped I/O enabled.");
        }
        else if (!System.getProperty("os.name").toLowerCase().contains("windows"))
        {
            logger_.info("Unable to lock JVM memory: " + e.getMessage());
        }
        return;
    }

    if (errno != Integer.MIN_VALUE)
    {
        if (errno == CLibrary.ENOMEM && System.getProperty("os.name").toLowerCase().contains("linux"))
        {
            logger_.warn("Unable to lock JVM memory (ENOMEM)."
                         + " This can result in part of the JVM being swapped out, especially with mmapped I/O enabled."
                         + " Increase RLIMIT_MEMLOCK or run Cassandra as root.");
        }
        else if (!System.getProperty("os.name").toLowerCase().contains("mac"))
        {
            // OS X allows mlockall to be called, but always returns an error
            logger_.warn("Unknown mlockall error " + errno);
        }
    }
}

 

이런 용도로 CLibrary 클래스가 추가되었다.

public final class CLibrary
{
    private static Logger logger = LoggerFactory.getLogger(CLibrary.class);

    public static final int MCL_CURRENT = 1;
    public static final int MCL_FUTURE = 2;
   
    public static final int ENOMEM = 12;

    static
    {
        try
        {
            Native.register("c");
        }
        catch (NoClassDefFoundError e)
        {
            logger.info("JNA not found. Native methods will be disabled.");
        }
        catch (UnsatisfiedLinkError e)
        {
            logger.info("Unable to link C library. Native methods will be disabled.");
        }
    }

    public static native int mlockall(int flags);

    public static native int munlockall();

    private CLibrary() {}
}

 

참고로 munlockall() 메서드도 native로 바인딩되었지만 1.0.2에서도 실제 사용하지 않고 있다.

 

 

마치며..

2일 동안 카산드라의 파일 시스템쪽이 어떻게 되어 있는지 잘 몰랐는데. 이번 기회에 카산드라의 mmap 이슈와 그 처리방법에 대해서 잘 파악할 수 있었다. 시간이 되면 카산드라 분석해볼까나…

 

 

 

* 참고 내용 : mlock, mlockall (man 페이지를 이용한 정보 처리)


mlock, mlockall, munlock, munlockall 시스템 콜 함수에 대한 정리
"man mlock" 명령어를 사용하면 mlock 에 대한 정보를 확인할 수 있다.

       #include <sys/mman.h>

       int mlock(const void *addr, size_t len);

       int munlock(const void *addr, size_t len);

       int mlockall(int flags);

       int munlockall(void);

man mlock 에 대한 결과값을 확인할 수 있다.

mlock() 또는 mlockall()은 swap 영역으로 메모리 이동이 되지 않도록 RAM 안에 사용된 virtual memory(가상 주소)를 lock 한다.
munlock() 또는 munlockall()은 리눅스 커널 메모리 관리자에 의해서 swap out될 수 있도록 unlock해주는 함수이다.

mlock() 함수는 주소와 길이에 대한 영역을 lock을 개런티한다.
mlockall() 함수는 프로세스에 대한 주소번지 모두를 lock 한다.
코드/데이터/스택/동적라이르리러,사용자 커널데이터/공유메모리/메모리맵 데이터까지모두 lock한다.

mlockall() 의 아규먼트는 int형 타입의 flag 값을 지정하면 된다. 아래 타입 중에 하나만 또는 OR로 사용가능하다.

- MCL_CURRENT (1): 프로세스의 주소 영역으로 매핑된 모든 페이지
- MCL_FUTURE (2) : 앞으로 프로세스의 주소 영역에 매필될 모든 페이지.
               이 옵션을 사용하면 메모리 할당과 같은 시스템 콜(mmap, sbrk, malloc)을 쓰다 RAM 영역을 넘어설때 할당을 해주지 못하게 된다.
               만약 Stack에서 그 일이 발생하면, SIGSEGV signal이 발생하고 stack 영역은 확장을 할 수 없다.

 

Memory locking은 두 개의 주요 어플리케이션에 사용할 수 있다. real time 알고리즘과 보안을 높이 필요로 하는 데이터 처리쪽에 사용할 수 있다.
특히 real time 알고리즘이 필요한 어플리케이션에서는 locking 함으로서, page fault를 원천적으로 봉쇄해서
time-critical section(시간 동기 문제)가 일어나지 않게 할 수 있다.

 

Cassandra 0.6.5~1.0.2 (최신) 까지 mlockall(MCL_CURRENT)을 주어 사용하고 있음. 따라서 언제든지 차곡차곡 쌓이는 일부 메모리는 swap 메모리로 늘어날 수 있음..

Posted by '김용환'
,

Swap 메모리

c or linux 2012. 1. 5. 12:04

 

시작하며..

리눅스의 swap 메모리에 대해서 이해한다.

 

본론

만약 어플리케이션의 RAM 용량이 차면, swap 메모리가 자동으로 늘어나도록 되어 있다. 하드디스크의 디스크를 swap 메모리로 만들기 때문에 속도가 느려진다. 또한 RAM의 용량보다 2배로 swap 메모리를 잡도록 되어 있다. (참조 : http://vr.org/docs/centos/4/html/rhel-sag-en-4/ch-swapspace.html) 시스템 엔지니어는 이런 이슈로 리눅스 서버를 셋팅할 때 swap 영역을 얼마나 잡을지 판단해야 한다. 때로는 개발자가 이 부분을 고쳐 성능을 향상 시킬 수 있다. (참고 : https://wiki.archlinux.org/index.php/Swap)


개인적으로는 메모리가 갑자기 부족한 경우를 제외하고는 swap 자체를 안쓰는 것을 선호한다. 성능상 swap 메모리를 쓰는 것보다 swap in , swap out이 성능을 무너뜨리는 것을 많이 봐왔다. 또한 너무 크게 사용하는 것도 경계한다. (난 보수적인 사람이 되었나 보다… 아직 적절히 활용한 대용량 처리 사례를 못해봐서 그런지도 모르겠다. ) 차라리 RAM 하나 더 꽂는게 더 나은 것 같다. 

하지만 DB나, 오픈 소스 대용량 처리 어플리케이션의 경우에서는 swap을 많이 활용하려고 하고 있다는 사실이다. 어떻게 하든 속도를 내기 위해서 메모리에서 처리하려다 보니 swap을 쓸 수 밖에 없는 구조로 가고, 너무 많이 써서 문제가 되니 다시 mlock으로 막는 구조로 가는 부분으로 처리하는 형태로 가고 있다.

 

1. swap 및 ram 메모리 상태 보기

free 명령어를 이용해서 간단히 살펴볼 수 있다.

# free
             total       used       free     shared    buffers     cached
Mem:       4150252    3991868     158384          0      78504    2265916
-/+ buffers/cache:    1647448    2502804
Swap:      2096472        208    2096264

 

2. Swap 메모리 설정하는 방법

리눅스 커널 2.6부터 swap 메모리를 설정할 수 있다. swap 영역은 dd 명령어와 mkswap 명령어를 이용해서 swap을 만들 수 있다. (http://www.artwork.com/gdsii/qckvu/faq/linux_swap.htm) 이렇게 만들어진 영역은 swap 메모리로 쓰고 있다는 뜻이다. 아래의 예는 swap을 2G 정도만 쓰고 있다는 것을 의미한다.

# cat /proc/swaps
Filename                                Type            Size    Used    Priority
/dev/cciss/c0d0p2                 partition       2096472    208        -1

 

3. Swap 메모리 사용 빈도 설정 방법

swap 메모리를 사용 빈도를 확인하는 방법은 vm.swappiness 정보를 보는 것이다. centos를 비롯한 일부 서버는 디폴트로 60으로 잡혀 있다.

]# sysctl vm.swappiness   (또는 cat /proc/sys/vm/swappiness 으로 확인 가능)
vm.swappiness = 60

vm.swapiness 정보는 커널 파라미터이며 swap 영역을 얼마나 더 보겠냐 (또는 회피하겠냐)는 의미를 가지고 있다. (kernel's preference (or avoidance) of swap space) 여기에 0의 값을 넣으면 swap을 최대한 쓰지 않겠다는 의미이다. 만약 100은 하드디스크의 swap영역을 최대한 활용하겠다는 의미이다.

제일 이슈는 swap 메모리가 많이 잡혀 있는 것보다 얼마나 사용하고 있는지를 측정하는 것이 좋다. free나 cat /proc/swaps 명령어를 이용해서 해결할 수 있다.

만약 swap 메모리를 쓰지 않으려면, vm.swappiness를 0으로 셋팅하면 된다.

/etc/sysctl.conf 에 ‘vm.swappiness=0 ‘ 설정을 추가하고, ‘echo 0 > /proc/sys/vm/swappiness ‘ 명령어를 이용해서 적용한다.


* Reference

http://forums.gentoo.org/viewtopic.php?t=175419

5. Swappiness (2.6 kernels)
Since 2.6, there has been a way to tune how much Linux favors swapping out to disk compared to shrinking the caches when memory gets full.

ghoti adds:
When an application needs memory and all the RAM is fully occupied, the kernel has two ways to free some memory at its disposal: it can either reduce the disk cache in the RAM by eliminating the oldest data or it may swap some less used portions (pages) of programs out to the swap partition on disk.
It is not easy to predict which method would be more efficient.
The kernel makes a choice by roughly guessing the effectiveness of the two methods at a given instant, based on the recent history of activity.

Before the 2.6 kernels, the user had no possible means to influence the calculations and there could happen situations where the kernel often made the wrong choice, leading to thrashing and slow performance. The addition of swappiness in 2.6 changes this. 


 

또한 vfs_cache_pressure 라는 값을 크게 잡아, 커널의 cache를 빨리 날려줄 수 있도록 한다. linux에서는 어플에서 처리한 남는 메모리를 바로 free되지 않고 buffer cache에 남게 한다. 이를 이용하면 free memory를 더 확보할 수 있다. 하지만 위험성이 존재하니, 테스트가 필요하다.

 


4. swap in, swap out 확인하는 방법

sar 명령어를 이용하면 메모리 상태를 확인할 수 있다. 

]# sar -r -s 11:41:00
Linux 2.6.9-78.ELsmp        

11시 41분 kbmemfree kbmemused  %memused kbbuffers  kbcached kbswpfree kbswpused  %swpused  kbswpcad
11시 42분   5932304  10700956     64.33    358820   1087040   2096280       192      0.01         0
Average:    5932304  10700956     64.33    358820   1087040   2096280       192      0.01         0

 

sar 명령어의 –B 파라미터는 swapping 통계를 낸다.

]# sar -B 2 5
Linux 2.6.9-78.ELsmp

11시 41분 28초  pgpgin/s pgpgout/s   fault/s  majflt/s
11시 41분 30초      2.04     75.51     21.43      0.00
11시 41분 32초      0.00      0.00    359.49      0.00
11시 41분 34초      0.00    258.46     19.49      0.00

 

5. 이슈

jvm 영역에서 사용된 메모리는 언제나 swap 영역으로 이동될 수 있다. 
jvm 에서 nio를 사용하는 경우 (예, DirectBuffer) 에는 리눅스 운영체제가 언제든지 swap 영역으로 이동할 수 있다. 이를 방지하기 위해서는 mlock/mlockall 시스템 콜을 이용해서 특정 virtual address를 ram 에 묶어 둘 수 있게 한다. 

mlock을 쓰는 방식이 cassandra 0.6.5 에서 추가되었다.

 

마치며..

리눅스의 swap 메모리를 이해할 수 있었다. jvm을 사용하면서 나타날 수 있는 swap 메모리 이슈에 대해서 쉽게 처리할 수 있도록 공부하자.

cassandra의 swap 이슈 에 대한 분석을 좀 더 이해하려고 한다.

Posted by '김용환'
,

 

시작하며..

자바언어에서는 RandomAccessFile 을 이용해서, native heap 영역의 메모리를 얻어올 수 있다. jvm 내의 heap 영역이 아니기 때문에 따로 잡은 메모리 공간을 이용한다. 이를 통해서 자바는 속도를 향상시켜 read/write를 빨리 할 수 있다. java 1.6 update 22 내부 소스를 바탕으로 어떤 구조로 되어 있는지 살펴본다.

 

본론

간단하게 RandomAccessFile 클래스를 이용해서 Channel을 얻어온 후 READ only용으로 메모리 mapping 하는 메서드를 이용해서 MappedByteBuffer 클래스 타입의 인스턴스를 리턴하는 메서드이다.


 

private static MappedByteBuffer mmap(String filename, long start, int size) throws IOException
{
    RandomAccessFile raf;
    try
    {
        raf = new RandomAccessFile(filename, "r");
    }
    catch (FileNotFoundException e)
    {
        throw new IOError(e);
    }

    try
    {
        return raf.getChannel().map(FileChannel.MapMode.READ_ONLY, start, size);
    }
    finally
    {
        raf.close();
    }
}

 


RandomAccess의 getChannel() 메서드는 sun.nio.ch.FileChannelImpl 클래스를 리턴한다.  FileChannelImpl 클래스의 원형은 다음과 같다.

class FileChannelImpl  {

public MappedByteBuffer map(MapMode mode, long position, long size)
    throws IOException {


addr = map0(imode, mapPosition, mapSize);

Unmapper um = new Unmapper(addr, size + pagePosition); // 큰 의미없는 data structure
return Util.newMappedByteBufferr(isize, addr + pagePosition, um);

}

}


 

FileChannelImpl 클래스의 map 메서드에서는 map0() 메서드를 호출하여 주소 번지를 얻어오고, ByteBuffer로 만들어 리턴한다.

map0() 메서드의 원형은 native 로 연결되어 있다.

// Creates a new mapping
private native long map0(int prot, long position, long length)
    throws IOException;

 

이 메서드는 jni 코드인 FileChannelImpl.c 파일로 연결되어 있다. 역시 mmap64 메서드를 사용하고 그 주소값을 리턴하고 있다. 디폴트는 MAP_SHARED이다.

 


JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileChannelImpl_map0(JNIEnv *env, jobject this,
                     jint prot, jlong off, jlong len)
{
    void *mapAddress = 0;
    jobject fdo = (*env)->GetObjectField(env, this, chan_fd);
    jint fd = fdval(env, fdo);
    int protections = 0;
    int flags = 0;

    if (prot == sun_nio_ch_FileChannelImpl_MAP_RO) {
        protections = PROT_READ;
        flags = MAP_SHARED;
    } else if (prot == sun_nio_ch_FileChannelImpl_MAP_RW) {
        protections = PROT_WRITE | PROT_READ;
        flags = MAP_SHARED;
    } else if (prot == sun_nio_ch_FileChannelImpl_MAP_PV) {
        protections =  PROT_WRITE | PROT_READ;
        flags = MAP_PRIVATE;
    }

    mapAddress = mmap64(
        0,                    /* Let OS decide location */
        len,                  /* Number of bytes to map */
        protections,          /* File permissions */
        flags,                /* Changes are shared */
        fd,                   /* File descriptor of mapped file */
        off);                 /* Offset into file */

    if (mapAddress == MAP_FAILED) {
    if (errno == ENOMEM) {
        JNU_ThrowOutOfMemoryError(env, "Map failed");
        return IOS_THROWN;
    }
    return handle(env, -1, "Map failed");
    }

    return ((jlong) (unsigned long) mapAddress); 
}

 


 

참고로..

다른 함수를 살펴보면, postion0 () 메서드에서는 lseek64를, size0() 메서드에서는 fstat64를, close0() 메서드에서는 close를, release에서는 fcntl를, forces0 메서드에서는 fsync를, unmap0 메서드는 munmap를 쓰는등 파일 관련된 시스템 콜을 사용하고 있다. 

특별히 transfer0은 sendfile 또는 sendfile64 (동적 라이브러리를 이용한 function pointer의 형태)를 사용한다. sendfile64 구현을 안한 리눅스가 있나 보다…

 

주소번지를 받고 나서 메모리 매핑을 하는Util.newMappedByteBufferr(isize, addr + pagePosition, um) 메서드를 살펴본다. Reflection의 Constructor인 directByteBufferConstructor를 이용해서 MappedByteBuffer 클래스를 생성한다.

static MappedByteBuffer newMappedByteBuffer(int size, long addr, Runnable unmapper)


MappedByteBuffer dbb;
if (directByteBufferConstructor == null)
     initDBBConstructor();

dbb = (MappedByteBuffer)directByteBufferConstructor.newInstance(
new Object[] { new Integer(size), new Long(addr), unmapper });

}

private static void initDBBRConstructor() {

Class th = Class.forName("java.nio.DirectByteBufferR"); // java api에는 나오지 않는 녀석
directByteBufferRConstructor = th.getDeclaredConstructor(
                    new Class[] { int.class, long.class,
                              Runnable.class });
directByteBufferRConstructor.setAccessible(true);

}

 



설명도 잘 나와 있다.

class DirectByteBuffer extends DirectByteBuffer implements DirectBuffer  {
..

// For memory-mapped buffers -- invoked by FileChannelImpl via reflection
    //
    protected DirectByteBufferR(int cap, long addr, FileDescriptor fd, Runnable unmapper)   {
        super(cap, addr, fd, unmapper);
   }


// For memory-mapped buffers -- invoked by FileChannelImpl via reflection
//
protected DirectByteBuffer(int cap, long addr, FileDescriptor fd, Runnable unmapper){
    super(-1, 0, cap, cap, fd);
    address = addr;
    viewedBuffer = null;
    cleaner = Cleaner.create(this, unmapper);

}
..
}


 

DirectByteBufferR 클래스는 래퍼류로서 DirectByteBuffer를 상속받았기 때문에 생성자 레벨에서 편하게 가공할 수 있다. DirectByteBuffer클래스는 MappedByteBuffer를 상속받은 클래스이기 때문에 MappedByteBuffer Casting 이 가능하다.

 

참고로.. Cleaner 클래스는 PhantomReference 를 상속해서 쓰고 있다. GC를 편리하게 하기 위함으로 추가할 때마다 내부적으로 linked list로 객체를 만들어 정리될 때 Runnable로 들어온 객체의 run() 메서드를 호출한다.

public class sun.misc.Cleaner extends java.lang.ref.PhantomReference ..

 

private static class Unmapper
    implements Runnable
{

    private long address;
    private long size;

    private Unmapper(long address, long size) {
        assert (address != 0);
        this.address = address;
        this.size = size;
    }

    public void run() { // 메모리가 부족할 때 종료되게 함.. PhantomReference 임.
        if (address == 0)
            return;
        unmap0(address, size);
        address = 0;
    }

}



unmap0 메서드를 호출하여 항상 gc가 되게 한다.

 

 

마치며..

자바에서 nio map 사용시 리눅스 mmap 시스템 콜에 의해서 매핑된 데이터를 처리 하는 로직을 살펴보았다. 자연스럽게 생명주기도 살펴보았다.

다음에는 Cassandra의 Linux swap 이슈에 대해서 살펴봐야지..  mmap이슈가 있었던 내용을 쓰면서 정리하고자 한다.

Posted by '김용환'
,