Cao Gong dit Redis code source (2) - analyse du processus de démarrage du serveur Redis et supplément de connaissances de base en langage c simple

Navigation dans l'article

L'intention initiale de la série de codes source Redis est de nous aider à mieux comprendre Redis et à mieux comprendre Redis, mais comment le comprendre n'est pas suffisant. Il est recommandé de suivre cet article pour créer l'environnement et suivre le code source vous-même, ou Lisez avec moi. Depuis que j'ai utilisé C il y a plusieurs années, certaines erreurs sont inévitables, j'espère que le lecteur pourra le signaler.

Cao Gong a déclaré que le code source de Redis (1) -réduire l'environnement de débogage, utiliser clion, obtenir le même effet que le débogage java

Quelques connaissances supplémentaires

Structure et entrée du projet

En plus des jouets universitaires, un vrai projet est composé d'un grand nombre de fichiers de code source pour former un projet. En Java, si un fichier java utilise des fonctions, des types, des variables, etc. dans d'autres fichiers java, il doit être importé à l'aide des instructions d'importation. La même chose est vraie dans le langage C. Dans le langage C, pour inclure les fonctions d'autres fichiers, vous devez utiliser l'instruction include.

Par exemple, dans l'entrée principale de redis, le fichier redis.c contient le groupe d'instructions suivant:

#include "redis.h"
#include "cluster.h"
#include "slowlog.h"
#include "bio.h"

#include <time.h>
#include <signal.h>

Parmi eux, ceux commençant par <, comme <time.h> est le fichier d'en-tête de la bibliothèque standard, qui sera recherché sous le chemin spécifié par le système, qui peut être comparé à la jdkclasse officielle; "bio.h" est enveloppé dans "", Il est personnalisé dans le projet.

Par exemple, time.h, je l'ai trouvé dans le chemin suivant de Linux:

[root@mini1 src]# locate time.h
/usr/include/time.h

Pour d'autres connaissances connexes, veuillez vous référer à:

https://www.runoob.com/cprogramming/c-header-files.html

Ma compréhension du fichier d'en-tête

De manière générale, nous écrirons nos méthodes de logique métier dans le fichier .c. Parmi elles, certaines méthodes peuvent être utilisées uniquement à l'intérieur de ce fichier, comme la méthode privée de la classe java; certaines méthodes peuvent être Doit être utilisé dans d'autres fichiers de code source externe. Comment ces méthodes peuvent-elles être utilisées en externe?

Il peut être compris comme une interface dans les principaux langages de haut niveau via le mécanisme du fichier d'en-tête. En java, une classe est définie. Bien que la méthode puisse être directement définie sur public, d'autres classes sont directement accessibles; cependant, dans le développement commercial habituel, nous Généralement, vous n'accédez pas directement à une classe d'implémentation, mais via l'interface qu'elle implémente; une bonne classe d'implémentation ne doit pas définir des méthodes non définies dans l'interface sur des autorisations publiques.

En parlant du fichier d'en-tête, par exemple, il existe un fichier source test.ccomme suit:


    
long long ustime(void) {
    struct timeval tv;
    long long ust;

    gettimeofday(&tv, NULL);
    ust = ((long long)tv.tv_sec)*1000000;
    ust += tv.tv_usec;
    return ust;
}
/* Return the UNIX time in milliseconds */
// 返回毫秒格式的 UNIX 时间
// 1 秒 = 1 000 毫秒
long long mstime(void) {
    return ustime()/1000;
}

Dans ce fichier, deux méthodes sont définies, mais en supposant que nous avons seulement besoin d'exposer les mstime(void)méthodes en externe , le fichier d'en-tête test.hdevrait ressembler à ceci:



long long mstime(void);

Dans ce cas, notre autre méthode, ustime, n'est pas visible pour le monde extérieur.

En bref, vous pouvez comprendre le fichier d'en-tête comme l'interface de la classe d'implémentation à exposer au monde extérieur; vous pouvez penser que mon analogie est inappropriée. Pourquoi appelez-vous le fichier c comme classe d'implémentation? En fait, lorsque nous étions à Huawei, nous avons utilisé c ++ L'idée, la pensée orientée objet, d'écrire le langage c.

J'ai vu un article sur Internet, citez-le ici ( https://zhuanlan.zhihu.com/p/57882822):

Contrairement à Redis, il est un codage C pur, mais intègre des idées orientées objet. Contrairement au point de vue ci-dessus, il peut être décrit comme "design en C ++ et code en C." Bien sûr, le but de cet article n'est pas de provoquer des conflits linguistiques. Différentes langues ont leurs avantages et leurs inconvénients. La sélection de la langue des projets open source est également principalement due à l'expérience personnelle et aux souhaits subjectifs des auteurs du projet.

Mais le fichier d'en-tête en langage C est différent de l'interface en langage Java. En Java, l'interface et la classe d'implémentation sont finalement compilées dans des fichiers de classe indépendants.

Dans le langage C, avant de compiler la classe d'implémentation, il y aura un processus de prétraitement. Le processus de prétraitement consiste à remplacer directement l'instruction include par le contenu du fichier d'en-tête inclus. Par exemple, prenons l'exemple du didacticiel débutant comme exemple:

 header.h
 char *test (void);

