1. 程式人生 > >Redis原始碼閱讀——基於epoll的事件模型

Redis原始碼閱讀——基於epoll的事件模型

Redis的事件模型實現基於linux的epoll,sun的export,FreeBSD和Mac osx的queue,還有select;我們簡單分析下Redis基於epoll實現的事件模型。main函式呼叫initServer實現服務初始化:

void initServer(void) {
    int j;
	
	//SIG_DFL:預設訊號處理程式
	//SIG_IGN:忽略訊號的處理程式
    signal(SIGHUP, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
	
	//設定訊號處理函式
    setupSignalHandlers();

    if (server.syslog_enabled) {
        openlog(server.syslog_ident, LOG_PID | LOG_NDELAY | LOG_NOWAIT,
            server.syslog_facility);
    }

    server.pid = getpid();
    server.current_client = NULL;
	//客戶端連結串列
    server.clients = listCreate();
	//非同步關閉的客戶端
    server.clients_to_close = listCreate();
	//從機
    server.slaves = listCreate();
	
    server.monitors = listCreate();
	
    server.clients_pending_write = listCreate();
	
    server.slaveseldb = -1; /* Force to emit the first SELECT command. */
	
    server.unblocked_clients = listCreate();
	
    server.ready_keys = listCreate();
	
    server.clients_waiting_acks = listCreate();
	
    server.get_ack_from_slaves = 0;
	
    server.clients_paused = 0;
	//
    server.system_memory_size = zmalloc_get_memory_size();

    createSharedObjects();
	//
    adjustOpenFilesLimit();

	//構建aeEventLoop物件
    server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);
	//
    server.db = zmalloc(sizeof(redisDb)*server.dbnum);

    /* Open the TCP listening socket for the user commands. */
    if (server.port != 0 &&
		//啟動TCP監聽服務 --> 並設定為非阻塞
        listenToPort(server.port,server.ipfd,&server.ipfd_count) == C_ERR)
        exit(1);

    /* Open the listening Unix domain socket. */

	//啟動本地監聽服務
    if (server.unixsocket != NULL) {
        unlink(server.unixsocket); /* don't care if this fails */
        server.sofd = anetUnixServer(server.neterr,server.unixsocket,
            server.unixsocketperm, server.tcp_backlog);
        if (server.sofd == ANET_ERR) {
            serverLog(LL_WARNING, "Opening Unix socket: %s", server.neterr);
            exit(1);
        }
        anetNonBlock(NULL,server.sofd);
    }

    /* Abort if there are no listening sockets at all. */

	//如果沒有監聽套接字,則退出 --->初始化預設為0 -- > 在listenToPort每成功建立一個監聽套接字,自加1
    if (server.ipfd_count == 0 && server.sofd < 0) {
        serverLog(LL_WARNING, "Configured to not listen anywhere, exiting.");
        exit(1);
    }

    /* Create the Redis databases, and initialize other internal state. */
    for (j = 0; j < server.dbnum; j++) {
        server.db[j].dict = dictCreate(&dbDictType,NULL);
        server.db[j].expires = dictCreate(&keyptrDictType,NULL);
        server.db[j].blocking_keys = dictCreate(&keylistDictType,NULL);
        server.db[j].ready_keys = dictCreate(&setDictType,NULL);
        server.db[j].watched_keys = dictCreate(&keylistDictType,NULL);
        server.db[j].eviction_pool = evictionPoolAlloc();
        server.db[j].id = j;
        server.db[j].avg_ttl = 0;
    }
    server.pubsub_channels = dictCreate(&keylistDictType,NULL);
    server.pubsub_patterns = listCreate();
    listSetFreeMethod(server.pubsub_patterns,freePubsubPattern);
    listSetMatchMethod(server.pubsub_patterns,listMatchPubsubPattern);
    server.cronloops = 0;
    server.rdb_child_pid = -1;
    server.aof_child_pid = -1;
    server.rdb_child_type = RDB_CHILD_TYPE_NONE;
    server.rdb_bgsave_scheduled = 0;
    aofRewriteBufferReset();
    server.aof_buf = sdsempty();
    server.lastsave = time(NULL); /* At startup we consider the DB saved. */
    server.lastbgsave_try = 0;    /* At startup we never tried to BGSAVE. */
    server.rdb_save_time_last = -1;
    server.rdb_save_time_start = -1;
    server.dirty = 0;
    resetServerStats();
    /* A few stats we don't want to reset: server startup time, and peak mem. */
    server.stat_starttime = time(NULL);
    server.stat_peak_memory = 0;
    server.resident_set_size = 0;
    server.lastbgsave_status = C_OK;
    server.aof_last_write_status = C_OK;
    server.aof_last_write_errno = 0;
    server.repl_good_slaves_count = 0;
    updateCachedTime();

    /* Create the serverCron() time event, that's our main way to process
     * background operations. */
    if(aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
        serverPanic("Can't create the serverCron time event.");
        exit(1);
    }

    /* Create an event handler for accepting new connections in TCP and Unix
     * domain sockets. */

	//建立epoll事件監聽  --> 一個tcp套接字一個監聽檔案
    for (j = 0; j < server.ipfd_count; j++) {
        if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
            acceptTcpHandler,NULL) == AE_ERR)
            {
                serverPanic(
                    "Unrecoverable error creating server.ipfd file event.");
            }
    }
	//建立epoll事件監聽  --> 一個本地套接字一個監聽檔案
    if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
        acceptUnixHandler,NULL) == AE_ERR) serverPanic("Unrecoverable error creating server.sofd file event.");

    /* Open the AOF file if needed. */
    if (server.aof_state == AOF_ON) {
        server.aof_fd = open(server.aof_filename,
                               O_WRONLY|O_APPEND|O_CREAT,0644);
        if (server.aof_fd == -1) {
            serverLog(LL_WARNING, "Can't open the append-only file: %s",
                strerror(errno));
            exit(1);
        }
    }

    /* 32 bit instances are limited to 4GB of address space, so if there is
     * no explicit limit in the user provided configuration we set a limit
     * at 3 GB using maxmemory with 'noeviction' policy'. This avoids
     * useless crashes of the Redis instance for out of memory. */
    if (server.arch_bits == 32 && server.maxmemory == 0) {
        serverLog(LL_WARNING,"Warning: 32 bit instance detected but no memory limit set. Setting 3 GB maxmemory limit with 'noeviction' policy now.");
        server.maxmemory = 3072LL*(1024*1024); /* 3 GB */
        server.maxmemory_policy = MAXMEMORY_NO_EVICTION;
    }

    if (server.cluster_enabled) clusterInit();
    replicationScriptCacheInit();
    scriptingInit(1);
    slowlogInit();
    latencyMonitorInit();
    bioInit();
}

