/* Start interactive mode when no command is provided */ if (argc == 0 && !config.eval) { /* Ignore SIGPIPE in interactive mode to force a reconnect */ signal(SIGPIPE, SIG_IGN);
/* Note that in repl mode we don't abort on connection error. * A new attempt will be performed for every command send. */ cliConnect(0); repl(); }
/* Otherwise, we have some arguments to execute */ if (cliConnect(0) != REDIS_OK) exit(1); if (config.eval) { return evalMode(argc,argv); } else { return noninteractive(argc,convertToSds(argc,argv)); }
//hiredis.h /* Context for a connection to Redis */ typedefstructredisContext { int err; /* Error flags, 0 when there is no error */ char errstr[128]; /* String representation of error when applicable */ int fd; int flags; char *obuf; /* Write buffer */ redisReader *reader; /* Protocol reader */
staticintissueCommandRepeat(int argc, char **argv, long repeat) { while (1) { config.cluster_reissue_command = 0; if (cliSendCommand(argc,argv,repeat) != REDIS_OK) { cliConnect(CC_FORCE);
/* If we still cannot send the command print error. * We'll try to reconnect the next time. */ if (cliSendCommand(argc,argv,repeat) != REDIS_OK) { cliPrintContextError(); return REDIS_ERR; } } /* Issue the command again if we got redirected in cluster mode */ if (config.cluster_mode && config.cluster_reissue_command) { cliConnect(CC_FORCE); } else { break; } } return REDIS_OK; }
gdb src/redis-server GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-git Copyright (C) 2018 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty"for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration"for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type"help". Type "apropos word" to search for commands related to "word"... Reading symbols from src/redis-server...done. (gdb) b readQueryFromClient Breakpoint 1 at 0x43c520: file networking.c, line 1379. (gdb) run redis.conf
然后再调试redis-cli,断点设置cliReadReply函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
gdb src/redis-cli GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-git Copyright (C) 2018 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty"for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration"for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type"help". Type "apropos word" to search for commands related to "word"... Reading symbols from src/redis-cli...done. (gdb) b cliReadReply Breakpoint 1 at 0x40ffa0: file redis-cli.c, line 845. (gdb) run
在客户端输入get命令,发现程序在断点处停止。
1 2 3 4 5
127.0.0.1:6379> get jackey
Breakpoint 1, cliReadReply (output_raw_strings=output_raw_strings@entry=0) at redis-cli.c:845 845 static int cliReadReply(int output_raw_strings) {
我们可以看到这时Redis已经准备好将命令发送给服务端了,先来查看一下要发送的内容。
1 2
(gdb) p context->obuf $1 = 0x684963 "*2\r\n$3\r\nget\r\n$6\r\njackey\r\n"
把\r\n替换成换行符看的后是这样:
1 2 3 4 5 6
*2 $3 get $6 jackey
*2表示命令参数的总数,包括命令的名字,也就是告诉服务端应该处理两个参数。
$3表示第一个参数的长度。
get是命令名,也就是第一个参数。
$6表示第二个参数的长度。
jackey是第二个参数。
当程序运行到redisGetReply时就会把命令发送给服务端了,这时我们再来看服务端的运行情况。
1 2 3 4 5
Thread 1 "redis-server" hit Breakpoint 1, readQueryFromClient ( el=0x7ffff6a41050, fd=7, privdata=0x7ffff6b1e340, mask=1) at networking.c:1379 1379 void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) { (gdb)
程序调整到
1
sdsIncrLen(c->querybuf,nread);
这时nread的内容会被加到c->querybuf中,我们来看一下是不是我们发送过来的命令。
1 2
(gdb) p c->querybuf $1 = (sds) 0x7ffff6a75cc5 "*2\r\n$3\r\nget\r\n$6\r\njackey\r\n"
robj *lookupKey(redisDb *db, robj *key, int flags) { dictEntry *de = dictFind(db->dict,key->ptr); if (de) { robj *val = dictGetVal(de);
/* Update the access time for the ageing algorithm. * Don't do it if we have a saving child, as this will trigger * a copy on write madness. */ if (server.rdb_child_pid == -1 && server.aof_child_pid == -1 && !(flags & LOOKUP_NOTOUCH)) { if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) { updateLFU(val); } else { val->lru = LRU_CLOCK(); } } return val; } else { returnNULL; } }