Redis源码分析(一)redis.c //redis-server.c
入口函数 int main()
4450 int main(int argc, char **argv) {4451 initServerConfig();4452 if (argc == 2) {4453 ResetServerSaveParams();4454 loadServerConfig(argv[1]);4455 } else if (argc > 2) {4456 fprintf(stderr,"Usage: ./redis-server [/path/to/redis.conf]\n");4457 exit(1);4458 } else {4459 redisLog(REDIS_WARNING,"Warning: no config file specified, using the default config. In order to specify a config file use 'redis-server /path/to/redis.conf'");4460 }4461 initServer();4462 if (server.daemonize) daemonize();4463 redisLog(REDIS_NOTICE,"Server started, Redis version " REDIS_VERSION);4464 #ifdef __linux__4465 linuxOvercommitMemoryWarning();4466 #endif4467 if (rdbLoad(server.dbfilename) == REDIS_OK)4468 redisLog(REDIS_NOTICE,"DB loaded from disk");4469 if (aeCreateFileEvent(server.el, server.fd, AE_READABLE,4470 acceptHandler, NULL, NULL) == AE_ERR) oom("creating file event");4471 redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port);4472 aeMain(server.el);4473 aeDeleteEventLoop(server.el);4474 return 0;4475 }
初始化参数 initServerConfig()
static void initServerConfig() {server.dbnum = REDIS_DEFAULT_DBNUM;//初始化redis的数据库个数16server.port = REDIS_SERVERPORT;//端口6379server.verbosity = REDIS_DEBUG;//默认这个为0// 这个是日志级别,有三种//#define REDIS_DEBUG 0 //#define REDIS_NOTICE 1 //#define REDIS_WARNING 2server.maxidletime = REDIS_MAXIDLETIME;//最大空闲时间60*5,也是客户端的超时时间server.saveparams = NULL;//持久化策略server.logfile = NULL; /* NULL = log on standard output, 也可以在后期脚本文件中将参数传入 */server.bindaddr = NULL;server.glueoutputbuf = 1;//在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启。server.daemonize = 0;设置redis是否以守护进程方式运行,默认为no。server.pidfile = "/var/run/redis.pid";server.dbfilename = "dump.rdb";server.requirepass = NULL;//设置redis连接的密码,设置之后客户端连接redis时需要先通过auth命令提供密码进行验证才能进行后续操作,默认没有密码。设置操作:requirepass mypassserver.shareobjects = 0;server.sharingpoolsize = 1024;server.maxclients = 0;server.maxmemory = 0;ResetServerSaveParams();appendServerSaveParams(60*60,1); /* save after 1 hour and 1 change */appendServerSaveParams(300,100); /* save after 5 minutes and 100 changes */appendServerSaveParams(60,10000); /* save after 1 minute and 10000 changes *//* Replication related */server.isslave = 0;server.masterhost = NULL;server.masterport = 6379;server.master = NULL;server.replstate = REDIS_REPL_NONE;}
初始化函数中的server变量包含
223 /* Global server state structure */224 struct redisServer {225 int port; //端口号226 int fd; //文件描述符227 redisDb *db; // 一个数组,保存着服务器中的所有数据库228 dict *sharingpool; //存放数据的哈希表229 unsigned int sharingpoolsize;230 long long dirty; /* changes to DB from the last save */231 list *clients; //客户端链表232 list *slaves, *monitors;233 char neterr[ANET_ERR_LEN]; //网络错误信息存储,最多256个char234 aeEventLoop *el; //事件循环结构体 235 int cronloops; /* number of times the cron function run */236 list *objfreelist; /* A list of freed objects to avoid malloc() */237 time_t lastsave; /* Unix time of last save succeeede */238 size_t usedmemory; /* Used memory in megabytes */239 /* Fields used only for stats */240 time_t stat_starttime; /* server start time */241 long long stat_numcommands; /* number of processed commands */242 long long stat_numconnections; /* number of connections received */243 /* Configuration */244 int verbosity;245 int glueoutputbuf;246 int maxidletime; //最大空闲时间247 int dbnum; //数据库的数量248 int daemonize;249 char *pidfile;250 int bgsaveinprogress;251 pid_t bgsavechildpid;252 struct saveparam *saveparams; //持久化参数253 int saveparamslen;254 char *logfile; //日志文件路径255 char *bindaddr;256 char *dbfilename;257 char *requirepass;258 int shareobjects;259 /* Replication related */260 int isslave;261 char *masterhost;262 int masterport;263 redisClient *master; /* client that is master for this slave */264 int replstate;265 unsigned int maxclients; 266 unsigned int maxmemory;267 /* Sort parameters - qsort_r() is only available under BSD so we268 * have to take this state global, in order to pass it to sortCompare() */269 int sort_desc;270 int sort_alpha;271 int sort_bypattern;272 };273
server函数中的redisDb *db;结构体
190 typedef struct redisDb { 191 dict *dict; //当前数据库的键空间192 dict *expires; //键的过期时间193 int id; //数据库ID标识194 } redisDb;195
server函数中的dict结构体
结构体位于:dict.h
67 //哈希表的定义 68 typedef struct dict { 69 //指向实际的哈希表记录(用数组+开链的形式进行保存) 70 dictEntry **table; 71 //type中包含一系列哈希表需要用到的函数 72 dictType *type; 73 //size表示哈希表的大小,为2的指数 74 unsigned long size; 75 //sizemask=size-1,方便哈希值根据size取模 76 unsigned long sizemask; 77 //used记录了哈希表中有多少记录 78 unsigned long used; 79 void *privdata; 80 } dict; 81 82 //对Hash表进行迭代遍历时使用的迭代器83 typedef struct dictIterator { 84 dict *ht; 85 int index; 86 dictEntry *entry, *nextEntry; 87 } dictIterator; 88
dict结构体中的dictEntry
46 //实际存放数据的地方47 typedef struct dictEntry {48 void *key;49 void *val;50 struct dictEntry *next;51 } dictEntry;52
dict结构体中的dictType
53 //要作用于哈希表上的相关函数54 /*55 * dictType在哈希系统中包含了一系列可由应用程序定义的56 * 函数指针57 */58 typedef struct dictType {59 unsigned int (*hashFunction)(const void *key);//哈希函数60 void *(*keyDup)(void *privdata, const void *key);//key复制61 void *(*valDup)(void *privdata, const void *obj);//value复制62 int (*keyCompare)(void *privdata, const void *key1, const void *key2);//key比较63 void (*keyDestructor)(void *privdata, void *key);//key销毁64 void (*valDestructor)(void *privdata, void *obj);//value销毁65 } dictType;66
server函数中的list结构体
adlist.h
67 /* 68 * redis中的双向链表 69 * head标识链表的头指针 70 * tail标识链表的尾结点 71 * dup/free/match是三个函数指针 72 * dup用于复制链表,返回值也是一个函数指针 73 * free用于释放链表 74 * match用于判断链表中是否存在*key的值 75 * len标识链表的长度 76 * listIter链表的迭代器,通过此迭代器可以对链表进行遍历 77 * 至于为什么要提供这种迭代器,可以查看设计模式相关的书箱 78 */ 79 80 typedef struct list { 81 listNode *head;//链表的头结点 82 listNode *tail;//链表的尾节点 83 void *(*dup)(void *ptr);//复制链表 84 void (*free)(void *ptr);//释放内存 85 int (*match)(void *ptr, void *key);//匹配 86 unsigned int len; //标识链表的长度, 87 listIter iter; 88 } list; 89
listNode结构体
40 /* 41 * redis中最基本的结构用以标识链表中的结点 42 * *prev标识上一个节点 43 * *next标识下一个节点 44 * *value标识节点的值 45 */ 46 47 typedef struct listNode { 48 struct listNode *prev; 49 struct listNode *next; 50 void *value; 51 } listNode; 52
迭代器
53 /* 54 * 迭代器用于链表的遍历 55 * next将要遍历的下一个元素 56 * direction遍历链表的方向 57 * 方向由下面的两个宏进行标识 58 * AL_START_HEAD表示向前 59 * AL_START_TAIL表示向后 60 */ 61 62 typedef struct listIter { 63 listNode *next; 64 int direction; 65 } listIter; 66
server函数中的aeEventLoop *el结构体
ae.h
112
113 /* State of an event based program */
114 /**
115 * 事件循环结构体
116 */
117 typedef struct aeEventLoop {
118 //用于标识下一个定时器
119 long long timeEventNextId;
120 //文件事件
121 aeFileEvent *fileEventHead;
122 //定时器事件
123 aeTimeEvent *timeEventHead;
124 //stop用于停止事件轮询
125 int stop;
126 } aeEventLoop;
127
aeEventLoopd的aeFileEvent和aeTimeEvent
78 typedef struct aeFileEvent {79 int fd;80 /**81 * AE_READABLE|AE_WRITEABLE_AE_EXCEPTION中的一个82 * 表示要监听的事件类型83 */84 int mask; /* one of AE_(READABLE|WRITABLE|EXCEPTION) */85 //文件事件相应的处理函数86 aeFileProc *fileProc;87 aeEventFinalizerProc *finalizerProc;88 void *clientData;89 //下一个文件事件90 struct aeFileEvent *next;91 } aeFileEvent;92 93 /* Time event structure */94 /**95 * redis自已定义的定时器事件96 * 其实现是一个链表,其中的每一个结点是一个Timer97 * when_sec与when_ms指定了定时器发生的时间98 * timeProc为响应函数99 * finalizerProc为删除定时器的析构函数
100 */
101 typedef struct aeTimeEvent {
102 //定时器的id
103 long long id; /* time event identifier. */
104 long when_sec; /* seconds */
105 long when_ms; /* milliseconds */
106 aeTimeProc *timeProc;
107 aeEventFinalizerProc *finalizerProc;
108 //定义了该定时器有的数据情况
109 void *clientData;
110 struct aeTimeEvent *next;
111 } aeTimeEvent;
常数定义部分
/* Error codes */#define REDIS_OK 0#define REDIS_ERR -1/* Static server configuration */#define REDIS_SERVERPORT 6379 /* TCP port */#define REDIS_MAXIDLETIME (60*5) /* default client timeout */#define REDIS_IOBUF_LEN 1024#define REDIS_LOADBUF_LEN 1024#define REDIS_STATIC_ARGS 4#define REDIS_DEFAULT_DBNUM 16#define REDIS_CONFIGLINE_MAX 1024#define REDIS_OBJFREELIST_MAX 1000000 /* Max number of objects to cache */#define REDIS_MAX_SYNC_TIME 60 /* Slave can't take more to sync */#define REDIS_EXPIRELOOKUPS_PER_CRON 100 /* try to expire 100 keys/second */#define REDIS_MAX_WRITE_PER_EVENT (1024*64)#define REDIS_REQUEST_MAX_SIZE (1024*1024*256) /* max bytes in inline command *//* Hash table parameters */#define REDIS_HT_MINFILL 10 /* Minimal hash table fill 10% *//* Command flags */#define REDIS_CMD_BULK 1 /* Bulk write command */#define REDIS_CMD_INLINE 2 /* Inline command *//* REDIS_CMD_DENYOOM reserves a longer comment: all the commands marked withthis flags will return an error when the 'maxmemory' option is set in theconfig file and the server is using more than maxmemory bytes of memory.In short this commands are denied on low memory conditions. */#define REDIS_CMD_DENYOOM 4/* Object types */#define REDIS_STRING 0#define REDIS_LIST 1#define REDIS_SET 2#define REDIS_HASH 3/* Object types only used for dumping to disk */#define REDIS_EXPIRETIME 253#define REDIS_SELECTDB 254#define REDIS_EOF 255/* Defines related to the dump file format. To store 32 bits lengths for short* keys requires a lot of space, so we check the most significant 2 bits of* the first byte to interpreter the length:** 00|000000 => if the two MSB are 00 the len is the 6 bits of this byte* 01|000000 00000000 => 01, the len is 14 byes, 6 bits + 8 bits of next byte* 10|000000 [32 bit integer] => if it's 01, a full 32 bit len will follow* 11|000000 this means: specially encoded object will follow. The six bits* number specify the kind of object that follows.* See the REDIS_RDB_ENC_* defines.** Lenghts up to 63 are stored using a single byte, most DB keys, and may* values, will fit inside. */#define REDIS_RDB_6BITLEN 0#define REDIS_RDB_14BITLEN 1#define REDIS_RDB_32BITLEN 2#define REDIS_RDB_ENCVAL 3#define REDIS_RDB_LENERR UINT_MAX/* When a length of a string object stored on disk has the first two bits* set, the remaining two bits specify a special encoding for the object* accordingly to the following defines: */#define REDIS_RDB_ENC_INT8 0 /* 8 bit signed integer */#define REDIS_RDB_ENC_INT16 1 /* 16 bit signed integer */#define REDIS_RDB_ENC_INT32 2 /* 32 bit signed integer */#define REDIS_RDB_ENC_LZF 3 /* string compressed with FASTLZ *//* Client flags */#define REDIS_CLOSE 1 /* This client connection should be closed ASAP */#define REDIS_SLAVE 2 /* This client is a slave server */#define REDIS_MASTER 4 /* This client is a master server */#define REDIS_MONITOR 8 /* This client is a slave monitor, see MONITOR *//* Slave replication state - slave side */#define REDIS_REPL_NONE 0 /* No active replication */#define REDIS_REPL_CONNECT 1 /* Must connect to master */#define REDIS_REPL_CONNECTED 2 /* Connected to master *//* Slave replication state - from the point of view of master* Note that in SEND_BULK and ONLINE state the slave receives new updates* in its output queue. In the WAIT_BGSAVE state instead the server is waiting* to start the next background saving in order to send updates to it. */#define REDIS_REPL_WAIT_BGSAVE_START 3 /* master waits bgsave to start feeding it */#define REDIS_REPL_WAIT_BGSAVE_END 4 /* master waits bgsave to start bulk DB transmission */#define REDIS_REPL_SEND_BULK 5 /* master is sending the bulk DB */#define REDIS_REPL_ONLINE 6 /* bulk DB already transmitted, receive updates *//* List related stuff */#define REDIS_HEAD 0#define REDIS_TAIL 1/* Sort operations */#define REDIS_SORT_GET 0#define REDIS_SORT_DEL 1#define REDIS_SORT_INCR 2#define REDIS_SORT_DECR 3#define REDIS_SORT_ASC 4#define REDIS_SORT_DESC 5#define REDIS_SORTKEY_MAX 1024/* Log levels */#define REDIS_DEBUG 0#define REDIS_NOTICE 1#define REDIS_WARNING 2/* Anti-warning macro... */#define REDIS_NOTUSED(V) ((void) V)