我們主要關注三個地方

1 構建aeEventLoop物件

	//構建aeEventLoop物件
    server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);

2 建立套接字監聽,分為TCP監聽和本地套接字監聽

    /* Open the TCP listening socket for the user commands. */
    if (server.port != 0 &&
		//啟動TCP監聽服務 --> 並設定為非阻塞
        listenToPort(server.port,server.ipfd,&server.ipfd_count) == C_ERR)
        exit(1);

    /* Open the listening Unix domain socket. */

	//啟動本地監聽服務
    if (server.unixsocket != NULL) {
        unlink(server.unixsocket); /* don't care if this fails */
        server.sofd = anetUnixServer(server.neterr,server.unixsocket,
            server.unixsocketperm, server.tcp_backlog);
        if (server.sofd == ANET_ERR) {
            serverLog(LL_WARNING, "Opening Unix socket: %s", server.neterr);
            exit(1);
        }
        anetNonBlock(NULL,server.sofd);
    }

3 將建立監聽的套接字加入EventLoop事件監聽

	//建立epoll事件監聽  --> 一個tcp套接字一個監聽檔案
    for (j = 0; j < server.ipfd_count; j++) {
        if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
            acceptTcpHandler,NULL) == AE_ERR)
            {
                serverPanic(
                    "Unrecoverable error creating server.ipfd file event.");
            }
    }
	//建立epoll事件監聽  --> 一個本地套接字一個監聽檔案
    if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
        acceptUnixHandler,NULL) == AE_ERR) serverPanic("Unrecoverable error creating server.sofd file event."); 

在分析如何構建aeEventLoop物件之前,我們先看下aeEventLoop的定義:

