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