Dans ce qui suit program.cles méthodes d' essai nécessitent l'utilisation de l'header.h ci - dessus est requis comprennent:

int x;
#include "header.h"

int main (void)
{
   puts (test ());
}

Après le prétraitement (c'est-à-dire un simple remplacement), l'effet est le suivant:

int x;
char *test (void);

int main (void)
{
   puts (test ());
}

Nous pouvons utiliser la commande suivante pour illustrer ce processus:

[root@mini1 test]# gcc -E program.c 
int x;
# 1 "header.h" 1

char *test (void);
# 3 "program.c" 2

int main (void)
{
   puts (test ());
}

Comme vous pouvez le voir ci-dessus, il a été remplacé; que faire si nous l'incluons deux fois?

[root@mini1 test]# gcc -E program.c 
int x;
# 1 "header.h" 1

char *test (void);
# 3 "program.c" 2
# 1 "header.h" 1

char *test (void);
# 4 "program.c" 2
int main (void)
{
   puts (test ());
}

Il peut être constaté que le contenu de cet en-tête apparaît deux fois et se répète. Mais dans le cas ci-dessus, aucune erreur n'est signalée, mais la méthode est définie deux fois.

Pourquoi devrait-il y avoir un ifndef dans le fichier d'en-tête

Lorsque vous regardez le fichier d'en-tête, vous trouverez l'instruction suivante, comme dans redis.h:

#ifndef __REDIS_H
#define __REDIS_H

#include "fmacros.h"
#include "config.h"

...
    
typedef struct redisObject {

    // 类型
    unsigned type:4;

    // 编码
    unsigned encoding:4;

    // 对象最后一次被访问的时间
    unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */

    // 引用计数
    int refcount;

    // 指向实际值的指针
    void *ptr;

} robj;

...
    
#endif

Comme vous pouvez le voir, au début, il y a une phrase:

#ifndef __REDIS_H
#define __REDIS_H

Il y a une phrase à la fin:

#endif

Ceci pour résoudre les problèmes suivants:

Lorsque le fichier d'en-tête est importé à plusieurs reprises (indirectement ou directement, il est inclus deux fois), s'il n'est pas ajouté, cela entraînera l'importation du contenu du fichier d'en-tête deux fois; après l'ajout, même s'il est inclus Deux fois, lorsque le programme est en cours d'exécution, au début, il est constaté que __REDIS_Hcette macro n'est pas définie , puis définissez-la; lorsque le programme rencontre le contenu de la deuxième inclusion, il est constaté que __REDIS_Hcette macro a été définie et qu'elle est ignorée directement, de sorte que la garantie Avec le même fichier d'en-tête, même s'il est inclus plusieurs fois, son contenu ne peut être analysé qu'une seule fois.

En outre, comme la déclaration de méthode, la définition peut être correcte plusieurs fois, mais s'il existe la définition de type suivante dans le fichier d'en-tête:

typedef char my_char;
char *test (void);

Si vous incluez à plusieurs reprises le même fichier d'en-tête, la définition du type sera répétée. Cependant, il est très étrange de l'avoir essayé sur centos 7.3.1611, version gcc :, gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-16)mais il n'a pas signalé d'erreur. Il semble que mes connaissances antérieures en langage C n'aient pas été apprises à la maison.

Je n'ai pas trouvé de doublons inclus sur Internet temporairement. Quels sont les inconvénients spécifiques? Il y a deux réponses trouvées sur Internet:

  1. Les variables globales sont définies dans le fichier d'en-tête;
  2. Perdre du temps de compilation

Cependant, la première réponse, à proprement parler, n'existe pas, car les entreprises interdisent généralement la définition de variables dans les fichiers d'en-tête.

Il y a un problème que vous connaissez, vous pouvez regarder: Quels sont les dangers de l'inclusion répétée de fichiers d'en-tête?

Dans la spécification de programmation en langage c de Huawei, une partie des dispositions du fichier d'en-tête

Vous pouvez rechercher par vous-même: spécification de programmation en langage c Huawei Technology Co., Ltd.

Je n'intercepte qu'une partie ici:

规则1.6 禁止在头文件中定义变量。
说明: 在头文件中定义变量,将会由于头文件被其他.c文件包含而导致变量重复定义。
    
规则1.7 只能通过包含头文件的方式使用其他.c提供的接口,禁止在.c中通过extern的方式使用外部
函数接口、变量。
说明:若a.c使用了b.c定义的foo()函数,则应当在b.h中声明extern int foo(int input);并在a.c
中通过#include <b.h>来使用foo。禁止通过在a.c中直接写extern int foo(int input);来使用foo,
后面这种写法容易在foo改变时可能导致声明和定义不一致。

Le 1.7 ici est également cohérent avec notre compréhension. Le fichier d'en-tête est une interface externe qui implémente le module. En général, seul le contenu suivant est autorisé:

  • Définition du type
  • Définition de macro
  • Déclaration de fonction (hors mise en œuvre)
  • Déclaration de variable (pas de définition)