/* State of an event based program */
typedef struct aeEventLoop {
    int maxfd;   				//監聽的最大檔案號  
    int setsize; 				//可以註冊的事件的上限,預設為1024*10
    long long timeEventNextId;	//定時器事件的ID編號管理(分配ID號所用)  
    time_t lastTime;     		/* Used to detect system clock skew */
    aeFileEvent *events; 		//註冊的檔案事件,這些是需要程序關注的檔案  
    aeFiredEvent *fired; 		//poll結果,待處理的檔案事件的檔案號和事件型別  
    aeTimeEvent *timeEventHead;	//定時器時間連結串列 
    int stop;		 			//時間輪詢是否結束?  
    void *apidata; 				//檔案事件的輪詢資料和結果資料:poll; 三種輪詢方式:epoll(linux),select(windows),kqueue  
    aeBeforeSleepProc *beforesleep;
} aeEventLoop; 

接著分析aeCreateEventLoop函式:

//底層epoll多路複用初始化,然後存放在aeEventLoop中 void * 型別的apidata,隱藏了底層的實現。

/*****************************************************************************
 * 函 數 名  : aeCreateEventLoop
 * 函式功能  : 構建aeEventLoop物件eventLoop  
 * 輸入引數  : int setsize  訊息佇列大小
 * 輸出引數  : 無
 * 返 回 值  : aeEventLoop
 * 呼叫關係  : 
 * 記    錄
 * 1.日    期: 2017年10月21日
 *   作    者: zyz
 *   修改內容: 新生成函式
*****************************************************************************/
aeEventLoop *aeCreateEventLoop(int setsize) {
	//setsize指的是放到eventloop中最大描述符大小,也就是該事件迴圈中可能有多少個fd。
    aeEventLoop *eventLoop;
    int i;

    if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;
    eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize);
    eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize);
	//eventLoop->events和event_loop->fired都是一個set size大小的
	//陣列,表明了event loop是用陣列下標來當fd。
    if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;
    eventLoop->setsize = setsize;
    eventLoop->lastTime = time(NULL);
    eventLoop->timeEventHead = NULL;
    eventLoop->timeEventNextId = 0;
	//eventLoop->timeEventHead儲存第一個時間事件,值得注意的是時間事件
	//連結串列不是有序列表,因此會後面的檢索會是O(n)
	
    eventLoop->stop = 0;			 	//指示事件迴圈是否繼續進行。
    eventLoop->maxfd = -1;			 	//事件連結串列中目前最大的fd,用於減小檢索範圍
    eventLoop->beforesleep = NULL;		//每輪迴圈前的回撥函式
    // aeApiCreate封裝了不同多路複用的實現,如linux的epoll,sun的export,FreeBSD和Mac osx的queue,還有select。
    if (aeApiCreate(eventLoop) == -1) goto err;
    /* Events with mask == AE_NONE are not set. So let's initialize the
     * vector with it. */
    for (i = 0; i < setsize; i++)
        eventLoop->events[i].mask = AE_NONE;// AE_NONE來指示該fd沒有放入事件迴圈庫
    return eventLoop;

err:
    if (eventLoop) {
        zfree(eventLoop->events);
        zfree(eventLoop->fired);
        zfree(eventLoop);
    }
    return NULL;
} 

很簡單,分配一個aeEventLoop 物件,然後一折物件為引數,呼叫底層實現函式aeApiCreate,我們看下epoll型別的實現函式:

/*****************************************************************************
 * 函 數 名  : aeApiCreate
 * 函式功能  : 封裝epoll事件驅動構建函式
 * 輸入引數  : aeEventLoop *eventLoop  事件迴圈描述結構物件
 * 輸出引數  : 無
 * 返 回 值  : static
 * 呼叫關係  : 
 * 記    錄
 * 1.日    期: 2017年10月23日
 *   作    者: zyz
 *   修改內容: 新生成函式
*****************************************************************************/
static int aeApiCreate(aeEventLoop *eventLoop) {
	//包含一個epoll專用檔案描述符和一個epoll_event物件指標
    aeApiState *state = zmalloc(sizeof(aeApiState));

    if (!state) return -1;
	//建立setsize個epoll_event
    state->events = zmalloc(sizeof(struct epoll_event)*eventLoop->setsize);
    if (!state->events) {
        zfree(state);
        return -1;
    }
	//該函式生成一個epoll專用的檔案描述符。它其實是在核心申請一空間,用來存放你想關注的socket fd上是否發生以及發生了什麼事件
    state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */
    if (state->epfd == -1) {
        zfree(state->events);
        zfree(state);
        return -1;
    }
    eventLoop->apidata = state;
    return 0;
} 

