OpenVZ系の環境でプロセスの使用メモリ量を節約するライブラリを書いた

以前、

スワップがなく、かつ、overcommit が効かないので、いろいろ動かない。

apache 2 系の worker mpm とか標準設定だと起動時に 500MB 確保するので当然起動すらしない。

もう LD_PRELOAD で malloc を MAP_SHARED な mmap(2) にマップする共有ライブラリ作ろうかなぁ。なんという劣化再発明。

OpenVZ 系の VM の二重苦 - kazuhoのメモ置き場

と書いたわけですが、これを実現する morememory.so という共有ライブラリを作った (/platform/linux/morememory – CodeRepos::Share – Trac)。LD_PRELOAD なので、以下のような感じで、既存のプログラムを再コンパイルせずに使える。

% gcc -fPIC -Wall -g -O -shared morememory.c -lpthread -ldl -o morememory.so
% sudo cp morememory.so /usr/local/lib
% LD_PRELOAD=morememory.so perl tiarra

cpantiarra といったコマンドの立ち上げ直後だと、7割以上もメモリ (RSS) を節約できていて幸せ。

実行プログラム ライブラリ非使用時 ライブラリ使用時 節約率
cpan 8436 2048 75.7%
tiarra 11032 2428 78.0%

OpenVZ 系のコンテナでは全プロセスの RSS の総和に対して上限値が決められているようなので、以下のような設計にした。

  • __morecore (≒sbrk) をスパースな一時ファイルへの mmap に差し替え
  • pthread_atfork を使って fork 時にヒープを複製
  • read や select 等、ブロックしそうな関数が呼ばれるタイミングで munmap → mmap
    • ヒープのRSSをいったん解放するため

当然、実行速度は遅くなるし nasty hack なので一般にオススメはできませんが、OpenVZ 系の環境で、大きなプログラムや (ほとんど寝ているだけの) サーバを多数動かす際に便利だと思われ。特にサーバ系は、select や read で寝ている間メモリをほとんど消費しなくなるので、効果が大きい。