Reading Redis #2
Redis’s src/ directory contains multiple main() functions, because src/Makefile builds multiple executable files from the directory.
all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_DUMP_NAME) $(REDIS_CHECK_AOF_NAME)
@echo ""
@echo "Hint: It's a good idea to run 'make test' ;)"
@echo ""
In addition to that, a few files have main() as a test function.
% git grep 'TEST_*MAIN' src
src/crc64.c:184:#ifdef TEST_MAIN
src/endianconv.c:104:#ifdef TESTMAIN
src/intset.c:284:#ifdef INTSET_TEST_MAIN
src/sds.c:965:#ifdef SDS_TEST_MAIN
src/util.c:577:#ifdef UTIL_TEST_MAIN
src/ziplist.c:955:#ifdef ZIPLIST_TEST_MAIN
src/zipmap.c:373:#ifdef ZIPMAP_TEST_MAIN
%
src/redis.c
Let’s read redis-server first. Its main() is in src/redis.c and this file is pretty long. Actually that’s the third longest file under src/.
% wc -l src/*.c | sort -n | tail
1556 src/hyperloglog.c
1764 src/networking.c
1929 src/config.c
2104 src/replication.c
2298 src/redis-cli.c
2856 src/t_zset.c
3690 src/redis.c
3900 src/sentinel.c
5007 src/cluster.c
51031 total
%
The main() is calling aeSetBeforeSleepProc(), aeMain() and aeDeleteEventLoop() in the end. These functions are defined under ae.c, Redis’ event-driven programming library.
int main(int argc, char **argv) {
struct timeval tv;
/* We need to initialize our libraries, and the server configuration. */
#ifdef INIT_SETPROCTITLE_REPLACEMENT
spt_init(argc, argv);
#endif
setlocale(LC_COLLATE,"");
zmalloc_enable_thread_safeness();
zmalloc_set_oom_handler(redisOutOfMemoryHandler);
srand(time(NULL)^getpid());
gettimeofday(&tv,NULL);
dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid());
server.sentinel_mode = checkForSentinelMode(argc,arg ev);
initServerConfig();
...
aeSetBeforeSleepProc(server.el,beforeSleep);
aeMain(server.el);
aeDeleteEventLoop(server.el);
return 0;
}
The purpose of the library is similar to libevent. It abstracts the difference between Solaris’ event ports, Linux’s epoll, *BSD’s (incl. OS X) kqueue, and good old-fashioned select().
/* A simple event-driven programming library. Originally I wrote this code
* for the Jim's event-loop (Jim is a Tcl interpreter) but later translated
* it in form of a library for easy reuse.
*
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
...
*/
...
/* Include the best multiplexing layer supported by this system.
* The following should be ordered by performances, descending. */
#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
#ifdef HAVE_EPOLL
#include "ae_epoll.c"
#else
#ifdef HAVE_KQUEUE
#include "ae_kqueue.c"
#else
#include "ae_select.c"
#endif
#endif
#endif
redis.c is initializing this event library from initServer().
void initServer(void) {
int j;
...
createSharedObjects();
adjustOpenFilesLimit();
server.el = aeCreateEventLoop(server.maxclients+REDIS_EVENTLOOP_FDSET_INCR);
server.db = zmalloc(sizeof(redisDb)*server.dbnum);
...
/* Create the serverCron() time event, that's our main way to process
* background operations. */
if(aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
redisPanic("Can't create the serverCron time event.");
exit(1);
}
/* Create an event handler for accepting new connections in TCP and Unix
* domain sockets. */
for (j = 0; j < server.ipfd_count; j++) {
if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
acceptTcpHandler,NULL) == AE_ERR)
{
redisPanic(
"Unrecoverable error creating server.ipfd file event.");
}
}
if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
acceptUnixHandler,NULL) == AE_ERR) redisPanic("Unrecoverable error creating server.sofd file event.");
...
if (server.cluster_enabled) clusterInit();
replicationScriptCacheInit();
scriptingInit();
slowlogInit();
latencyMonitorInit();
bioInit();
}
...
Both acceptTcpHandler() and acceptUnixHandler() are defined under networking.c, and it delegates the process to acceptCommonHandler().
What’s next?
I will read networking.c, where we can see redis-server’s protocol parser.