Redisソース分析(二十七)---rioシステムI/Oのパッケージ


I/O操作はシステムごとに不可欠な部分です.またI/O操作の良し悪しは,ある程度システムの効率に影響を及ぼす.今日私はRedisの中のI/Oがどのように処理したのかを学びました.同じように、Redisは彼自身のシステムの中で、I/O層もパッケージされています.略称RIO.まずRIOに何があるか見てみましょう.
struct _rio {
    /* Backend functions.
     * Since this functions do not tolerate short writes or reads the return
     * value is simplified to: zero on error, non zero on complete success. */
    /*         */
    size_t (*read)(struct _rio *, void *buf, size_t len);
    /*         */
    size_t (*write)(struct _rio *, const void *buf, size_t len);
    /*            */
    off_t (*tell)(struct _rio *);
    /* The update_cksum method if not NULL is used to compute the checksum of
     * all the data that was read or written so far. The method should be
     * designed so that can be called with the current checksum, and the buf
     * and len fields pointing to the new block of data to add to the checksum
     * computation. */
    /*            ,          */
    void (*update_cksum)(struct _rio *, const void *buf, size_t len);

    /* The current checksum */
    /*        */
    uint64_t cksum;

    /* number of bytes read or written */
    /*               */
    size_t processed_bytes;

    /* maximum single read or write chunk size */
    /*            */
    size_t max_processing_chunk;

    /* Backend-specific vars. */
    /* rio I/O   */
    union {
    	//buffer   
        struct {
        	//buffer    
            sds ptr;
            //   
            off_t pos;
        } buffer;
        //     
        struct {
            FILE *fp;
            off_t buffered; /* Bytes written since last fsync. */
            //       
            off_t autosync; /* fsync after 'autosync' bytes written. */
        } file;
    } io;
};
には3つの必須の方法、read、writeの方法、オフセット量を取得するtellの方法、2つの構造体変数、1つのbuffer構造体、1つのfile構造体があり、著者らは異なるI/O状況に対して、異なる処理を行い、一時的なI/O操作を実行するとき、rioと同じである.bufferは、ファイルとI/O操作を行う場合にrioを実行する.file間の操作.rio統一定義の読み書き方法を見てみましょう.
/* The following functions are our interface with the stream. They'll call the
 * actual implementation of read / write / tell, and will update the checksum
 * if needed. */
/* rio     */
static inline size_t rioWrite(rio *r, const void *buf, size_t len) {
    while (len) {
    	//                  
        size_t bytes_to_write = (r->max_processing_chunk && r->max_processing_chunk < len) ? r->max_processing_chunk : len;
        //       ,     
        if (r->update_cksum) r->update_cksum(r,buf,bytes_to_write);
        //     
        if (r->write(r,buf,bytes_to_write) == 0)
            return 0;
        buf = (char*)buf + bytes_to_write;
        len -= bytes_to_write;
        //       
        r->processed_bytes += bytes_to_write;
    }
    return 1;
}

/* rio     */
static inline size_t rioRead(rio *r, void *buf, size_t len) {
    while (len) {
    	//                  
        size_t bytes_to_read = (r->max_processing_chunk && r->max_processing_chunk < len) ? r->max_processing_chunk : len;
        //     
        if (r->read(r,buf,bytes_to_read) == 0)
            return 0;
        //    ,     
        if (r->update_cksum) r->update_cksum(r,buf,bytes_to_read);
        buf = (char*)buf + bytes_to_read;
        len -= bytes_to_read;
        r->processed_bytes += bytes_to_read;
    }
    return 1;
}
ここでは、データが変更されるたびに、Redisは計算チェックサムの処理アルゴリズムを行い、データ操作の変更動作を示しています.使用するアルゴリズムは、CRC 64アルゴリズムを紹介したもので、RIOのbuffer IOとFile IOに対して、Redisは2つのRIO構造体を定義しています.
/*          ,   BufferRio */
static const rio rioBufferIO = {
    rioBufferRead,
    rioBufferWrite,
    rioBufferTell,
    NULL,           /* update_checksum */
    0,              /* current checksum */
    0,              /* bytes read or written */
    0,              /* read/write chunk size */
    { { NULL, 0 } } /* union for io-specific vars */
};

/*          ,   FileRio */
static const rio rioFileIO = {
    rioFileRead,
    rioFileWrite,
    rioFileTell,
    NULL,           /* update_checksum */
    0,              /* current checksum */
    0,              /* bytes read or written */
    0,              /* read/write chunk size */
    { { NULL, 0 } } /* union for io-specific vars */
};
では、bufferのReadメソッドやFileのReadメソッドなど、対応する読み書きメソッドがそれぞれ定義されています.
/* Returns 1 or 0 for success/failure. */
/*   rio  buffer         */
static size_t rioBufferRead(rio *r, void *buf, size_t len) {
    if (sdslen(r->io.buffer.ptr)-r->io.buffer.pos < len)
        return 0; /* not enough buffer to return len bytes. */
    memcpy(buf,r->io.buffer.ptr+r->io.buffer.pos,len);
    r->io.buffer.pos += len;
    return 1;
}
/* Returns 1 or 0 for success/failure. */
/*   rio  fp     */
static size_t rioFileRead(rio *r, void *buf, size_t len) {
    return fread(buf,len,1,r->io.file.fp);
}
で機能するrioのオブジェクト変数は異なり、最後にRedisの宣言で4つの異なるタイプのデータの書き込み方法が与えられた.
/* rio          ,      riowrite   */
size_t rioWriteBulkCount(rio *r, char prefix, int count);
size_t rioWriteBulkString(rio *r, const char *buf, size_t len);
size_t rioWriteBulkLongLong(rio *r, long long l);
size_t rioWriteBulkDouble(rio *r, double d);
は、そのうちの1つの方法を実装する.
/* Write multi bulk count in the format: "*<count>\r
". */ /* rio , riowrite */ size_t rioWriteBulkCount(rio *r, char prefix, int count) { char cbuf[128]; int clen; cbuf[0] = prefix; clen = 1+ll2string(cbuf+1,sizeof(cbuf)-1,count); cbuf[clen++] = '\r'; cbuf[clen++] = '
'; if (rioWrite(r,cbuf,clen) == 0) return 0; return clen; }
で呼び出されたrioWriteメソッドか、buffer IOかFile IOかを定義します.それぞれにそれぞれ違う実現があるだけだ.ファイルのwriteメソッドでは、rioにコンテンツを読み込むときの詳細があります.file.bufferの場合、bufferは所定の同期最小バイトを超えており、bufferコンテンツをファイルにリフレッシュする必要があります.
/* Returns 1 or 0 for success/failure. */
/*  buf  rio  file    */
static size_t rioFileWrite(rio *r, const void *buf, size_t len) {
    size_t retval;

    retval = fwrite(buf,len,1,r->io.file.fp);
    r->io.file.buffered += len;

    if (r->io.file.autosync &&
        r->io.file.buffered >= r->io.file.autosync)
    {
    	//        
        fflush(r->io.file.fp);
        aof_fsync(fileno(r->io.file.fp));
        r->io.file.buffered = 0;
    }
    return retval;
}