非常簡單粗暴,構建一個aeApiState物件,這個物件包含一個檔案描述符和一個epoll_event 物件指標。

typedef struct aeApiState {
    int epfd;
    struct epoll_event *events;
} aeApiState;

然後呼叫epoll_create建立事件模型,將得到檔案描述符賦值給aeApiState物件的epfd;分配指定size的poll_event物件記憶體,將地址賦值給aeApiState物件的events;然後aeApiState物件地址賦值給aeEventLoop物件的apidata;

第二件事是建立套接字監聽,不是這裡關注的重點,我們只需知道得到一個存放套接字描述符的陣列。這些套接字就是被監聽的物件。

第三件事是將這些套接字封裝後加入epoll監聽服務。我們看下函式aeCreateFileEvent:

/*****************************************************************************
 * 函 數 名  : aeCreateFileEvent
 * 函式功能  : 建立事件監聽物件
 * 輸入引數  : aeEventLoop *eventLoop  事件監聽服務描述符物件指標
               int fd                  被監聽檔案描述符
               int mask                監聽事件掩碼
               aeFileProc *proc        事件響應函式
               void *clientData        引數
 * 輸出引數  : 無
 * 返 回 值  : 
 * 呼叫關係  : 
 * 記    錄
 * 1.日    期: 2018年03月06日
 *   作    者: zyz
 *   修改內容: 新生成函式
*****************************************************************************/
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
        aeFileProc *proc, void *clientData)
{
    if (fd >= eventLoop->setsize) {
        errno = ERANGE;
        return AE_ERR;
    }
    aeFileEvent *fe = &eventLoop->events[fd];
	//呼叫底層實現,將被監聽檔案描述符加入監聽服務
    if (aeApiAddEvent(eventLoop, fd, mask) == -1)
        return AE_ERR;
    fe->mask |= mask;
    if (mask & AE_READABLE) fe->rfileProc = proc;
    if (mask & AE_WRITABLE) fe->wfileProc = proc;
    fe->clientData = clientData;
    if (fd > eventLoop->maxfd)
        eventLoop->maxfd = fd;
    return AE_OK;
}

在繼續分析之前我們先看下一個結構體:

/* File event structure */
/* 檔案事件結構體 */  
typedef struct aeFileEvent {
	//只為讀事件或者寫事件中的1種	
    int mask; /* one of AE_(READABLE|WRITABLE) */
	//讀方法  
    aeFileProc *rfileProc;
	//寫方法 
    aeFileProc *wfileProc;
	 //客戶端資料  
    void *clientData;
} aeFileEvent;

在呼叫aeCreateEventLoop函式時,分配了指定大小的記憶體,用於在抽象層代表可以監聽指定數量的檔案描述符。在函式中就根據指定檔案描述符取出指定的aeFileEvent 物件,然後以該物件為引數,呼叫底層實現函式aeApiAddEvent

//EPOLL_CTL_ADD:	註冊新的fd到epfd中;
//EPOLL_CTL_MOD:	修改已經註冊的fd的監聽事件;
//EPOLL_CTL_DEL:	從epfd中刪除一個fd;

/*****************************************************************************
 * 函 數 名  : aeApiAddEvent
 * 函式功能  : epoll事件註冊函式封裝
 * 輸入引數  : aeEventLoop *eventLoop  事件驅動模型結構物件
               int fd                  被監聽事件的檔案描述符
               int mask                事件掩碼
 * 輸出引數  : 無
 * 返 回 值  : static
 * 呼叫關係  : 
 * 記    錄
 * 1.日    期: 2017年10月23日
 *   作    者: zyz
 *   修改內容: 新生成函式
*****************************************************************************/
static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
    aeApiState *state = eventLoop->apidata;
    struct epoll_event ee = {0}; /* avoid valgrind warning */
    /* If the fd was already monitored for some event, we need a MOD
     * operation. Otherwise we need an ADD operation. */
    int op = eventLoop->events[fd].mask == AE_NONE ?
            EPOLL_CTL_ADD : EPOLL_CTL_MOD;
	
	//events可以是以下幾個巨集的集合:
	//EPOLLIN:         觸發該事件,表示對應的檔案描述符上有可讀資料。(包括對端SOCKET正常關閉);
	//EPOLLOUT:        觸發該事件,表示對應的檔案描述符上可以寫資料;
	//EPOLLPRI:        表示對應的檔案描述符有緊急的資料可讀(這裡應該表示有帶外資料到來);
	//EPOLLERR:        表示對應的檔案描述符發生錯誤;
	//EPOLLHUP:        表示對應的檔案描述符被結束通話;
	//EPOLLET:         將EPOLL設為邊緣觸發(Edge Triggered)模式,這是相對於水平觸發(Level Triggered)來說的。
	//EPOLLONESHOT:    只監聽一次事件,當監聽完這次事件之後,如果還需要繼續監聽這個socket的話,需要再次把這個socket加入到EPOLL佇列裡。
    ee.events = 0;
    mask |= eventLoop->events[fd].mask; /* Merge old events */
    if (mask & AE_READABLE) ee.events |= EPOLLIN;
    if (mask & AE_WRITABLE) ee.events |= EPOLLOUT;
    ee.data.fd = fd;
    if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;
    return 0;
}

