Redisコード読解心得

5524 ワード

1.必要な場所でエラーログを打つだけで、次々と投げ出す必要はありません.多くのエラーは現在の関数で明確です.一般的なC操作は、戻ると判断する必要はありません.cプログラムは、2つの場所だけが間違っている可能性があります.ファイルは読み書き2を開く.メモリ申請
例えばこんなところ
server.clients = listCreate();

ListCreate内部にメモリ申請がありますが、クライアントがNULLであるかどうかを判断する必要はありません.後でクライアントを使用した場所でnullにアクセスした場合、メモリアクセスエラーが発生し、SIGSEGV信号がトリガーされます.詳しくはhttp://blog.ddup.us/?p=89
2.マクロを提供して構造オブジェクトにアクセスし、実行効率とコード集約性のバランスを保つ
typedef struct list {
    listNode *head;
    listNode *tail;
    void *(*dup)(void *ptr);
    void (*free)(void *ptr);
    int (*match)(void *ptr, void *key);
    unsigned int len;
} list;

/* Functions implemented as macros */
#define listLength(l) ((l)->len)
#define listFirst(l) ((l)->head)
#define listLast(l) ((l)->tail)

3.ハッシュテーブルの実装
http://huangz.iteye.com/blog/1455808
ここでは比較的深く説明されているコード説明があり、redisの実現の特色は屋台式のrehashであり、一度にデータを操作しすぎることを避けることにある.
特筆すべきは、この文章の中で
唯一の残念なことにdicthtは辞書の内部実装として隠されていたはずが、今では完全に明らかになった.
実は第2条を見て、内部実装を暴露して同時にマクロを通じてデータにアクセスして、効率を高めることができて、関数呼び出しがオーバーヘッドがあるためです.
「内部実装を隠す」はオブジェクト向けの教条ですが、複数の人が協力している間にデータがアクセスできないことを知らない人がいるのではないかと心配しています.
コードを書く人が直接オブジェクトのデータを操作するのではなく、関数やマクロを通じてオブジェクトにアクセスすることを約束すれば、パッケージの原則は満たされます.
これはjavaの利点が「頼りない人が頼りないコードを書くことを制限する」ことにあることを改めて証明した.
また、hashテーブルのサイズについて
typedef struct dictht {
    dictEntry **table;      //       
    unsigned long size;     //     
    unsigned long sizemask; // mask  ,        
    unsigned long used;     //       
} dictht;

ここでsizeはバケツの数であり,redisの実装ではこの数を2^n,すなわちhashテーブルが拡張されるたびにバケツの数を4,8,16,32というインクリメンタルシーケンスとする.どうしてそう決めたの?
コードを見続けます
n.sizemask = realsize-1;

h = dictHashKey(d, de->key) & d->ht[1].sizemask;

すなわち、sizemaskは常にsize-1、sizeは2^nである、sizemaskの2進数は11111となる.(n個1).
一般に、1つのノードのhash値を計算した後、この(hash値%ポインタ配列の長さ)を、このノードを配列のいくつかの要素がヘッダポインタであるチェーンテーブルに置くべきであると得られる.redisの聡明さは%の操作を避けることであり、迅速な&操作で代替されている.
X & (2^n -1) == X % 2^n

X/2はこの2進数のXをnビット右にシフトすることに相当し、X%2はXの最も低いNビットをとる値に相当し、例えば
9 / 4 = 01001 >> 2 = 010 = 2

9 % 4 = 01001 & 011 = 01 = 1

