Redis zmalloc

9832 ワード



#
演算子は文字列を作成するために使用されます.#演算子の後ろには、次のようなパラメータ(中央にスペースまたはTabがあります).
#define STR(s) # s
STR(hello 	world)
cpp命令で前処理された後は"hello␣world"で、自動的に"号で実パラメータを1文字列に囲み、実パラメータの連続する複数の空白文字が1つのスペースに置き換えられる.
たとえば、
#define STR(s) #s
fputs(STR(strncmp("ab\"c\0d", "abc", '\4"')
	== 0) STR(: @
), s);

前処理後はfputs("strncmp(\"ab\\\"c\\0d\", \"abc\", '\\4\"') == 0" ": @
", s);
であるが、実パラメータに文字定数または文字列が含まれている場合、マクロ展開後の文字列の定義子"\"に置き換えられ、文字定数または文字列の\および"\\および\"に置き換えられる.
マクロ定義では、前後の2つの前処理Tokenを##演算子で1つの前処理Tokenに接続することができ、#演算子とは異なり、##演算子は関数式マクロ定義に限らず、変数式マクロ定義も使用できる.例:
#define CONCAT(a, b) a##b
CONCAT(con, cat)

前処理後はconcatであった.たとえば、1つのマクロを2つの#番に展開するように定義します.
#define HASH_HASH # ## #

真ん中の##は演算子で、マクロ展開時に前後の2つの#号がこの演算子に接続されています.注意中央の2つのスペースは少なくありません.####と書くと####の2つのTokenに分割され、定義##演算子によって前後の2つの前処理Tokenを接続するために使用され、マクロ定義の先頭または末尾に表示されないため、エラーが発生します.
まず、C言語のマクロではネストが許容され、そのネスト後、一般的な展開法則は関数のパラメータのように、まずパラメータを展開し、関数を分析するので、展開順序は内から外になるが、マクロに#があればパラメータを展開しない.マクロに##があれば、まず関数を展開し、中のパラメータを展開する.以下の例:include
#define TO_STRING2( x ) #x #define TO_STRING( x ) TO_STRING1( x ) #define TO_STRING1( x ) #x #define PARAM( x ) #x #define ADDPARAM( x ) INT_##x int main() {     const char * str = TO_STRING(PARAM( ADDPARAM( 1 ) ) );     printf("%s",str);     str = TO_STRING2(PARAM( ADDPARAM( 1 ) ) );     printf("%s",str);     return 1; }その出力結果は、「ADDPARAM(1)」PARAM(ADDPARAM(1))がマクロTO_STRINGは、その定義に#がないので、先に奥の「PARAM(ADDPARAM(1)」を展開し、PARAMに#があるので奥の展開の結果がADDPARAM(1)となり、それから外の展開となり、その結果が「ADDPARAM(1)」となり、TO_STRING 2は、その定義に#があるので直接展開する、その結果、PARAM(ADDPARAM(1))であり、以下の例について、#include #define TO_STRING2( x ) a_##x #define TO_STRING( x ) TO_STRING1( x ) #define TO_STRING1( x ) #x #define PARAM( x ) #x #define ADDPARAM( x ) INT_##x int main() {     str = TO_STRING(TO_STRING2(PARAM( ADDPARAM( 1 ) ) ));     printf("%s",str);     return 1; } その出力結果は:a_PARAM( INT_1 )
まずTO_を分析するのでSTRINGのパラメータTO_STRING 2(PARAM(ADDPARAM(1))は、TO_STRING 2(x)は、その定義に##があるので、先にこの関数を展開し、その結果a_PARAM(ADDPARAM(1)),ADDPARAM(1)展開,結果はINT_1であるため、その総結果はa_PARAM( INT_1 ) 
HAVE_MALLOC_SIZEは、システムに関数があるかどうかを決定するために使用されるmalloc_size.
redis_malloc_sizeの機能は、パラメータpが指すメモリブロックのサイズを取得することである.しかしlibcのfreeは確かにptrの実際の大きさを知ることができますが、私たちのプログラムは知りません.ポインタが指す最初のいくつかのBに基づいて、確定するしかありません.
void * calloc ( size_t num, size_t size ); // zcalloc(size_t size);       ,        0     。

Allocate space for array in memory Allocates a block of memory for an array of 
num elements, each of them 
size bytes long, and initializes all its bits to zero.//mallocは0をクリアしていません
The effective result is the allocation of an zero-initialized memory block of (
num * 
size) bytes.
void * realloc ( void * ptr, size_t size );

Reallocate memory block The size of the memory block pointed to by the 
ptr parameter is changed to the 
size bytes,
 expanding or reducing the amount of memory available in the block.
The function may 
move the memory block to a new location, in which case the new location is returned. The content of the memory block is preserved up to the lesser of the new and old sizes(パラメータが小さくなるためsizeサイズを縮小するのではなく、blockが移動してもreallocは裁断しない)、even if the block is moved.
 If the new size is larger, the value of the newly allocated portion is indeterminate.
              ,          .           .               ,            .realloc              .          NULL   .      .   p           .

In case that 
ptr is NULL, the function behaves exactly as  malloc, assigning a new block of 
size bytes and returning a pointer to the beginning of it.
In case that the 
size is 0, the memory previously allocated in 
ptr is deallocated as if a call to  free was made, and a NULL pointer is returned.
Redisではメモリ割り当て操作があちこちで行われています.異なるプラットフォーム間の差異を遮断し、メモリの使用量を統計するために、Redisはメモリ分配関数をカプセル化し、プログラムではzmalloc、zfreeの一連の関数を統一的に使用し、zmallocに位置する.h,zmalloc.c文中.
前述したように、パッケージは、下位プラットフォームの違いを遮断し、関連する統計関数を自分で実現するのに便利である.具体的には、
GoogleのTC_がシステムに存在する場合MALLOCライブラリでは、tc_を使用します.mallocファミリー関数は、元のmallocファミリー関数の代わりに使用されます.
現在のシステムがMacシステムである、のメモリ割り当て関数.
その他の場合、各セグメントに割り当てられた空間の先頭に、割り当てられた空間サイズを記録するために一定の長さのフィールドが同時に割り当てられる.
なぜならtc_mallocおよびMacプラットフォームの下のmalloc関数ファミリーは、割り当てられた空間サイズを計算する関数(それぞれtc_malloc_sizeおよびmalloc_size)を提供するので、空間レコードサイズを個別に割り当てる必要はありません.linuxおよびsunプラットフォームでは、割り当てられた空間サイズを記録します.linuxの場合、sizeof(size_t)を使用してフィールドレコードを固定します.sun osの場合、sizeof(long long)を使用してフィールドレコードを固定します.つまり上のソースのPREFIX_SIZEマクロ.
size_tは標準Cライブラリで定義されており、unsigned intである必要があります.(OSビット数によって異なる)は、申請時に、1つのPREFIX空間サイズ格納申請のサイズを複数申請するために用いられ、申請空間のヘッダPREFIXサイズ格納申請量は、ptrがPREFIXの後ろのアドレスを外部に指す.
では、この記録は何の役に立つのでしょうか.答えは、現在のプロセスがどれだけのメモリを消費しているかを統計するためです.zmalloc.cには、このような静的変数があります.
static size_t used_memory = 0;

プロセスが現在使用しているメモリの合計数を記録します.この変数は、メモリを割り当てたり解放したりするたびに更新されます.メモリを割り当てるとき、どのくらいのメモリを割り当てるかを明確に知ることができるからです.ただし、メモリを解放する場合、(malloc_size関数が提供されていないプラットフォームについて)メモリを解放するポインタを指すだけでは解放する空間の大きさが分からない.この場合、上記のPREFIX_SIZEの長さ設定フィールドが機能し、その中に記録されている内容から空間の大きさを得ることができる.zmalloc関数は以下のように(無関係コードを除く):
反発ロックpthread_mutex_tの使用、
LinuxThreads実装ではpthread_mutex_tは構造であり、PTHREAD_MUTEX_INITIALIZERは構造定数です.
 
       

update_zmalloc_stat_alloc(__n,__size) 和 update_zmalloc_stat_free(__n) 这两个宏负责在分配内存或是释放内存的时候更新used_memory变量。定义成宏主要是出于效率上的考虑。将其还原为函数,就是下边这个样子:

void update_zmalloc_stat_free(__n)
{
    do {
        size_t _n = (__n);
        if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1));
        if (zmalloc_thread_safe) {
            pthread_mutex_lock(&used_memory_mutex);
            used_memory -= _n;
            pthread_mutex_unlock(&used_memory_mutex);
        } else {
            used_memory -= _n;
        }
    } while(0)
}

