原创作品,转载请标明:http://blog.csdn.net/xiejingfa/article/details/51433696

Redis源码剖析系列文章汇总:传送门

Reids内部封装了一个I/O层,称之为rio。今天我们就来简单介绍一下rio模块的具体实现。

本文主要涉及rio.h和rio.c两个文件。


1、rio结构体

关于文件读写操作和buffer的操作主要基于rio对象进行操作,我们先来看看rio结构体的定义,如下:

/* 系统IO操作的封装 */
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. */// io变量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. */// 状态位,与fds对应int *state;     /* Error state of each fd. 0 (if ok) or errno. */// 文件描述符的个数int numfds;// 偏移量off_t pos;// 缓冲区sds buf;} fdset;} io;
};

我们可以看到rio结构体主要包含以下三方面内容:

  1. 读写操作、获取偏移量操作等相关的回调函数。rio可以处理buffer、file、socket三种不同类型的I/O对象,不同的rio对象底层使用相应的系统调用完成read、write、tell、flush操作。比如,对于file rio对象,底层通过fwrite函数完成写操作,通过fread函数完成读操作(具体见源码)。
  2. 校验和操作。rio使用了RCR64算法计算校验和,具体实现可以参看crc64.h和crc64.c文件。
  3. IO变量。_rio中的io成员是一个联合体,针对不同的I/O情况进行不同的处理:当执行内存buffer的I/O操作时,使用rio.buffer结构体;当执行文件I/O操作时,使用rio.file结构体;当执行socket的I/O操作时,使用rio.fdset结构体。

介绍完rio结构体后,我们来看看buffer rio对象的实现(file rio对象和socket rio对象的现实大家可以参考注释版源码,这里就不一一讲解)。

buffer的写操作实际上就是将数据存入r->io.buffer中,由rioBufferWrite函数实现:

/* 将buf中指定长度len的内容追加到rio对象的缓冲区中,操作成功返回1,否则返回0。*/
static size_t rioBufferWrite(rio *r, const void *buf, size_t len) {// 调用sdscatlen实现append操作r->io.buffer.ptr = sdscatlen(r->io.buffer.ptr,(char*)buf,len);// 更新长度信息r->io.buffer.pos += len;return 1;
}

buffer的写操作实际上就是将数据从r->io.buffer中读出,由rioBufferRead函数实现:

/* 从rio对象的缓冲区中读取长度为len的内容到buf中,操作成功返回1,否则返回0。*/
static size_t rioBufferRead(rio *r, void *buf, size_t len) {// 如果rio对象的缓冲区中内容的长度小于len,读取失败if (sdslen(r->io.buffer.ptr)-r->io.buffer.pos < len)return 0; /* not enough buffer to return len bytes. */// 将缓冲区中的内容复制到bufmemcpy(buf,r->io.buffer.ptr+r->io.buffer.pos,len);// 更新偏移量r->io.buffer.pos += len;return 1;
}

buffer的tell操作和flush操作如下:

/*  返回rio对象缓冲区的偏移量 */
static off_t rioBufferTell(rio *r) {return r->io.buffer.pos;
}/*  该函数什么事也没有做,直接返回1。*/
static int rioBufferFlush(rio *r) {// REDIS_NOTUSED定义在redis.h文件中:#define REDIS_NOTUSED(V) ((void) V)REDIS_NOTUSED(r);return 1; /* Nothing to do, our write just appends to the buffer. */
}

根据上面定义的函数,我们就可以创建buffer rio对象:

/* 根据上面的方法定义的流为内存时使用的buffer rio对象 */
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 */
};

rioInitWithBuffer函数负责初始化buffer rio对象,创建io.buffer缓冲区。

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

2、统一的读写操作接口

rio模块封装了Redis对buffer读写操作、file读写操作、socket读写操作,同时也定义了统一的read、write、tell、flus操作接口。

执行rio的写操作:

