對於redis框架的理解(二)


之前梳理過redis main函數主體流程

大體是 initServerConfig() -> loadServerConfig()

-> daemonize() -> initServer() -> aeSetBeforeSleepProc()

->aeMain() -> aeDeleteEventLoop();

 

initServerConfig() 初始化server的配置

loadServerConfig()會從配置文件里加載對應的配置

daemonize()創建守護進程

看一下daemonize的函數組成

void daemonize(void) {
  int fd;

  if (fork() != 0) exit(0); /* parent exits */
  //設置為首進程
  //之前parent和child運行在同一個session里,

  //parent是會話(session)的領頭進程,
  //parent進程作為會話的領頭進程,如果exit結束執行的話,

  //那么子進程會成為孤兒進程,並被init收養。
  //執行setsid()之后,child將重新獲得一個新的會話(session)id。
  setsid(); /* create a new session */

  /* Every output goes to /dev/null. If Redis is daemonized but
  * the 'logfile' is set to 'stdout' in the configuration file
  * it will not log at all. */

  //  /dev/null相當於黑洞文件,所有寫入他的數據都會消失,

  //從他里面讀不出數據
  if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
    dup2(fd, STDIN_FILENO);    //將標准輸入重定向到fd
    dup2(fd, STDOUT_FILENO); //將標准輸出重定向到fd
    dup2(fd, STDERR_FILENO); //將錯誤輸出重定向到fd
    if (fd > STDERR_FILENO)

      close(fd);

   }
}

 

着重解釋下dup2這個函數

int dup(int oldfd);

復制oldfd所指向的文件描述符,返回系統目前未使用的最小的文件描述符。

新的文件描述符和oldfd指向一個文件,

他們共享讀寫,枷鎖等權限,當一個文件描述符操作lseek,另一個也會隨着偏移。

但是他們不共享close-on-exec。

int dup2(int oldfd, int newfd);

復制oldfd到newfd,如果newfd指向的文件打開,那么會關閉該文件。

dup2失敗后返回-1,成功則共享文件狀態。

 

接下來看看initServer函數做了些什么 下面是initServer內部的幾個步驟

//由於initserver是守護進程,忽略sighup
//sighub在控制終端關閉的時候會發給session首進程
//session首進程退出時,該信號被發送到該session中的前台進程組中的每一個進程
signal(SIGHUP, SIG_IGN);

//寫管道發現讀進程終止時產生sigpipe信號,

//寫已終止的SOCK_STREAM套接字同樣會產生此信號
signal(SIGPIPE, SIG_IGN);

server.pid = getpid();
server.current_client = NULL;
server.clients = listCreate(); //創建客戶隊列
server.clients_to_close = listCreate(); //創建關閉隊列
server.slaves = listCreate(); //創建從機隊列
server.monitors = 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;

//創建別的模塊公用的對象
createSharedObjects(); //創建共享對象
adjustOpenFilesLimit(); //改變可打開文件的最大數量

可以看看adjustOpenFilesLimit() 里邊做了什么

void adjustOpenFilesLimit(void) {
    rlim_t maxfiles = server.maxclients+REDIS_MIN_RESERVED_FDS;
    struct rlimit limit;

    //獲取每個進程能創建的各種系統資源的最大數量
    if (getrlimit(RLIMIT_NOFILE,&limit) == -1) {
    redisLog(REDIS_WARNING,"Unable to obtain the current NOFILE limit (%s),

    assuming 1024 and setting the max clients configuration accordingly.",
    strerror(errno));
    //1024 - 目前redis最小保留的用於其他功能的描述符
    server.maxclients = 1024-REDIS_MIN_RESERVED_FDS;
    } else {
        //系統當前(軟)限制
        rlim_t oldlimit = limit.rlim_cur;

        /* Set the max number of files if the current limit is not enough
        * for our needs. */
        if (oldlimit < maxfiles) {
        //最大軟件限制
        rlim_t bestlimit;
        int setrlimit_error = 0;

        /* Try to set the file limit to match 'maxfiles' or at least
        * to the higher value supported less than maxfiles. */
        //嘗試設置限制數符合maxfiles或者最接近
        bestlimit = maxfiles;
        //循環判斷,如果最大的連接數大於當前系統能使用的最大軟件限制
        while(bestlimit > oldlimit) {
        //設置每次遞減的數量
        rlim_t decr_step = 16;
        //設置當前軟件限制
        limit.rlim_cur = bestlimit;
        limit.rlim_max = bestlimit;
        //設置成功則斷開,不成功繼續循環,找到最接近的最大限制
        if (setrlimit(RLIMIT_NOFILE,&limit) != -1) break;
        //設置限制錯誤碼
        setrlimit_error = errno;

        /* We failed to set file limit to 'bestlimit'. Try with a
        * smaller limit decrementing by a few FDs per iteration. */

        //最大限制遞減,如果小於規定值那么退出循環
        if (bestlimit < decr_step) break;
        bestlimit -= decr_step;
            /* Assume that the limit we get initially is still valid if

      * our last try was even lower. */
     
 //最大連接數小於當前系統允許的資源數量,那么擴充為系統允許的數量
      if (bestlimit < oldlimit) bestlimit = oldlimit;

      //這個條件用於處理最大連接數
      if (bestlimit < maxfiles) {
      //保存當前服務器客戶隊列最大數量
        int old_maxclients = server.maxclients;
        //服務器客戶隊列最大數量
        server.maxclients = bestlimit-REDIS_MIN_RESERVED_FDS;
        if (server.maxclients < 1) {
          redisLog(REDIS_WARNING,"Your current 'ulimit -n' "
          "of %llu is not enough for Redis to start. "
          "Please increase your open file limit to at least "
          "%llu. Exiting.",
          (unsigned long long) oldlimit,
          (unsigned long long) maxfiles);
          exit(1);
        }

   }

 

 回到initServer函數內部

adjustOpenFilesLimit()函數過后是調用

server.el = aeCreateEventLoop(server.maxclients+REDIS_EVENTLOOP_FDSET_INCR);

這個函數是創建基本的時間循環結構。這個api寫在Ae.c中,這個文件主要負責創建事件輪詢

的結構,創建文件讀寫事件,刪除文件讀寫事件,刪除事件輪詢結構,派發文件讀寫功能等,下一屆再講。

然后是創建了一個定時器回調函數

if(aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
redisPanic("Can't create the serverCron time event.");
exit(1);
}

接着創建了 TCP的回調函數,主要用於有新的連接到來觸發acceptTcpHandler

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.");
}
}

 

//然后添加了udp的回調
if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
acceptUnixHandler,NULL) == AE_ERR) redisPanic("Unrecoverable error creating server.sofd file event.");

 

這就是initServer大體的流程

下一節我們詳細講一下eventloop結構和其中封裝的幾種網絡模型。

我的微信號

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM