redis --持久化rio


       rio是对流式IO的抽象,提供读写接口,消费/生产具体不同的I/O设备。rdb.c就是使用抽象封装RDB的内存读写和文件读写。rio对象提供以下方法:

        read:从流读数据

        write:向流写数据

        tell :获取当前的偏移量

        flush : 刷空缓冲区方法

        checksum:检查读写的checksum 校验和操作。rio使用了RCR64算法计算校验和,具体实现可以参看crc64.h和crc64.c文件。

       IO变量。_rio中的io成员是一个联合体,针对不同的I/O情况进行不同的处理:当执行内存buffer的I/O操作时,使用rio.buffer结构体;当执行文件I/O操作时,使用rio.file结构体;当执行socket的I/O操作时,使用rio.fdset结构体。

 

/* rio.c is a simple stream-oriented I/O abstraction that provides an interface
 * to write code that can consume/produce data using different concrete input
 * and output devices. For instance the same rdb.c code using the rio
 * abstraction can be used to read and write the RDB format using in-memory
 * buffers or files.
 *
 * A rio object provides the following methods:
 *  read: read from stream.
 *  write: write to stream.
 *  tell: get the current offset.
 *
 * It is also possible to set a 'checksum' method that is used by rio.c in order
 * to compute a checksum of the data written or read, or to query the rio object
 * for the current checksum.

// 封装成对象
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. */
	// 0表示错误,非0表示成功
    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 *);
	// flush ??
    int (*flush)(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. */
    union {
        /* In-memory buffer target. */
		// 内存缓冲区buffer结构体
        struct {
			// buffer中的内容,实际就是char数组
            sds ptr;
			// 偏移量
            off_t pos;
        } buffer;
        /* Stdio file pointer target. */
		// 文件结构体
        struct {
			// 打开的文件句柄
            FILE *fp;
			// 最后一个fsync后写入的字节数 ??
            off_t buffered; /* Bytes written since last fsync. */
			// 多少个字节后执行自动fsync
            off_t autosync; /* fsync after 'autosync' bytes written. */
        } file;
        /* Multiple FDs target (used to write to N sockets). */
		// 可以对应多个socket
        struct {
			// 文件描述符
            int *fds;       /* File descriptors. */
			// 每个socket的状态,0表示正常
            int *state;     /* Error state of each fd. 0 (if ok) or errno. */
			// 数量
            int numfds;
			// 处理到哪里了
            off_t pos;
			// 字符数组
            sds buf;
        } fdset;
    } io;
};

         读、写、tell、flush 4个方法,具体实现并未见到,代码如下:

/* 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. */

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;
		// 如果update_cksum不为空,调用指针函数;指针函数做什么,未找到??
        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;
		// processed_bytes,就是一个计数器不管读、写都用到了;
        r->processed_bytes += bytes_to_write;
    }
    return 1;
}

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;
}

// tell & flush具体在哪定义要去子类看看
static inline off_t rioTell(rio *r) {
    return r->tell(r);
}

static inline int rioFlush(rio *r) {
    return r->flush(r);
}


        上面是rio.h文件,下面介绍rio.c源码,下面这行代码解释了rio.h文件的tell、write、read、flush的具体实现;用指针函数实现抽象和实现的分离。

static const rio rioBufferIO 

/* ------------------------- Buffer I/O implementation ----------------------- */

/* Returns 1 or 0 for success/failure. */
static size_t rioBufferWrite(rio *r, const void *buf, size_t len) {
	// io.buffer.ptr 指向sds的字符串,指针前面8个字节是长度
    r->io.buffer.ptr = sdscatlen(r->io.buffer.ptr,(char*)buf,len);
    r->io.buffer.pos += len;
    return 1;
}

/* Returns 1 or 0 for success/failure. */
static size_t rioBufferRead(rio *r, void *buf, size_t len) {
	// 没有存储那么多字节,留个问题 pos咋ptr的左边还是右边
    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);
	// 不论读写,pos都向后移动
    r->io.buffer.pos += len;
    return 1;
}

/* Returns read/write position in buffer. */
static off_t rioBufferTell(rio *r) {
    return r->io.buffer.pos;
}

/* Flushes any buffer to target device if applicable. Returns 1 on success
 * and 0 on failures. */
static int rioBufferFlush(rio *r) {
	// 不支持的方法,估计实现只打印日志
    REDIS_NOTUSED(r);
    return 1; /* Nothing to do, our write just appends to the buffer. */
}

static const rio rioBufferIO = {
    rioBufferRead,
    rioBufferWrite,
    rioBufferTell,
    rioBufferFlush,
    NULL,           /* update_checksum */
    0,              /* current checksum */
    0,              /* bytes read or written */
    0,              /* read/write chunk size */
    { { NULL, 0 } } /* union for io-specific vars */	// 貌似没啥用,后面留意下到底什么意思
};

// 处理化一个rio对象
void rioInitWithBuffer(rio *r, sds s) {
    *r = rioBufferIO;
    r->io.buffer.ptr = s;
    r->io.buffer.pos = 0;
}

           文件IO的实现:其中aof_fsync函数的实现在读aof的时候再看

/* --------------------- Stdio file pointer implementation ------------------- */

/* Returns 1 or 0 for success/failure. */
static size_t rioFileWrite(rio *r, const void *buf, size_t len) {
    size_t retval;
	
	//返回值:返回实际写入的数据块数目
	//(1)buffer:是一个指针,对fwrite来说,是要获取数据的地址;
	//(2)size:要写入内容的单字节数;
	//(3)count:要进行写入size字节的数据项的个数;
	//(4)stream:目标文件指针;
	//(5)返回实际写入的数据项个数count。
    retval = fwrite(buf,len,1,r->io.file.fp);
    r->io.file.buffered += len;

	// autosync大于0,并且buffered大于等于autosync,自动刷新
    if (r->io.file.autosync &&
        r->io.file.buffered >= r->io.file.autosync)
    {	//flush()会强迫将缓冲区内的数据写回参数stream 指定的文件中. 如果参数stream 为NULL,fflush()会将所有打开的文件数据更新.
        fflush(r->io.file.fp);
		// 函数原型:int _fileno( FILE *stream );
		// 函数功能:fileno()用来取得参数stream指定的文件流所使用的文件描述符
        aof_fsync(fileno(r->io.file.fp));
		// reset
        r->io.file.buffered = 0;
    }
    return retval;
}

/* Returns 1 or 0 for success/failure. */
static size_t rioFileRead(rio *r, void *buf, size_t len) {
	// buffer  用于接收数据的内存地址
	// size 要读的每个数据项的字节数,单位是字节
	// count 要读count个数据项,每个数据项size个字节.
	// stream 输入流
	// 返回值 返回真实读取的项数,若大于count则意味着产生了错误
    return fread(buf,len,1,r->io.file.fp);
}

/* Returns read/write position in file. */
static off_t rioFileTell(rio *r) {
	// The ftell() function returns the current position of the file pointer in a stream.
	// The ftello() function is identical to ftell() except for the return type.
	// The ftello64() function is identical to the ftello() function except that it is able to return file offsets that are greater than 2 gigabytes. The ftello64() function is a part of the large file extensions.

    return ftello(r->io.file.fp);
}

/* Flushes any buffer to target device if applicable. Returns 1 on success
 * and 0 on failures. */
static int rioFileFlush(rio *r) {
    return (fflush(r->io.file.fp) == 0) ? 1 : 0;
}

static const rio rioFileIO = {
    rioFileRead,
    rioFileWrite,
    rioFileTell,
    rioFileFlush,
    NULL,           /* update_checksum */
    0,              /* current checksum */
    0,              /* bytes read or written */
    0,              /* read/write chunk size */
    { { NULL, 0 } } /* union for io-specific vars */
};

void rioInitWithFile(rio *r, FILE *fp) {
    *r = rioFileIO;
    r->io.file.fp = fp;
    r->io.file.buffered = 0;
    r->io.file.autosync = 0;
}


        另有四个bulk方法 2个general方法

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);

void rioGenericUpdateChecksum(rio *r, const void *buf, size_t len);
void rioSetAutoSync(rio *r, off_t bytes);


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值