/*  将buf数组中的长度为len的字符写入rio对象中,写入成功返回1,写入失败返回0。*/
static inline size_t rioWrite(rio *r, const void *buf, size_t len) {while (len) {// 判断当前要求写入的字节数是否操作了max_processing_chunk规定的最大长度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);// 调用write方法执行写入操作if (r->write(r,buf,bytes_to_write) == 0)return 0;// 更新buf下次写入的位置buf = (char*)buf + bytes_to_write;len -= bytes_to_write;// 更新已写入的字节数r->processed_bytes += bytes_to_write;}return 1;
}

执行rio的读操作:

/* 从rio对象中读出长度为len字节的数据并保存到buf数组中。读取成功返回1,读取失败返回0。*/
static inline size_t rioRead(rio *r, void *buf, size_t len) {while (len) {// 判断当前要求读出的字节数是否操作了max_processing_chunk规定的最大长度size_t bytes_to_read = (r->max_processing_chunk && r->max_processing_chunk < len) ? r->max_processing_chunk : len;// 调用read方法读出数据到buf中if (r->read(r,buf,bytes_to_read) == 0)return 0;// 更新buf下次写入的位置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;
}

执行rio的tell操作:

/* 返回当前偏移量 */
static inline off_t rioTell(rio *r) {return r->tell(r);
}

执行rio的flush操作:

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

从上面四个函数的实现中可以看出它们都是调用rio对象内部的回调函数来执行执行相应的write、read、tell、flush操作的,这些回调函数具体完成的操作又跟rio对象是buffer rio对象还是file rio对象,亦或是socket rio对象有关。

3、其它

Redis中的rio模块还封装了一些辅助生成AOF协议的函数,这些函数主要包括:

// 以"*<count>\r\n"的形式将count以字符串的格式写入rio对象中,返回写入的字节数。
size_t rioWriteBulkCount(rio *r, char prefix, int count);
// 以"$<count>\r\n<payload>\r\n"格式往rio对象中写入二进制安全字符串。
size_t rioWriteBulkString(rio *r, const char *buf, size_t len);
// 以"$<count>\r\n<payload>\r\n"的格式往rio对象中写入long long类型的值。
size_t rioWriteBulkLongLong(rio *r, long long l);
// 以"$<count>\r\n<payload>\r\n"的格式往rio对象中写入double类型的值。
size_t rioWriteBulkDouble(rio *r, double d);

Redis中的rio模块对系统I/O函数进行封装。在Redis内部实现中,RDB、AOF等都使用该模块的功能,所以这里简要介绍了rio模块的实现原理。

源码见:

  1. rio.h:https://github.com/xiejingfa/the-annotated-redis-3.0/blob/master/rio.h
  2. rio.c:https://github.com/xiejingfa/the-annotated-redis-3.0/blob/master/rio.c

【Redis源码剖析】 - Redis IO操作之rio相关推荐

  1. Redis源码剖析和注释(十六)---- Redis输入输出的抽象(rio)

    Redis源码剖析和注释(十六)---- Redis输入输出的抽象(rio) . https://blog.csdn.net/men_wen/article/details/71131550 Redi ...

  2. Redis源码剖析之GEO——Redis是如何高效检索地理位置的?

    Redis GEO 用做存储地理位置信息,并对存储的信息进行操作.通过geo相关的命令,可以很容易在redis中存储和使用经纬度坐标信息.Redis中提供的Geo命令有如下几个: geoadd:添加经 ...

  3. 【Redis源码剖析】 - Redis持久化之RDB

    原创作品,转载请标明:http://blog.csdn.net/xiejingfa/article/details/51553370 Redis源码剖析系列文章汇总:传送门 Redis是一个高效的内存 ...

  4. 【Redis源码剖析】 - Redis内置数据结构之压缩列表ziplist

    在前面的一篇文章[Redis源码剖析] - Redis内置数据结构之双向链表中,我们介绍了Redis封装的一种"传统"双向链表list,分别使用prev.next指针来指向当前节点 ...

  5. Redis源码剖析之内存淘汰策略(Evict)

    文章目录 何为Evict 如何Evict Redis中的Evict策略 源码剖析 LRU具体实现 LFU具体实现 LFU计数器增长 LFU计数器衰减 evict执行过程 evict何时执行 evict ...

  6. redis源码剖析(十五)——客户端思维导图整理

    redis源码剖析(十五)--客户端执行逻辑结构整理 加载略慢

  7. redis源码剖析(3):基础数据结构dict

    目录 1.dict概述 2.字典的定义 3.哈希算法 4.字典的初始化及新增键值对 4.1 字典初始化 4.2 新增键值对 5.rehash(重新散列)操作 5.1 rehash操作方式 5.2 re ...

  8. Redis源码剖析和注释(二十四)--- Redis Sentinel实现(哨兵操作的深入剖析)

    Redis Sentinel实现(下) 本文是Redis Sentinel实现(上)篇文章的下半部分剖析.主要剖析以下内容: 4. 哨兵的使命 Redis Sentinel实现下 哨兵的使命 1 周期 ...

  9. Redis源码剖析(二)io多路复用函数及事件驱动流程

    作为服务器监听客户端请求的方法,io多路复用起到了不可忽略的作用,利用io复用监听的方法叫Reactor模式,在前一篇也提到过,使用io复用是现在常用的提高并发性的方法,而且效果显著. 通常io多路复 ...

最新文章

  1. 13 款惊艳的 Node.js 框架——第1部分
  2. TensorFlow 莫烦 手写识别 cross_entry (五)
  3. TCP close_wait 状态的解释
  4. myeclipse和输入法冲突的问题
  5. VTK:图片之RTAnalyticSource
  6. 搭建基础架构-Order
  7. 逆向工程核心原理学习笔记(二):字符串检索法查找main函数
  8. python实训总结万能版3000字_实训总结万能版2000字范文五篇
  9. 4种分布式Session的实现方式!老大直呼666...
  10. P5732 【深基5.习7】杨辉三角(python3实现)
  11. python入门第二章房贷计算器 打印五子棋棋盘 猜数字 计数器 逢七拍手游戏
  12. 我来做百科(第二十天) C
  13. 杭电ACM 第2007题
  14. 微信小程序——推箱子小游戏
  15. 依图科技CTO颜水成被曝离职!或加入东南亚某电商
  16. 【抽象代数概念速查】Lagrange Interpolation-拉格朗日插值
  17. .7z.001,.7z.002这样的文件如何解压
  18. JAVA:日期时间范围查询0点到23点59分59秒之间
  19. 计算机网络常用相关术语大全
  20. ubuntu更改更新源

热门文章

  1. 推荐一个优秀的c++源代码,TinyXml2
  2. 苹果Meta都在冲的Pancake技术,中国VR团队YVR竟抢先交出产品答卷
  3. 华为设备VXLAN配置举例
  4. ASP.NET计算机类专业毕业设计(课程设计)题目大全
  5. 你的代码值多少钱 ?
  6. 大赢家软件测试工资,C++实验:某学校对教师每月工资的计算规定如下:固定工资+课时补贴。...
  7. 关于使用通用mapper出现的错误
  8. matlab中电流表在哪儿,电流表的符号
  9. 为什么不能直接通过IP访问网站
  10. blur表单验证方式