Ce dernier point, je veux ajouter. Nous avons juste interdit de définir des variables dans le fichier d'en-tête, donc nos variables sont définies dans le fichier c. Par exemple, dans redis.c, une variable globale est définie:

/* Global vars */
struct redisServer server; /* server global state */

Une variable globale aussi importante conserve fondamentalement toutes les valeurs d'état d'une instance de redis-server et n'est utilisée que dans son propre redis.c, ce qui est impossible. Comment l'utiliser dans d'autres fichiers, l'instruction suivante doit être faite dans le fichier d'en-tête redis.h:

/*-----------------------------------------------------------------------------
 * Extern declarations
 *----------------------------------------------------------------------------*/

extern struct redisServer server;

À propos de la définition de type

Utilisez généralement struct pour définir une structure, similaire à la classe dans un langage de haut niveau.

Par exemple, les chaînes dans redis sont généralement stockées à l'aide de la structure de données sds. La définition de la structure est la suivante:

struct sdshdr {

    // buf 中已占用空间的长度
    int len;

    // buf 中剩余可用空间的长度
    int free;

    // 数据空间
    char buf[];
};

De plus, en langage C, typedef sera largement utilisé pour définir un alias de type.

Vous pouvez vous référer à ce tutoriel pour plus de détails:

https://www.runoob.com/cprogramming/c-typedef.html

À propos des pointeurs

Connaissances de base: https://www.runoob.com/cprogramming/c-pointers.html

Permettez-moi de parler de ma compréhension des pointeurs ici. Les pointeurs pointent généralement vers une adresse mémoire. Vous pouvez d'abord ignorer le type de ce pointeur. En fait, lorsque nous ne nous soucions pas du type de données à l'adresse vers laquelle il pointe, nous pouvons le définir directement void * ptr.

Ce pointeur est supposé pointer vers l'adresse A. Lorsque nous pensons que ce qui précède est stocké en tant que caractère, nous pouvons convertir ce pointeur de void * en type char *, puis déréférencer le pointeur, car le type char n'occupe qu'un seul Octet, il suffit donc de partir de la position pointée par le pointeur, de prendre le contenu actuel de cet octet, puis de l'analyser en tant que char, vous pouvez obtenir la valeur char à cette adresse.

Si nous convertissons de force void * en int *, lors du déréférencement, nous prendrons 4 octets à partir de la position actuelle du pointeur, car l'entier occupe 4 octets, puis le convertirons en entier.

En général, lors du déréférencement d'un pointeur, la première chose à faire est de regarder le type de données du pointeur actuel, comme le pointeur int *, puis de pointer vers int, il faudra 4 octets pour déréférencer; s'il pointe vers une structure, Il calculera le nombre d'octets occupés par la structure, puis prendra les octets correspondants pour déréférencer la variable du type de structure.

Dans cette partie, vous pouvez jeter un œil à ceci:

https://www.runoob.com/cprogramming/c-data-types.html

https://www.runoob.com/cprogramming/c-pointer-arithmetic.html

Initialisation des éléments de configuration lors du processus de démarrage de redis

J'en ai dit beaucoup dans la section précédente. Nous ne sommes pas assez pour parler du processus de démarrage complet de redis dans cette conférence. Il peut y avoir deux conférences. Cette conférence en expliquera d'abord une partie.

L'entrée de démarrage se trouve dans la méthode principale de redis.c. Si vous utilisez mon code pour créer un environnement de débogage, vous pouvez démarrer directement 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 lieu, définir le fuseau horaire

  • 2 places, définissez le nombre de threads pour l'allocation de mémoire, ici sera réglé sur 1

  • À trois endroits, définissez le pointeur de fonction lorsque l'oom se produit. Le pointeur de fonction pointe vers une fonction, similaire à java 8, dans l'expression lambda, lève une référence de méthode au flux; le pointeur de fonction sera rappelé lorsque l'oom. Il est similaire au modèle de conception de modèle ou au modèle de stratégie en java.

  • 4 places, définissez la graine du nombre aléatoire

  • A 5, l' obtention de l'heure courante, fixé à tvla variable

    Notez que l'adresse de tv est passée ici, ce qui est une utilisation typique en langage c, similaire à passer une référence d'objet en java, puis à l'intérieur de la méthode, le champ interne de l'objet sera modifié

  • 6 places, semez la fonction de hachage

  • À sept heures, le serveur est initialisé.

Voici sept points:

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;
}

Tout ce qui précède est commenté, nous ne pouvons pas le regarder en premier: copie, cluster, lua, etc., regardez d'abord l'autre.

Résumé

Cela fait longtemps que je n'ai pas touché c, et c'est oublié, mais en général, ce n'est pas difficile. Le plus dur c'est les fuites de mémoire, mais nous les utilisons juste pour le débogage et ne nous inquiétons pas de ces problèmes.

La partie pointeur a besoin d'un peu de fondement, et tout le monde peut prendre un moment pour l'apprendre.

Si vous avez des questions ou des suggestions, faites-le moi savoir.

Je suppose que tu aimes

Origine www.cnblogs.com/grey-wolf/p/12682760.html
conseillé
Classement