コードにはused_の更新を除くmemory変数のほかに、いくつか注目すべき点があります.
先対_nの低位向上、最後に_nはsizeof(long)の倍数になり、例えば32ビットシステムに対してsizeof(long)==100(バイナリ),n上向きに整列すると,下位2桁とも0になる.
なぜなら、実際の申請量では、OSはメモリの位置合わせであることが多いからです.
メモリの位置合わせのメリット:
1、アドレス線の節約
2,cache最適化.(4 Bの整数倍)
プロセスに複数のスレッドが存在する場合は、変数を更新するときにロックします.//バックグラウンドスレッドは、バックグラウンドスレッドが何個あるかを変更する必要があります.
zmalloc関数には、更新する統計量もあります:zmalloc_allocations[ ].
もう1つのメモリ使用量の統計はzmalloc_を呼び出すことによってused_memory関数は次のように返します.
size_t zmalloc_used_memory(void) {
    size_t um;

    if (zmalloc_thread_safe) pthread_mutex_lock(&used_memory_mutex);
    um = used_memory; // used_memory         。
    if (zmalloc_thread_safe) pthread_mutex_unlock(&used_memory_mutex);
    return um;
}

またzmalloc.cでは、異なるシステムに対してzmalloc_も実現されているget_rss関数は、linuxシステムで/proc/$pid/statファイルを読み込むことでシステム統計のメモリ使用量を取得します./proc仮想ファイルシステムについては、前の記事を参照してください.
RSSリターン
OSで戻れない場合はused_しか戻らないmomory_mutexで推定します.
/*Fragmentation=RSS/allocated-bytes*/フラグメント比=RSS/有効データを返します.
     70 float zmalloc_get_fragmentation_ratio(void) {
     71     return (float)zmalloc_get_rss()/zmalloc_used_memory(); used_とはmemory,redisはzmalloc,zfreeで統計されたスタック上のメモリを統一する.
     72 }
      1  /* WARNING: the function zmalloc_get_rss() is not designed to be fast
      2  * and may not be called in the busy loops where Redis tries to release
      3  * memory expiring or swapping out objects.
      4  *
      5  * For this kind of "fast RSS reporting" usages use instead the
      6  * function RedisEstimateRSS() that is a much faster (and less precise)
      7  * version of the funciton. */
     12 size_t zmalloc_get_rss(void) {
     13     int page = sysconf(_SC_PAGESIZE);
     14     size_t rss;
     15     char buf[4096];
     16     char filename[256];
     17     int fd, count;
     18     char *p, *x;
     19     snprintf(filename,256,"/proc/%d/stat",getpid()); //     
     20     if ((fd = open(filename,O_RDONLY)) == -1) return 0;
     21     if (read(fd,buf,4096) <= 0) { //    
     22         close(fd);
     23         return 0;
     24     }
     25     close(fd);
     26     p = buf;
     27     count = 23; /* RSS is the 24th field in /proc/<pid>/stat */
         /*
               

Locate first occurrence of character in string

Returns a pointer to the first occurrence of character in the C string str. The terminating null-character is considered part of the C string. Therefore, it can also be located to retrieve a pointer to the end of a string. 28 while(p && count--) { 29 p = strchr(p,' '); 30 if (p) p++; 31 } 32 if (!p) return 0;//23 33 x = strchr(p,' '); // 34 if (!x) return 0; 35 *x = '\0'; , 36 rss = strtoll(p,NULL,10); // long long ,10 ? 37 rss *= page;// 。 38 return rss; 39 }