このうち01000は4で割り切れる部分、01は余りの部分です.これにより、%の操作を回避するために、迅速に値を求めることができます.
引き続き,hashテーブルの様々な操作は内部に2つのテーブルがあることを両立させ,同時に機能の直交,反復器の実現を維持し,単一スレッドの場合にのみこのようなhashテーブルを書くことができる.
4.コード仕様
プライベート関数の下線の先頭
変数と関数はアルパカ法と名付けられています(実は私はこれが好きではありません)
まずincludeパブリックヘッダファイル、その後includeプライベートヘッダファイル
5.lzf圧縮アルゴリズム
pass、アルゴリズムが全然分からないので、後で使えるかもしれません.
6.ae.cこれは専門的な話題で、インタフェースを見て、私は問題があります.
/* State of an event based program */
typedef struct aeEventLoop {
    int maxfd;
    long long timeEventNextId;
    aeFileEvent events[AE_SETSIZE]; /* Registered events */
    aeFiredEvent fired[AE_SETSIZE]; /* Fired events */
    aeTimeEvent *timeEventHead;
    int stop;
    void *apidata; /* This is used for polling API specific data */
    aeBeforeSleepProc *beforesleep;
} aeEventLoop;


/* Prototypes */
aeEventLoop *aeCreateEventLoop(void);  /*   ,      */

void aeDeleteEventLoop(aeEventLoop *eventLoop); /*   ,      */


void aeStop(aeEventLoop *eventLoop); /* 1.       ? */


int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
        aeFileProc *proc, void *clientData); /*         ,         fd,2.         ? */


void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask); /*     ,3. mask     ? */


int aeGetFileEvents(aeEventLoop *eventLoop, int fd); /* 4. get    ,    ? */


long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
        aeTimeProc *proc, void *clientData,
        aeEventFinalizerProc *finalizerProc); /* 5.final       ? */


int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id); /*    id     ,    */


int aeProcessEvents(aeEventLoop *eventLoop, int flags); /*6.    */


int aeWait(int fd, int mask, long long milliseconds); /* 7.   */



void aeMain(aeEventLoop *eventLoop); /* 8.          */


char *aeGetApiName(void); /* 9.    */


void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep); /*    sleep       ,10.         sleep? */

閲覧完了ae.cその後、私はこれらの質問に答えます.
6.1簡単にeventLoop->stop=1;イベントループを終了するためにaeMainで判断します
    while (!eventLoop->stop) {
        if (eventLoop->beforesleep != NULL)
            eventLoop->beforesleep(eventLoop);
        aeProcessEvents(eventLoop, AE_ALL_EVENTS);
    }

6.2 eventLoop->events[fd]は、あるイベントを格納するためのものであり、フラグビットの機能はなく、epoll(epollを使用すると仮定)は、イベントの配列を返すので、遍歴する必要がある問題を心配する必要はありません.通常のファイルでも使用できます.6.11クライアントコマンドの境界をどのように区別するかという問題を引き起こします.
6.3 maxfdはprocessで使われています.maxfdが-1であれば、現在登録されていない記述子を説明し、処理しなくてもいいです.
if (eventLoop->maxfd != -1 ||
        ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {

maskは減算に使用され、清0ではなく、減算される可能性があります.
6.4は、対応するfdのmaskを返します.
6.5 aeDeleteTimeEventで呼び出すために使用されますが、実際の例は分かりません.
6.6 waitが完了した後、すでに発生したファイルイベント、または時間イベントを処理する.この関数はまず時間イベントのチェーンテーブルを巡り,最近処理する必要がある時間イベントを見つけ,その後,将来の時間−現在の時間をwaitのパラメータ(n秒待ち)とし,waitが終了した後に読み書きを実行するイベントである.同時にTimeEventのチェーンテーブルを巡り、現在古いTimeEventを探して実行します.
6.7 selectの簡単な包装、syncioに用いる.c,指定時間内に相関関数を同期読み書きする.
6.8イベントの主循環関数を起動します.唯一の問題はeventLoop->beforesleep(eventLoop)です.実際に6.12の使い方がわかりません
6.9 apiの名前、epoll、selectまたはkqueueを返し、クライアントへのINFOコマンド表示用
6.10プログラムはepollなどの方法でイベントの発生を待つ. 
6.11
6.12
一体に値するのは、ae_epoll.cには水平トリガ(LT)が用いられており,エッジトリガ(ET)ではなく,効率はやや低い.何の意味があるのか分からない.