基於epoll的實現方式,實際呼叫的是epoll_ctl,將被監聽檔案描述符加入監聽服務。在這裡事件掩碼為AE_READABLE,對應到epoll為EPOLLIN,即只對輸入事件感興趣。

到這裡,事件監聽服務已經構建完成,下面就是事件處理的部分了

事件處理部分在函式中實現,在main函式中呼叫

/*****************************************************************************
 * 函 數 名  : aeMain
 * 函式功能  : 事件迴圈處理
 * 輸入引數  : aeEventLoop *eventLoop  事件監聽服務描述符物件指標
 * 輸出引數  : 無
 * 返 回 值  : 
 * 呼叫關係  : 
 * 記    錄
 * 1.日    期: 2018年03月06日
 *   作    者:  
 *   修改內容: 新生成函式
*****************************************************************************/
void aeMain(aeEventLoop *eventLoop) {
    eventLoop->stop = 0;
    while (!eventLoop->stop) {						//以死迴圈的方式進入輪詢,直到stop被置為非0
        if (eventLoop->beforesleep != NULL)
            eventLoop->beforesleep(eventLoop);		//前期處理,回撥函式
		//處理訊息
        aeProcessEvents(eventLoop, AE_ALL_EVENTS);	//事件處理(見下面)  
    }
}


相關推薦

Redis原始碼閱讀——基於epoll事件模型

