#ifndef __REDIS_RIO_H
#define __REDIS_RIO_H
#include <stdio.h>
#include <stdint.h>
#include "sds.h"
#include "connection.h"
#define RIO_FLAG_READ_ERROR (1<<0) rio读错误标志整数1
#define RIO_FLAG_WRITE_ERROR (1<<1) rio写错误标志整数2
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 *); 获取当前流指向的位置函数指针
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 and flags (see RIO_FLAG_*) */ 当前的校验和 和 标志
uint64_t cksum, flags;
/* 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. */ 目标是内存缓冲区
struct {
sds ptr;
off_t pos;
} buffer;
/* Stdio file pointer target. */ 目标是标准的输入输出文件指针
struct {
FILE *fp;
off_t buffered; /* Bytes written since last fsync. */ 自从上次同步到磁盘后的写入数据
off_t autosync; /* fsync after 'autosync' bytes written. */ 满足操作一次fsync的写入字节数
} file;
/* Connection object (used to read from socket) */连接对象(用来从套接字读取)
struct {
connection *conn; /* Connection */ 连接
off_t pos; /* pos in buf that was returned */返回缓存数据中的偏移位置
sds buf; /* buffered data */ 缓存数据
size_t read_limit; /* don't allow to buffer/read more than that */ 允许最大缓存或者读取的大小
size_t read_so_far; /* amount of data read from the rio (not buffered) */ 截止当下从Rio读取数据总量的大小(不包括缓存)
} conn;
/* FD target (used to write to pipe). */文件句柄目标(用来写入管道)
struct {
int fd; /* File descriptor. */ 文件描述符
off_t pos; 偏移量,同上即返回缓存数据中的偏移位置
sds buf; 缓冲数据
} fd;
} io;
};
typedef struct _rio 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. */
下面的函数是我们同流交互的接口。它们调用实际的 读/写/查的实现,如果有需要也会更新校验和
static inline size_t rioWrite(rio *r, const void *buf, size_t len) {
if (r->flags & RIO_FLAG_WRITE_ERROR) return 0; 如果有写入错误,返回
while (len) { 写入长度不为0,一直写
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) { 写入出错
r->flags |= RIO_FLAG_WRITE_ERROR;
return 0;
}
buf = (char*)buf + bytes_to_write; 正常,缓冲区的数据指向写入数据长度之后的位置
len -= bytes_to_write; 等待写入的长度 减少 本次写入的长度
r->processed_bytes += bytes_to_write; 已处理的数据 加上 本次写入的长度
}
return 1; 正常返回非0,即1
}
static inline size_t rioRead(rio *r, void *buf, size_t len) {
if (r->flags & RIO_FLAG_READ_ERROR) return 0; 存在读数据错误,立即返回
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) { 读取数据错误
r->flags |= RIO_FLAG_READ_ERROR;
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;
}
返回流的当前位置
static inline off_t rioTell(rio *r) {
return r->tell(r);
}
刷新数据到磁盘
static inline int rioFlush(rio *r) {
return r->flush(r);
}
/* This function allows to know if there was a read error in any past
* operation, since the rio stream was created or since the last call
* to rioClearError(). */
这个函数可以让我们知道,自从Rio数据流被创建或者自从上次调用函数rioClearError之后,在过去这些操作中是否有读取错误。
因为有错误就会设置错误标志
static inline int rioGetReadError(rio *r) {
return (r->flags & RIO_FLAG_READ_ERROR) != 0;
}
/* Like rioGetReadError() but for write errors. */
类似函数rioGetReadError,但是是用来判断写入错误的
static inline int rioGetWriteError(rio *r) {
return (r->flags & RIO_FLAG_WRITE_ERROR) != 0;
}
清除错误标志
static inline void rioClearErrors(rio *r) {
r->flags &= ~(RIO_FLAG_READ_ERROR|RIO_FLAG_WRITE_ERROR);
}
void rioInitWithFile(rio *r, FILE *fp); 初始化rio的文件指针
void rioInitWithBuffer(rio *r, sds s); 初始化rio的缓存区
void rioInitWithConn(rio *r, connection *conn, size_t read_limit); 初始化RIO套接字连接
void rioInitWithFd(rio *r, int fd); 初始化RIO文件描述符
void rioFreeFd(rio *r); 释放RIO文件描述符
void rioFreeConn(rio *r, sds* out_remainingBufferedData); 释放RIO套接字连接
size_t rioWriteBulkCount(rio *r, char prefix, long count); rio写入整数
size_t rioWriteBulkString(rio *r, const char *buf, size_t len); rio写入字符串
size_t rioWriteBulkLongLong(rio *r, long long l); rio写入长整形
size_t rioWriteBulkDouble(rio *r, double d); rio写入浮点型
struct redisObject; 声明结构体,给下面使用
int rioWriteBulkObject(rio *r, struct redisObject *obj); 写入redis对象
void rioGenericUpdateChecksum(rio *r, const void *buf, size_t len); 正常的校验和更新
void rioSetAutoSync(rio *r, off_t bytes); 设置自动同步数据到磁盘的条件(即按照数据大小达到多少就同步数据到磁盘)
#endif
************************************************************************************************
/* 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.
rio.c是一个简单的基于流的I/O抽象,它提供了一个交互接口用来编写代码可以用不同的具体输入输出设备来消费/生产数据。
举例来说,rdb.c使用相同rio的抽象代码 能够读和写内存缓冲区或文件方式的RDB格式
* A rio object provides the following methods: 一个rio对象提供一下方法:
* 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.
rio.c通常也可能设置一个校验和的方法,用来对已读或已写数据计算校验和,或者用当前校验和查询rio对象
*/
#include "fmacros.h"
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "rio.h"
#include "util.h"
#include "crc64.h"
#include "config.h"
#include "server.h"
/* ------------------------- Buffer I/O implementation ----------------------- */
缓存I/O的实现
/* Returns 1 or 0 for success/failure. */ 对于成功/失败返回1或0
写入缓存
static size_t rioBufferWrite(rio *r, const void *buf, size_t len) {
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. */ 对于成功/失败返回1或0
从缓存读取
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.ptr+r->io.buffer.pos开始的len个字节
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. */
如果合适,将缓冲区所有的数据刷新到目标设备,成功返回1,失败返回0
static int rioBufferFlush(rio *r) {
UNUSED(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,即无校验和
0, /* flags */ 标志为0
0, /* bytes read or written */ 读或写的字节数
0, /* read/write chunk size */ 读/写的块字节数
{ { NULL, 0 } } /* union for io-specific vars */ 为特定io变量设置的联合值,初始化字符串为空,偏移量为0
};
初始Rio化缓存
void rioInitWithBuffer(rio *r, sds s) {
*r = rioBufferIO;
r->io.buffer.ptr = s;
r->io.buffer.pos = 0;
}
/* --------------------- Stdio file pointer implementation ------------------- */
标准的输入输出文件指针实现
/* Returns 1 or 0 for success/failure. */
成功返回1,失败返回0
static size_t rioFileWrite(rio *r, const void *buf, size_t len) {
size_t retval;
retval = fwrite(buf,len,1,r->io.file.fp); 写入文件,成功时返回1(因为写入的是1块大小为len长度的数据)
r->io.file.buffered += len;
if (r->io.file.autosync &&
r->io.file.buffered >= r->io.file.autosync) 是否设置了自动刷新的大小,设置了并且当前的缓冲区大小已经超过了这个设定值
{
fflush(r->io.file.fp);将缓冲区的数据刷新到内核缓冲区
redis_fsync(fileno(r->io.file.fp));将内核缓冲区的数据刷新到磁盘
r->io.file.buffered = 0; 缓存清0
}
return retval;
}
/* Returns 1 or 0 for success/failure. */
成功返回1,失败返回0
static size_t rioFileRead(rio *r, void *buf, size_t len) {
return fread(buf,len,1,r->io.file.fp); 读取1块长度为len的数据块,成功返回1,失败返回0
}
/* Returns read/write position in file. */
返回文件中读/写位置
static off_t rioFileTell(rio *r) {
return ftello(r->io.file.fp);
}
/* Flushes any buffer to target device if applicable. Returns 1 on success
* and 0 on failures. */
如果合适,将缓冲区所有的数据刷新到目标设备,成功返回1,失败返回0
static int rioFileFlush(rio *r) {
return (fflush(r->io.file.fp) == 0) ? 1 : 0; fflush返回0表示刷新成功
}
static const rio rioFileIO = { 同rioBufferIO
rioFileRead,
rioFileWrite,
rioFileTell,
rioFileFlush,
NULL, /* update_checksum */
0, /* current checksum */
0, /* flags */
0, /* bytes read or written */
0, /* read/write chunk size */
{ { NULL, 0 } } /* union for io-specific vars */
};
初始化rio文件
void rioInitWithFile(rio *r, FILE *fp) {
*r = rioFileIO;
r->io.file.fp = fp;
r->io.file.buffered = 0;
r->io.file.autosync = 0;
}
/* ------------------- Connection implementation -------------------
套接字连接实现
* We use this RIO implemetnation when reading an RDB file directly from
* the connection to the memory via rdbLoadRio(), thus this implementation
* only implements reading from a connection that is, normally,
* just a socket. */
我们使用这个RIO实现,在rdbLoadRio函数中直接从套接字连接中读取一个RDB文件到内存
static size_t rioConnWrite(rio *r, const void *buf, size_t len) {
UNUSED(r);
UNUSED(buf);
UNUSED(len);
return 0; /* Error, this target does not yet support writing. */ 错误,这个目标还不支持写
}
/* Returns 1 or 0 for success/failure. */
成功返回1,失败返回0
static size_t rioConnRead(rio *r, void *buf, size_t len) {
size_t avail = sdslen(r->io.conn.buf)-r->io.conn.pos; 剩余可使用缓存空间
/* If the buffer is too small for the entire request: realloc. */
如果当前请求超过了缓存剩余可用空间,重新分配空间
if (sdslen(r->io.conn.buf) + sdsavail(r->io.conn.buf) < len)
r->io.conn.buf = sdsMakeRoomFor(r->io.conn.buf, len - sdslen(r->io.conn.buf));
/* If the remaining unused buffer is not large enough: memmove so that we
* can read the rest. */
如果剩余的可用空间不够大:
if (len > avail && sdsavail(r->io.conn.buf) < len - avail) {
sdsrange(r->io.conn.buf, r->io.conn.pos, -1); 将偏移量之前的缓存缩减
r->io.conn.pos = 0; 偏移量重置为0
}
/* If we don't already have all the data in the sds, read more */
如果我们还没有把所有数据读到sds字符串,继续读
while (len > sdslen(r->io.conn.buf) - r->io.conn.pos) {
size_t buffered = sdslen(r->io.conn.buf) - r->io.conn.pos; 已经缓存部分
size_t toread = len - buffered; 将要读取缺失部分
/* Read either what's missing, or PROTO_IOBUF_LEN, the bigger of
* the two. */
读取缺失部分或者PROTO_IOBUF_LEN(16K)中大的一个
if (toread < PROTO_IOBUF_LEN) toread = PROTO_IOBUF_LEN;
if (toread > sdsavail(r->io.conn.buf)) toread = sdsavail(r->io.conn.buf);
如果将要读取的确实部分大小大于sds字符串可用空间大小,那么只读取可用空间大小
if (r->io.conn.read_limit != 0 && 有读取/缓存大小限制
r->io.conn.read_so_far + buffered + toread > r->io.conn.read_limit) 总和大于限制大小
{
if (r->io.conn.read_limit >= r->io.conn.read_so_far - buffered) 这里和下面依据不匹配,-号貌似应该为+号
意思为限制的读 大于 没有缓存的读取数据 + 已经缓存的读取数据 ,那么剩下的就是可以读取的数据
toread = r->io.conn.read_limit - r->io.conn.read_so_far - buffered;
else {
errno = EOVERFLOW; 溢出
return 0;
}
}
int retval = connRead(r->io.conn.conn,
(char*)r->io.conn.buf + sdslen(r->io.conn.buf),
toread); 将toread个字节读取到缓存buf当前数据的后面
if (retval <= 0) { 如果读到的数据小于等于0,需要分清
if (errno == EWOULDBLOCK) errno = ETIMEDOUT; 错误需要设置
return 0; 正常情况,到末尾了,返回0
}
sdsIncrLen(r->io.conn.buf, retval); sds缓存增加本次读取数据的长度
}
memcpy(buf, (char*)r->io.conn.buf + r->io.conn.pos, len); 将新读取的数据搬迁到缓存区
r->io.conn.read_so_far += len;已读数据+读取长度
r->io.conn.pos += len;偏移位置的长度改变
return len;
}
/* Returns read/write position in file. */
返回文件中读/写位置
static off_t rioConnTell(rio *r) {
return r->io.conn.read_so_far;
}
/* Flushes any buffer to target device if applicable. Returns 1 on success
* and 0 on failures. */
如果合适,将缓冲区所有的数据刷新到目标设备,成功返回1,失败返回0
static int rioConnFlush(rio *r) {
/* Our flush is implemented by the write method, that recognizes a
* buffer set to NULL with a count of zero as a flush request. */
我们的刷新是透过写方法实现的,这个方法将计数为0的缓存设置为空,作为一个清空请求。
return rioConnWrite(r,NULL,0);
}
初始化
static const rio rioConnIO = {
rioConnRead,
rioConnWrite,
rioConnTell,
rioConnFlush,
NULL, /* update_checksum */
0, /* current checksum */
0, /* flags */
0, /* bytes read or written */
0, /* read/write chunk size */
{ { NULL, 0 } } /* union for io-specific vars */
};
/* Create an RIO that implements a buffered read from an fd
* read_limit argument stops buffering when the reaching the limit. */
创建一个RIO,该RIO实现从fd的缓冲读取,当到达限制参数read_limit时停止缓冲
void rioInitWithConn(rio *r, connection *conn, size_t read_limit) {
*r = rioConnIO;
r->io.conn.conn = conn;
r->io.conn.pos = 0;
r->io.conn.read_limit = read_limit;
r->io.conn.read_so_far = 0;
r->io.conn.buf = sdsnewlen(NULL, PROTO_IOBUF_LEN);
sdsclear(r->io.conn.buf);
}
/* Release the RIO tream. Optionally returns the unread buffered data
* when the SDS pointer 'remaining' is passed. */
释放RIO流。当传递SDS指针“剩余”时,可选择返回未读缓冲数据。
void rioFreeConn(rio *r, sds *remaining) {
if (remaining && (size_t)r->io.conn.pos < sdslen(r->io.conn.buf)) { 传入指针非空 并且 还没有到sds结束
if (r->io.conn.pos > 0) sdsrange(r->io.conn.buf, r->io.conn.pos, -1); 如果偏移位置大于0,那么截取从偏移位置开始到结尾的数据
*remaining = r->io.conn.buf;
} else { 缓冲区已经全读
sdsfree(r->io.conn.buf);
if (remaining) *remaining = NULL;
}
r->io.conn.buf = NULL;
}
/* ------------------- File descriptor implementation ------------------
文件描述符实现
* This target is used to write the RDB file to pipe, when the master just
* streams the data to the replicas without creating an RDB on-disk image
* (diskless replication option).
* It only implements writes. */
这个实现用于将RDB文件写入到管道,因为主机只是将数据流写入到副机,而不是通过创建一个磁盘映像(无盘复制选项),
它只实现写
/* Returns 1 or 0 for success/failure.
成功返回1,失败返回0
* When buf is NULL and len is 0, the function performs a flush operation
* if there is some pending buffer, so this function is also used in order
* to implement rioFdFlush(). */
当缓存为空,长度为0,如果存在挂起的缓冲区,该函数执行一个刷新操作,因此该函数也被用于实现函数rioFdFlush
static size_t rioFdWrite(rio *r, const void *buf, size_t len) {
ssize_t retval;
unsigned char *p = (unsigned char*) buf;
int doflush = (buf == NULL && len == 0); 判断是否是做刷新操作
/* For small writes, we rather keep the data in user-space buffer, and flush
* it only when it grows. however for larger writes, we prefer to flush
* any pre-existing buffer, and write the new one directly without reallocs
* and memory copying. */
对于少量的写,我们更加倾向于将数据保存在用户空间的缓存,当它增长时,直接刷新。然而对于大量的写,
我们倾向于刷新已经存在的缓存,直接写入到新的缓存,而不用再次分配和内存拷贝
if (len > PROTO_IOBUF_LEN) { 超过超过了16K
/* First, flush any pre-existing buffered data. */首先,刷新任何已经存在的缓存数据
if (sdslen(r->io.fd.buf)) {
if (rioFdWrite(r, NULL, 0) == 0)
return 0;
}
/* Write the new data, keeping 'p' and 'len' from the input. */
} else {
if (len) { 长度大于0
r->io.fd.buf = sdscatlen(r->io.fd.buf,buf,len);将buf中len长度的数据拼接到r->io.fd.buf
if (sdslen(r->io.fd.buf) > PROTO_IOBUF_LEN) 如果航都超过了16K
doflush = 1; 需要做刷新
if (!doflush)
return 1;
}
/* Flusing the buffered data. set 'p' and 'len' accordintly. */
刷新缓存数据,设置指针p和长度len保持一致
p = (unsigned char*) r->io.fd.buf;
len = sdslen(r->io.fd.buf);
}
size_t nwritten = 0;
while(nwritten != len) {
retval = write(r->io.fd.fd,p+nwritten,len-nwritten);
if (retval <= 0) { 写数据小于等于0
/* With blocking io, which is the sole user of this
* rio target, EWOULDBLOCK is returned only because of
* the SO_SNDTIMEO socket option, so we translate the error
* into one more recognizable by the user. */
对于这个rio目标的唯一用户阻塞io,只会因为SO_SNDTIMEO套接字选项而返回EWOULDBLOCK错误,
因此我们将错误转换为用户可以识别的错误。
if (retval == -1 && errno == EWOULDBLOCK) errno = ETIMEDOUT;
return 0; /* error. */
}
nwritten += retval; 写入数据增加到已写计数上
}
r->io.fd.pos += len; 偏移量加上已写入数据长度
sdsclear(r->io.fd.buf);情况缓存
return 1;
}
/* Returns 1 or 0 for success/failure. */
成功返回1,失败返回0
static size_t rioFdRead(rio *r, void *buf, size_t len) {
UNUSED(r);
UNUSED(buf);
UNUSED(len);
return 0; /* Error, this target does not support reading. */
}
/* Returns read/write position in file. */
返回文件中的读/写位置
static off_t rioFdTell(rio *r) {
return r->io.fd.pos;
}
/* Flushes any buffer to target device if applicable. Returns 1 on success
* and 0 on failures. */
如果合适,将缓冲区所有的数据刷新到目标设备,成功返回1,失败返回0
static int rioFdFlush(rio *r) {
/* Our flush is implemented by the write method, that recognizes a
* buffer set to NULL with a count of zero as a flush request. */
我们的刷新是通过写入方法实现的,通过设置一个缓存为NULL,长度为0,作为一次刷新请求
return rioFdWrite(r,NULL,0);
}
static const rio rioFdIO = {
rioFdRead,
rioFdWrite,
rioFdTell,
rioFdFlush,
NULL, /* update_checksum */
0, /* current checksum */
0, /* flags */
0, /* bytes read or written */
0, /* read/write chunk size */
{ { NULL, 0 } } /* union for io-specific vars */
};
void rioInitWithFd(rio *r, int fd) {
*r = rioFdIO;
r->io.fd.fd = fd;
r->io.fd.pos = 0;
r->io.fd.buf = sdsempty();
}
/* release the rio stream. */
释放RIO流
void rioFreeFd(rio *r) {
sdsfree(r->io.fd.buf);
}
/* ---------------------------- Generic functions ---------------------------- */
通用函数
/* This function can be installed both in memory and file streams when checksum
* computation is needed. */
当校验和计算需要时,这个函数可以安装在内存和文件流的处理上
void rioGenericUpdateChecksum(rio *r, const void *buf, size_t len) {
r->cksum = crc64(r->cksum,buf,len); 计算crc64校验码
}
/* Set the file-based rio object to auto-fsync every 'bytes' file written.
* By default this is set to zero that means no automatic file sync is
* performed.
对于基于文件的rio对象 设置每写bytes个字节到文件就自动刷新到磁盘。
默认这个值会设置为0,意味不会执行自动刷新文件到磁盘。
* This feature is useful in a few contexts since when we rely on OS write
* buffers sometimes the OS buffers way too much, resulting in too many
* disk I/O concentrated in very little time. When we fsync in an explicit
* way instead the I/O pressure is more distributed across time. */
此功能在某些情况下很有用,因为当我们依赖操作系统写缓冲区时,有时操作系统缓冲区太多,
导致太多磁盘I/O集中在很短的时间内。当我们以显式方式进行fsync时,I/O压力在时间上的分布的更开(不会集中在一起)。
void rioSetAutoSync(rio *r, off_t bytes) {
if(r->write != rioFileIO.write) return;
r->io.file.autosync = bytes;
}
/* --------------------------- Higher level interface --------------------------
高层次的交互接口
* The following higher level functions use lower level rio.c functions to help
* generating the Redis protocol for the Append Only File. */
下面的高级函数使用低级rio.c函数来帮助生成AOF的Redis协议。
/* Write multi bulk count in the format: "*<count>\r\n". */
以以下格式写入块计数:"*<count>\r\n".
size_t rioWriteBulkCount(rio *r, char prefix, long count) {
char cbuf[128];
int clen;
cbuf[0] = prefix;
clen = 1+ll2string(cbuf+1,sizeof(cbuf)-1,count); 1是prefix的长度,将长度写入缓存cbuf
cbuf[clen++] = '\r';
cbuf[clen++] = '\n';
if (rioWrite(r,cbuf,clen) == 0) return 0;
return clen;
}
/* Write binary-safe string in the format: "$<count>\r\n<payload>\r\n". */
用如下格式写入二进制安全的字符串: "$<count>\r\n<payload>\r\n"
size_t rioWriteBulkString(rio *r, const char *buf, size_t len) {
size_t nwritten;
if ((nwritten = rioWriteBulkCount(r,'$',len)) == 0) return 0; 写入长度
if (len > 0 && rioWrite(r,buf,len) == 0) return 0; 写入具体内容
if (rioWrite(r,"\r\n",2) == 0) return 0; 写入结束
return nwritten+len+2; 写入总长度
}
/* Write a long long value in format: "$<count>\r\n<payload>\r\n". */
用如下格式写入一个长整形: "$<count>\r\n<payload>\r\n"
size_t rioWriteBulkLongLong(rio *r, long long l) {
char lbuf[32];
unsigned int llen;
llen = ll2string(lbuf,sizeof(lbuf),l);
return rioWriteBulkString(r,lbuf,llen);
}
/* Write a double value in the format: "$<count>\r\n<payload>\r\n" */
用如下格式写入一个双精度浮点数: "$<count>\r\n<payload>\r\n"
size_t rioWriteBulkDouble(rio *r, double d) {
char dbuf[128];
unsigned int dlen;
dlen = snprintf(dbuf,sizeof(dbuf),"%.17g",d);
return rioWriteBulkString(r,dbuf,dlen);
}
*******************************************************************************************************