总的来说,对一个指针解引用时,首先就是看当前指针的数据类型,比如 int *指针,那么说明指向int,就会取4个字节来进行解引用;如果是指向一个结构体,就会计算结构体占用的字节数,然后取对应的字节,来解引用为结构体类型的变量。
这部分,大家可以看看这块:
https://www.runoob.com/cprogramming/c-data-types.html
https://www.runoob.com/cprogramming/c-pointer-arithmetic.html
redis启动过程之配置项初始化前面说了很多,我们本讲也不够讲完全部的redis启动过程了,可能还要两讲的样子,本讲先讲解一部分。
启动入口在:redis.c中的main 方法,如果使用我这边的代码来搭建调试环境,可以直接启动redis-server。
int main(int argc, char **argv) { struct timeval tv; /** * 1 设置时区 */ setlocale(LC_COLLATE,""); /** *2 */ zmalloc_enable_thread_safeness(); // 3 zmalloc_set_oom_handler(redisOutOfMemoryHandler); // 4 srand(time(NULL)^getpid()); // 5 gettimeofday(&tv,NULL); // 6 dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid()); // 检查服务器是否以 Sentinel 模式启动 server.sentinel_mode = checkForSentinelMode(argc,argv); // 7 初始化服务器 initServerConfig();
1处,设置时区
2处,设置进行内存分配的线程的数量,这里会设为1
3处,设置oom发生时的函数指针,函数指针指向一个函数,类似于java 8中,lambda表达式中,丢一个方法的引用给流;函数指针会在oom时,被回调,总体来说,就类似于java中的模板设计模式或者策略模式。
4处,设置随机数的种子
5处,获取当前时间,设置到 tv这个变量中
注意,这里把tv的地址传进去了,这是c语言中典型的用法,类似于java中传一个对象的引用进去,然后在方法内部,会修改该对象的内部field等
6处,设置hash函数的种子
7处,初始化服务器。
这里重点说下7处:
void initServerConfig() { int j; // 服务器状态 // 设置服务器的运行 ID getRandomHexChars(server.runid,REDIS_RUN_ID_SIZE); // 设置默认配置文件路径 server.configfile = NULL; // 设置默认服务器频率 server.hz = REDIS_DEFAULT_HZ; // 为运行 ID 加上结尾字符 server.runid[REDIS_RUN_ID_SIZE] = '\0'; // 设置服务器的运行架构 server.arch_bits = (sizeof(long) == 8) ? 64 : 32; // 设置默认服务器端口号 server.port = REDIS_SERVERPORT; // tcp 全连接队列的长度 server.tcp_backlog = REDIS_TCP_BACKLOG; // 绑定的地址的数量 server.bindaddr_count = 0; // UNIX socket path server.unixsocket = NULL; server.unixsocketperm = REDIS_DEFAULT_UNIX_SOCKET_PERM; // 绑定的 TCP socket file descriptors server.ipfd_count = 0; server.sofd = -1; // redis可使用的redis db的数量 server.dbnum = REDIS_DEFAULT_DBNUM; // redis 日志级别 server.verbosity = REDIS_DEFAULT_VERBOSITY; // Client timeout in seconds,客户端最大空闲时间;超过这个时间的客户端,会被强制关闭 server.maxidletime = REDIS_MAXIDLETIME; // Set SO_KEEPALIVE if non-zero. 如果设为非0,则开启tcp的SO_KEEPALIVE server.tcpkeepalive = REDIS_DEFAULT_TCP_KEEPALIVE; // 打开这个选项,会周期性地清理过期key server.active_expire_enabled = 1; // 客户端发来的请求中,查询缓存的最大值;比如一个set命令,value的大小就会和这个缓冲区大小比较, // 如果大了,就根本放不进缓冲区 server.client_max_querybuf_len = REDIS_MAX_QUERYBUF_LEN; // rdb保存参数,比如每60s保存,n个键被修改了保存,之类的 server.saveparams = NULL; // 如果为1,表示服务器正在从磁盘载入数据: We are loading data from disk if true server.loading = 0; // 日志文件位置 server.logfile = zstrdup(REDIS_DEFAULT_LOGFILE); // 开启syslog等机制 server.syslog_enabled = REDIS_DEFAULT_SYSLOG_ENABLED; server.syslog_ident = zstrdup(REDIS_DEFAULT_SYSLOG_IDENT); server.syslog_facility = LOG_LOCAL0; // 后台运行 server.daemonize = REDIS_DEFAULT_DAEMONIZE; // aof状态 server.aof_state = REDIS_AOF_OFF; // aof的刷磁盘策略,默认每秒刷盘 server.aof_fsync = REDIS_DEFAULT_AOF_FSYNC; // 正在rewrite时,不刷盘 server.aof_no_fsync_on_rewrite = REDIS_DEFAULT_AOF_NO_FSYNC_ON_REWRITE; // Rewrite AOF if % growth is > M and... server.aof_rewrite_perc = REDIS_AOF_REWRITE_PERC; // the AOF file is at least N bytes. aof达到多大时,触发rewrite server.aof_rewrite_min_size = REDIS_AOF_REWRITE_MIN_SIZE; // 最后一次执行 BGREWRITEAOF 时, AOF 文件的大小 server.aof_rewrite_base_size = 0; // Rewrite once BGSAVE terminates.开启该选项时,BGSAVE结束时,触发rewrite server.aof_rewrite_scheduled = 0; // 最近一次aof进行fsync的时间 server.aof_last_fsync = time(NULL); // 最近一次aof重写,消耗的时间 server.aof_rewrite_time_last = -1; // Current AOF rewrite start time. server.aof_rewrite_time_start = -1; // 最后一次执行 BGREWRITEAOF 的结果 server.aof_lastbgrewrite_status = REDIS_OK; // 记录 AOF 的 fsync 操作被推迟了多少次 server.aof_delayed_fsync = 0; // File descriptor of currently selected AOF file server.aof_fd = -1; // AOF 的当前目标数据库 server.aof_selected_db = -1; /* Make sure the first time will not match */ // UNIX time of postponed AOF flush server.aof_flush_postponed_start = 0; // fsync incrementally while rewriting? 重写过程中,增量触发fsync server.aof_rewrite_incremental_fsync = REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC; // pid文件 server.pidfile = zstrdup(REDIS_DEFAULT_PID_FILE); // rdb 文件名 server.rdb_filename = zstrdup(REDIS_DEFAULT_RDB_FILENAME); // aof 文件名 server.aof_filename = zstrdup(REDIS_DEFAULT_AOF_FILENAME); // 是否要密码 server.requirepass = NULL; // 是否进行rdb压缩 server.rdb_compression = REDIS_DEFAULT_RDB_COMPRESSION; // rdb checksum server.rdb_checksum = REDIS_DEFAULT_RDB_CHECKSUM; // bgsave失败,停止写入 server.stop_writes_on_bgsave_err = REDIS_DEFAULT_STOP_WRITES_ON_BGSAVE_ERROR; // 在执行 serverCron() 时进行渐进式 rehash server.activerehashing = REDIS_DEFAULT_ACTIVE_REHASHING; server.notify_keyspace_events = 0; // 支持的最大客户端数量 server.maxclients = REDIS_MAX_CLIENTS; // bpop阻塞的客户端 server.bpop_blocked_clients = 0; // 可以使用的最大内存 server.maxmemory = REDIS_DEFAULT_MAXMEMORY; // 内存淘汰策略,也就是key的过期策略 server.maxmemory_policy = REDIS_DEFAULT_MAXMEMORY_POLICY; server.maxmemory_samples = REDIS_DEFAULT_MAXMEMORY_SAMPLES; // hash表的元素小于这个值时,使用ziplist 编码模式;以下几个类似 server.hash_max_ziplist_entries = REDIS_HASH_MAX_ZIPLIST_ENTRIES; server.hash_max_ziplist_value = REDIS_HASH_MAX_ZIPLIST_VALUE; server.list_max_ziplist_entries = REDIS_LIST_MAX_ZIPLIST_ENTRIES; server.list_max_ziplist_value = REDIS_LIST_MAX_ZIPLIST_VALUE; server.set_max_intset_entries = REDIS_SET_MAX_INTSET_ENTRIES; server.zset_max_ziplist_entries = REDIS_ZSET_MAX_ZIPLIST_ENTRIES; server.zset_max_ziplist_value = REDIS_ZSET_MAX_ZIPLIST_VALUE; server.hll_sparse_max_bytes = REDIS_DEFAULT_HLL_SPARSE_MAX_BYTES; // 该标识打开时,表示正在关闭服务器 server.shutdown_asap = 0; // 复制相关 server.repl_ping_slave_period = REDIS_REPL_PING_SLAVE_PERIOD; server.repl_timeout = REDIS_REPL_TIMEOUT; server.repl_min_slaves_to_write = REDIS_DEFAULT_MIN_SLAVES_TO_WRITE; server.repl_min_slaves_max_lag = REDIS_DEFAULT_MIN_SLAVES_MAX_LAG; // cluster模式相关 server.cluster_enabled = 0; server.cluster_node_timeout = REDIS_CLUSTER_DEFAULT_NODE_TIMEOUT; server.cluster_migration_barrier = REDIS_CLUSTER_DEFAULT_MIGRATION_BARRIER; server.cluster_configfile = zstrdup(REDIS_DEFAULT_CLUSTER_CONFIG_FILE); // lua脚本 server.lua_caller = NULL; server.lua_time_limit = REDIS_LUA_TIME_LIMIT; server.lua_client = NULL; server.lua_timedout = 0; // server.migrate_cached_sockets = dictCreate(&migrateCacheDictType,NULL); server.loading_process_events_interval_bytes = (1024*1024*2); // 初始化 LRU 时间 server.lruclock = getLRUClock(); // 初始化并设置保存条件 resetServerSaveParams(); // rdb的默认保存策略 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.masterauth = NULL; server.masterhost = NULL; server.masterport = 6379; server.master = NULL; server.cached_master = NULL; server.repl_master_initial_offset = -1; server.repl_state = REDIS_REPL_NONE; server.repl_syncio_timeout = REDIS_REPL_SYNCIO_TIMEOUT; server.repl_serve_stale_data = REDIS_DEFAULT_SLAVE_SERVE_STALE_DATA; server.repl_slave_ro = REDIS_DEFAULT_SLAVE_READ_ONLY; server.repl_down_since = 0; /* Never connected, repl is down since EVER. */ server.repl_disable_tcp_nodelay = REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY; server.slave_priority = REDIS_DEFAULT_SLAVE_PRIORITY; server.master_repl_offset = 0; /* Replication partial resync backlog */ // 初始化 PSYNC 命令所使用的 backlog server.repl_backlog = NULL; server.repl_backlog_size = REDIS_DEFAULT_REPL_BACKLOG_SIZE; server.repl_backlog_histlen = 0; server.repl_backlog_idx = 0; server.repl_backlog_off = 0; server.repl_backlog_time_limit = REDIS_DEFAULT_REPL_BACKLOG_TIME_LIMIT; server.repl_no_slaves_since = time(NULL); /* Client output buffer limits */ // 设置客户端的输出缓冲区限制 for (j = 0; j < REDIS_CLIENT_LIMIT_NUM_CLASSES; j++) server.client_obuf_limits[j] = clientBufferLimitsDefaults[j]; /* Double constants initialization */ // 初始化浮点常量 R_Zero = 0.0; R_PosInf = 1.0/R_Zero; R_NegInf = -1.0/R_Zero; R_Nan = R_Zero/R_Zero; // 初始化命令表,比如get、set、hset等各自的处理函数,放进一个hash表,方便后续处理请求 server.commands = dictCreate(&commandTableDictType,NULL); server.orig_commands = dictCreate(&commandTableDictType,NULL); populateCommandTable(); server.delCommand = lookupCommandByCString("del"); server.multiCommand = lookupCommandByCString("multi"); server.lpushCommand = lookupCommandByCString("lpush"); server.lpopCommand = lookupCommandByCString("lpop"); server.rpopCommand = lookupCommandByCString("rpop"); /* Slow log */ // 初始化慢查询日志 server.slowlog_log_slower_than = REDIS_SLOWLOG_LOG_SLOWER_THAN; server.slowlog_max_len = REDIS_SLOWLOG_MAX_LEN; /* Debugging */ // 初始化调试项 server.assert_failed = "<no assertion failed>"; server.assert_file = "<no file>"; server.assert_line = 0; server.bug_report_start = 0; server.watchdog_period = 0; }以上都加了注释,我们可以先不看:复制、cluster、lua等相关的,先看其他的。
总结太久没碰c了,有些遗忘,不过总体来说,并不难,难的是内存泄露之类,但我们只是debug学习使用,不用担心这些问题。