Redis的事件模型實現基於linux的epoll,sun的export,FreeBSD和Mac osx的queue,還有select;我們簡單分析下Redis基於epoll實現的事件模型。main函式呼叫initServer實現服務初始化:void initServer(v

Redis epoll事件模型

Redis的事件模型在這裡我們用ae_epoll.c,epoll詳細工作原理  https://blog.csdn.net/luolaifa000/article/details/84190836 一、redis對原始的epoll資料結構進行了封裝  二、

Redis原始碼閱讀(六)叢集-故障遷移(下)

Redis原始碼閱讀(六)叢集-故障遷移(下)   最近私人的事情比較多,沒有抽出時間來整理部落格。書接上文,上一篇裡總結了Redis故障遷移的幾個關鍵點,以及Redis中故障檢測的實現。本篇主要介紹叢集檢測到某主節點下線後,是如何選舉新的主節點的。注意到Redis叢集是無中心的,那麼使用分散式一

Redis原始碼閱讀(四)叢集-請求分配

    叢集搭建好之後,使用者傳送的命令請求可以被分配到不同的節點去處理。那Redis對命令請求分配的依據是什麼?如果節點數量有變動,命令又是如何重新分配的,重分配的過程是否會阻塞對外提供的服務?接下來會從這兩個問題入手,分析Redis3.0的原始碼實現。 1. 分配依據—

Redis原始碼閱讀筆記--資料庫redisDb

一. 資料庫 Redis的資料庫使用字典作為底層實現,資料庫的增、刪、查、改都是構建在字典的操作之上的。 redis伺服器將所有資料庫都儲存在伺服器狀態結構redisServer(redis.h/redisServer)的db陣列(應該是一個連結串列)裡:

Redis原始碼閱讀之: 環境搭建及準備

1.下載原始碼 2.IDE配置(Clion on windows) ps:Clion特別適合看C程式碼, 而且跨平臺 直接進入clion開啟redis原始碼的資料夾 沒mingw則安裝下m

Redis原始碼閱讀筆記—sds

Redis系統當中,針對字串進行的更加完善的封裝,建立了一個動態字串,並構建了大量的實用api。相關的實現程式碼為sds.h及sds.c,以下為我的原始碼閱讀筆記。內容較多,逐步更新typedefchar *sds;  struct __attribute__ ((__pac

redis原始碼閱讀筆記-dict.h

dict.h 在redis中,dict.h主要是hash的底層實現方式。 在dict.h中主要是一些資料結構的定義,以及一些巨集函式的定義相關的內容。 雜湊節點定義 原始碼中的dictEntry就是雜湊節點的相關定義 typedef struct dictEnt

redis原始碼閱讀—adlist.c

adlist.c adlist.c檔案中主要是一些雙向連結串列的操作的實現。 listCreate函式 //listCreate函式類似於一個list的建構函式 //直接通過zmalloc函式申請list大小的空間,並且將pre,next指標都置為NULL //

nginx原始碼閱讀(十五).事件模組小結(從解析配置到事件的處理)

前言 本小節主要是整理一下前幾節分析的nginx的核心模組的ngx_events_module以及事件模組,關於事件模組什麼時候初始化以及事件的處理等,因此不會涉及到太多具體的程式碼,主要是把握事件模組的整體。 配置項結構體的建立及賦值 在使

基於EPOLL模型的局域網聊天室和Echo服務器

受限 urn let event flag 選擇 idt block 1-1 一、EPOLL的優點 在Linux中,select/poll/epoll是I/O多路復用的三種方式,epoll是Linux系統上獨有的高效率I/O多路復用方式,區別於select/poll。先

redis 文件事件模型

desc edi org sockaddr sel 事件處理 CI sizeof logs 參考文獻: 深入剖析 redis 事件驅動 Redis 中的事件循環 深入了解epoll (轉) Redis自己的事件模型 ae EPOLL(7) Linux IO模式及

閱讀基於sketch的軟件定義網絡測量數據平面硬件模型

導致 分組 數據 png 規則 blog bloom 負責 提高 概要 硬件實現 基於sketch 功能:采集包數、流長數據,恢復五元組 重點:高速條件下性能較好,節省硬件資源 摘要: 提出一種基於sketch 數據結構的軟件定義測量數據平面硬件模型,並在以現場可編程邏輯

原始碼基於IEEE 14匯流排標準的複合微電網SIMULINK模型

本程式設計了一種基於IEEE 14匯流排標準的複合微電網模型,該微電網模型包括柴油發電機、PV模型、電池儲能系統、電弧爐等非線性負載。微電網採用併網執行方式。 本模型的參考文獻: A new approach for soft synchronization of microgri

Redis值集合物件原始碼閱讀

setTypeCreate:返回一個集合物件 robj *setTypeCreate(robj *value) { if (isObjectRepresentableAsLongLong(value,NULL) == REDIS_OK) return creat

Redis之列表物件原始碼閱讀

listTypeTryConversion:嘗試將列表物件轉換成linkedlist編碼 void listTypeTryConversion(robj *subject, robj *value) { // 確保 subject 為 ZIPLIST 編碼 if (su

Redis之雜湊物件原始碼閱讀

hashTypeTryConversion:對傳入的引數進行檢查是否需要從ziplist轉換成hashtable void hashTypeTryConversion(robj *o, robj **argv, int start, int end) { int i;

Redis之資料庫實現原始碼閱讀

lookupKey:查詢指定的鍵,如果存在返回對應的值 robj *lookupKey(redisDb *db, robj *key) { // 查詢鍵空間 dictEntry *de = dictFind(db->dict,key->ptr);

原始碼基於MATLAB/SIMULINK的光伏電池板模型

基於電路的光伏電池模擬模型,用於估計光伏面板的IV特性曲線相對於環境引數(溫度和輻照)和電池引數(寄生電阻和理想因子)的變化。 A circuit based simulation model for a PV cell for estimating the IV characteri

基於idea搭建Hadoop原始碼閱讀環境

Hadoop原始碼是這麼做,其他原始碼環境也類似。 1、到官網下載Hadoop原始碼包,例如hadoop-2.6.5-src.tar.gz. https://www-eu.apache.org/dist/hadoop/common/hadoop-2.6.5/ 2、將下載的原始碼包解壓到某