redis程序中的一点问题,照片中是redis源码解析中<sds.h>文件中的一段程序

Redis源码分析(五)---sparkline微线图
(window.slotbydup=window.slotbydup || []).push({
id: '2611110',
container: s,
size: '240,200',
display: 'inlay-fix'
您当前位置: &
[ 所属分类
sparkline这个单词,我第一次看的时候,也不知道这什么意思啊,以前根本没听过啊,但是这真真实实的出现在了redis的代码中了,刚刚开始以为这也是属于普通的队列嘛,就把他分在了struct包里了。好来分析完了,与原本我所想的差太大了。sparkline英文中的意思“微线图”,这么说吧,类似于折线图,由一个一个信息点构成。所以看到这个意思,你或许就明白了sparkline.c是干什么用的了吧,就是画图用的。我们看看这个画图的内部结构是什么,画图需要的元素是哪些:/* sparkline.h -- ASCII Sparklines header file * * --------------------------------------------------------------------------- * * Copyright(C)
Salvatore Sanfilippo && * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */#ifndef __SPARKLINE_H#define __SPARKLINE_H/* sparkline是一类信息体积小和数据密度高的图表。目前它被用作一些测量, *相关的变化的信息呈现的方式,如平均温度,股市交投活跃。sparkline常常以一组多条的形式出现在柱状图,折线图当中。 *可以理解为一个图线信息 *//* A sequence is represented of many "samples" *//* 可以理解为图像上的一个信息点,有文字,有值的大小 */struct sample { char *};/* 图线信息结构体,包括n个元素点,可以据此描述出图,绘图的可不是直接按点和值直接绘制的 */struct sequence { //当前元素点个数 //总共的文字个数,有些点没有label描述,为NULL //元素点列表 struct sample * //元素中的最大值,最小值 double min,};/* 定义了一些渲染图时候一些属性操作设置 */#define SPARKLINE_NO_FLAGS 0#define SPARKLINE_FILL 1 /* Fill the area under the curve. */#define SPARKLINE_LOG_SCALE 2 /* Use logarithmic scale. */struct sequence *createSparklineSequence(void); //创建图线序列结构体void sparklineSequenceAddSample(struct sequence *seq, double value, char *label); //在图线序列中添加一个信息点void freeSparklineSequence(struct sequence *seq); //释放图线序列sds sparklineRenderRange(sds output, struct sequence *seq, int rows, int offset, int len, int flags); //渲染图线序列为一个图,其实就是得到一个字符串组成的图sds sparklineRender(sds output, struct sequence *seq, int columns, int rows, int flags); //方法同上,只是少可一个偏移量#endif /* __SPARKLINE_H */我们看到上面的sample结构体其实“信息点”元素的意思了,里面很简单,一个文字label,一个value值,简洁明了,然后sequence就自然是图线了,里面就定义了元素点列表了,里面还有线的长度,和最大值,最小值,信息还挺全面的线。后序的画图操作都是根据这个图线序列结构体操作的。结构体一点都不复杂。但是画图的实现一点都不简单,如何根据给定的一些点信息画出一个类似折线的图线呢,可别忘了,这是要在命令行窗口的图线哦,所以不会像高级语言中的GUI的操作那样很方便,我们看看redis代码中是怎么写的。/* sparkline.c -- ASCII Sparklines * This code is modified from /antirez/aspark and adapted * in order to return SDS strings instead of outputting directly to * the terminal. * * ---------------------------------------------------------------------------在sparkline.c中的注释声明,此代码修改自 /antirez/aspark,原来是开源的代码实现,但是一开始真的不知道还有这么个叫aspark的东西,都跟BigData里的spark搞混了,然后我点击此地址,官方解释来了:aspark is a C program to display ASCII Sparklines.It is completely useless in 2011.不错,意思就是说aspark就是用来在C程序上显示图线效果的。后来,我看了下,的确代码差不多,redis的代码在上面加了自己的东西,稍稍修改,aspark的图线展现有几种形式,第一种,最简单的展示:$ ./aspark 1,2,3,4,10,7,6,5 `-_ __-` `第二张把行数扩展为更多行,展示更多的数据,上面的这个为2行展示,数据多的时候,调节行数,默认输出2行展示$ ./aspark 1,2,3,4,5,6,7,8,9,10,10,8,5,3,1 --rows 4 _-``_
-` ` _-` `_当然可以更加可视化,在空白处填充字符,看起来更舒服:$ ./aspark 1,2,3,4,5,6,7,8,9,10,10,8,5,3,1 --rows 4 --fill _o##_
o#|||||||# _o#||||||||||#_用了"|"符号,很有想象力的哦,最最关键的我们看如何实现这样的效果呢,核心代码如下:/* Render part of a sequence, so that render_sequence() call call this function * with differnent parts in order to create the full output without overflowing * the current terminal columns. *//* 渲染出这个图线信息 */sds sparklineRenderRange(sds output, struct sequence *seq, int rows, int offset, int len, int flags) { double relmax = seq-&max - seq-& int steps = charset_len* int row = 0; char *chars = zmalloc(len); int loop = 1; int opt_fill = flags & SPARKLINE_FILL; int opt_log = flags & SPARKLINE_LOG_SCALE; if (opt_log) { relmax = log(relmax+1); } else if (relmax == 0) { relmax = 1; } while(loop) { loop = 0; memset(chars,' ',len); for (j = 0; j & j++) {struct sample *s = &seq-&samples[j+offset];//value派上用处了double relval = s-&value - seq-&if (opt_log) relval = log(relval+1);//最后会算出相关的stepstep = (int) (relval*steps)/if (step & 0) step = 0;if (step &= steps) step = steps-1;if (row & rows) { /* Print the character needed to create the sparkline */ /* step控制输出的字符是哪一个 */ int charidx = step-((rows-row-1)*charset_len); loop = 1; if (charidx &= 0 && charidx & charset_len) { chars[j] = opt_fill ? charset_fill[charidx] : charset[charidx]; } else if(opt_fill && charidx &= charset_len) { //用"|"填充内容,更加可视化 chars[j] = '|'; }} else { /* Labels spacing */ if (seq-&labels && row-rows & label_margin_top) { loop = 1; } /* Print the label if needed. */ if (s-&label) { int label_len = strlen(s-&label); int label_char = row - rows - label_margin_ if (label_len & label_char) { loop = 1; chars[j] = s-&label[label_char]; } }} } if (loop) {row++;output = sdscatlen(output,chars,len);output = sdscatlen(output,"\n",1); } } zfree(chars);}由于本人能力有限,有点不太懂里面的具体细节,大概看了下,把变量用到的地方稍稍看了下,上面的代码都是非常优秀的代码,值得我们学习,今天至少让我知道了什么叫sparkline叫什么 了,哈哈。
本文数据库(综合)相关术语:系统安全软件
转载请注明本文标题:本站链接:
分享请点击:
1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
脚步无法到达的地方,目光可以到达;目光无法到达的地方,梦想可以到达。
手机客户端
,专注代码审计及安全周边编程,转载请注明出处:http://www.codesec.net
转载文章如有侵权,请邮件 admin[at]codesec.net<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
您的访问请求被拒绝 403 Forbidden - ITeye技术社区
您的访问请求被拒绝
亲爱的会员,您的IP地址所在网段被ITeye拒绝服务,这可能是以下两种情况导致:
一、您所在的网段内有网络爬虫大量抓取ITeye网页,为保证其他人流畅的访问ITeye,该网段被ITeye拒绝
二、您通过某个代理服务器访问ITeye网站,该代理服务器被网络爬虫利用,大量抓取ITeye网页
请您点击按钮解除封锁&服务器内部问题 - 私塾在线
服务器正在重启或服务器内部错误,请稍候访问!Sponsered by
Redis 日志和断言
Redis 日志
linux 的世界里,最好用的调试工具不是 gdb,而是日志和 printf。日志在一个软件系统中是非常常见的,一个关键的作用即定位错误,当系统出问题首先想到就是日志,查看日志能快速定位问题。Redis 中的日志模块较为简单。我们在 Redis 源码中,到处都可以见到 redisLog()。
通常,日志会分为几个级别。在 Redis 中 5 个日志级别,在 redis.h 文件中有定义:
/* Log levels */
#define REDIS_DEBUG 0 // 调试级别,这一级别产生最多的日志信息
#define REDIS_VERBOSE 1
#define REDIS_NOTICE 2
#define REDIS_WARNING 3
#define REDIS_LOG_RAW (1&&10) /* Modifier to log without timestamp */
#define REDIS_DEFAULT_VERBOSITY REDIS_NOTICE
服务器的配置结构体中,struct redisServer.verbosity 是用来设定日志级别的,譬如将日志级别设定为REDIS_NOTICE 后,代码中 REDIS_VERBOSE 和REDIS_DEBUG 级别的日志都不会被打印。
日志级别值越是低,日志级别越高,产生了日志也就越多,开发人员在产品上线之前会将日志级别调至最低,方便发现定位或发现潜在的问题。而上线之后,可以将志级别降低,减少调试日志。如果日志级别过高,则日志量大,可能会对线上的服务产生影响,因为写日志就是写文件操作,系统调用是要消耗时间的。
日志是想要记录某一个时间点,在哪里发送了什么事情,以方便出现问题的时候,恢复现场,快速定位问题所在。“某一时间点”即添加时间戳;“在哪里”即程序执行的位置,对应的是源码的文件,行号函数等;“发生了什么事情”即记录一些关键数据。
// redis 日志函数,会将给定的数据写入日志文件,和常用的printf 函数用法差不多
void redisLog(int level, const char *fmt, ...) {
char msg[REDIS_MAX_LOGMSG_LEN];
// 如果日志级别小于预设的日志级别,直接返回
if ((level&0xff) & server.verbosity)
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
// redisLogRaw() 函数将给定的信息,在增加时间戳和进程id 后写入日志文件
redisLogRaw(level,msg);
Redis 断言
为什么需要断言?>>>>>>>>>>>>& TODO 当你认为某些事情在正常情况下不可能出现,应尽可能结束任务,而不是捕捉错误,尝试挽救。同样在西加加里,使用 try…catch() 会让程序的逻辑变乱,甚至让程序的行为变得不可预测,大胆的使用断言吧。
Redis 中不仅仅实现了断言,且在断言失败的时候会打印一些关键的信息。
在 Redis.h 中定义了两个断言相关的宏:
#define redisAssertWithInfo(_c,_o,_e) \
((_e)?(void)0 : \
(_redisAssertWithInfo(_c,_o,#_e,__FILE__,__LINE__),_exit(1)))
#define redisAssert(_e) \
((_e)?(void)0 : \
(_redisAssert(#_e,__FILE__,__LINE__),_exit(1)))
如果断言为真,执行一个空操作;断言为假,会打印关键的信息。
_redisAssert() 函数会记录断言发生的错误信息,文件名和行号
void _redisAssert(char *estr, char *file, int line) {
// 向日志文件中写入BUG 头部
bugReportStart();
// 将文件名,行号,错误信息写入日志
redisLog(REDIS_WARNING,"=== ASSERTION FAILED ===");
redisLog(REDIS_WARNING,"==& %s:%d '%s' is not true",file,line,estr);
// 如果需要,可以记录错误信息,文件名和行号,以便在进程崩溃后调试(gdb core?)
#ifdef HAVE_BACKTRACE
server.assert_failed =
server.assert_file =
server.assert_line =
redisLog(REDIS_WARNING,"(forcing SIGSEGV to print the bug report.)");
// 强制segmentation fault。无效的内存访问,可以产生SIGSEGV,如此会
// 产生coredump 文件以供进程崩溃后调试使用
*((char*)-1) = 'x';
这有个小有意思的语句:*((char*)-1) = ’x’;(char *)-1表示指向地址值为 -1 的指针,它所指向的内存肯定是非法的,对非法内存的操作会触发 SIGSEGV 信号,进程结束后会产生 coredump 文件,方便调试使用。使用gdb、可执行文件和 coredump 文件能快速定位问题所在,即使进程已经崩溃了。
_redisAssertWithInfo() 函数会打印 Redis 服务器当前服务的客户端和某个关键 Redis 对象的信息,具体请参看源码,在这不展开了。redis源代码分析5–内存(上)
这一节介绍redis中内存分配相关的api,下一节介绍redis内存使用过程中的一些细节。
redis中所有的内存分配都由自己接管。主要由zmalloc.c和zmalloc.h中的zmalloc、zremalloc、zfree实现。
void *zmalloc(size_t size) {
void *ptr = malloc(size+PREFIX_SIZE);
if (!ptr) zmalloc_oom(size);
#ifdef HAVE_MALLOC_SIZE
// apple系统,额外说明
increment_used_memory(redis_malloc_size(ptr));
*((size_t*)ptr) =
increment_used_memory(size+PREFIX_SIZE);
return (char*)ptr+PREFIX_SIZE;
可以看到,系统中除了分配请求大小的内存外,还在该内存块头部保存了该内存块的大小,这样,释放的时候可以通过该大小找到该内存块的起始位置:
void zfree(void *ptr) {
realptr = (char*)ptr-PREFIX_SIZE;
oldsize = *((size_t*)realptr);
decrement_used_memory(oldsize+PREFIX_SIZE);
free(realptr);
另外对于 apple系统,可以用malloc_size(redis_malloc_size是对它的封装)取得指针所指向的内存块大小,因此就不需要手动保存大小了。(笔者没有用过apple系统,仅看了下apple的相关文档: /library/mac/#documentation/Darwin/Reference/ManPages/man3 /malloc_size.3.html)
此条目发表在
分类目录。将加入收藏夹。
友情链接功能}

我要回帖

更多关于 redis源代码分析 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信