Memcached源码分析之memcached.c

Posted Beyond it

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Memcached源码分析之memcached.c相关的知识,希望对你有一定的参考价值。

memcached.c 由于代码太多,在此省略了部分代码,例如UPD连接,二进制协议,某些错误输出和调试输出等,建议从main函数开始看起。

  1.  #include "memcached.h"
  2. //尝试从socket中读取数据的结果枚举
  3. enum try_read_result {
  4.     READ_DATA_RECEIVED,
  5.     READ_NO_DATA_RECEIVED,
  6.     READ_ERROR, /** an error occured (on the socket) (or client closed connection) */
  7.     READ_MEMORY_ERROR /** failed to allocate more memory */
  8. };
  9. struct stats stats; //全局统计变量
  10. struct settings settings; //全局配置变量
  11. static conn *listen_conn = NULL;  //全局的监听连接对象
  12. static int max_fds; //fds上限
  13. static struct event_base *main_base; //主线程的event_base 对象
  14. //向客户端发送数据的结果枚举
  15. enum transmit_result {
  16.     TRANSMIT_COMPLETE, /** All done writing. */
  17.     TRANSMIT_INCOMPLETE, /** More data remaining to write. */
  18.     TRANSMIT_SOFT_ERROR, /** Can‘t write any more right now. */
  19.     TRANSMIT_HARD_ERROR /** Can‘t write (c->state is set to conn_closing) */
  20. };
  21. extern pthread_mutex_t conn_lock; //连接锁
  22.  
  23. /**
  24. 创建连接函数,参数为:
  25. sfd 要监听的socket fd
  26. init_state 连接的初始化状态conn_states
  27. event_flags 监听的事件
  28. read_buffer_size 读缓存大小
  29. transport 监听的socket 类型
  30. base event_base
  31.  
  32. 每监听一个fd(listen fd和client fd)都会创建这样一个conn来保存相关信息,表示一个“连接”,
  33. 无论连接是已经连接上了,还是仅仅处于listen状态。
  34. */
  35. conn *conn_new(const int sfd, enum conn_states init_state,
  36.                 const int event_flags,
  37.                 const int read_buffer_size, enum network_transport transport,
  38.                 struct event_base *base) {
  39.     conn *c;
  40.     assert(sfd >= 0 && sfd < max_fds);
  41.     c = conns[sfd];
  42.     if (NULL == c) {
  43.         if (!(c = (conn *)calloc(1, sizeof(conn)))) {
  44.             STATS_LOCK();
  45.             stats.malloc_fails++;
  46.             STATS_UNLOCK();
  47.             fprintf(stderr, "Failed to allocate connection object\n");
  48.             return NULL;
  49.         }
  50.         MEMCACHED_CONN_CREATE(c);
  51.         c->rbuf = c->wbuf = 0;
  52.         //c->xx省略部分初始化代码
  53.         c->hdrsize = 0;
  54.         c->rbuf = (char *)malloc((size_t)c->rsize);
  55.         c->wbuf = (char *)malloc((size_t)c->wsize);
  56.         c->ilist = (item **)malloc(sizeof(item *) * c->isize);
  57.         c->suffixlist = (char **)malloc(sizeof(char *) * c->suffixsize);
  58.         c->iov = (struct iovec *)malloc(sizeof(struct iovec) * c->iovsize);
  59.         c->msglist = (struct msghdr *)malloc(sizeof(struct msghdr) * c->msgsize);
  60.         if (c->rbuf == 0 || c->wbuf == 0 || c->ilist == 0 || c->iov == 0 ||
  61.                 c->msglist == 0 || c->suffixlist == 0) {
  62.             conn_free(c);
  63.             STATS_LOCK();
  64.             stats.malloc_fails++;
  65.             STATS_UNLOCK();
  66.             fprintf(stderr, "Failed to allocate buffers for connection\n");
  67.             return NULL;
  68.         }
  69.         STATS_LOCK();
  70.         stats.conn_structs++;
  71.         STATS_UNLOCK();
  72.         c->sfd = sfd;
  73.         conns[sfd] = c;
  74.     }
  75.     c->transport = transport;
  76.     c->protocol = settings.binding_protocol;
  77.     if (!settings.socketpath) {
  78.         c->request_addr_size = sizeof(c->request_addr);
  79.     } else {
  80.         c->request_addr_size = 0;
  81.     }
  82.     if (transport == tcp_transport && init_state == conn_new_cmd) {
  83.         if (getpeername(sfd, (struct sockaddr *) &c->request_addr,
  84.                         &c->request_addr_size)) {
  85.             perror("getpeername");
  86.             memset(&c->request_addr, 0, sizeof(c->request_addr));
  87.         }
  88.     }
  89.     if (settings.verbose > 1) {
  90.        //省略向终端输出调试的代码
  91.     }
  92.     c->state = init_state;
  93.     //c->xxx 省略部分初始化代码
  94.     c->noreply = false;
  95.     //创建事件,处理函数为event_handler,并把conn 连接对象传入event_handler中。
  96.     //(主线程调用conn_new时:)在主线程,创建完listenfd后,调用此函数监听网络连接事件,此时conn对象的conn_state为conn_listening
  97.    
  98.     /**(建议看到worker线程调用conn_new时才看以下解析)
  99.     (worker线程调用conn_new时)在worker线程,收到主线程丢过来的client fd时,调用此函数监听来自client fd的网络事件。
  100.    
  101.      也就是说,无论是主线程还是worker线程,都会调用此函数conn_new,创建conn连接对象,同时监听各自的fd。
  102.      而且都是调用event_handler作处理,只是不一样的fd, 不一样的conn对象(即下面的(void *) c)
  103.      进入 event_handler看看都做了啥?
  104.     */
  105.     event_set(&c->event, sfd, event_flags, event_handler, (void *)c);
  106.     event_base_set(base, &c->event); //为事件设置事件基地
  107.     c->ev_flags = event_flags;
  108.     if (event_add(&c->event, 0) == -1) { //把事件加入监听
  109.         perror("event_add");
  110.         return NULL;
  111.     }
  112.     STATS_LOCK();
  113.     stats.curr_conns++;
  114.     stats.total_conns++;
  115.     STATS_UNLOCK();
  116.     MEMCACHED_CONN_ALLOCATE(c->sfd);
  117.     return c;
  118. }
  119. //向客户端输出字符串
  120. static void out_string(conn *c, const char *str) {
  121.     size_t len;
  122.     assert(c != NULL);
  123.     if (c->noreply) {
  124.         if (settings.verbose > 1)
  125.             fprintf(stderr, ">%d NOREPLY %s\n", c->sfd, str);
  126.         c->noreply = false;
  127.         conn_set_state(c, conn_new_cmd);
  128.         return;
  129.     }
  130.     if (settings.verbose > 1)
  131.         fprintf(stderr, ">%d %s\n", c->sfd, str);
  132.     /* Nuke a partial output... */
  133.     c->msgcurr = 0;
  134.     c->msgused = 0;
  135.     c->iovused = 0;
  136.     add_msghdr(c); //添加一个msghdr
  137.     len = strlen(str);
  138.     if ((len + 2) > c->wsize) {
  139.         /* ought to be always enough. just fail for simplicity */
  140.         str = "SERVER_ERROR output line too long";
  141.         len = strlen(str);
  142.     }
  143.     memcpy(c->wbuf, str, len); //把要发送的字符串写入wbuf字段中
  144.     memcpy(c->wbuf + len, "\r\n", 2); //添加换行回车符
  145.     c->wbytes = len + 2;
  146.     c->wcurr = c->wbuf;
  147.     conn_set_state(c, conn_write); //把连接状态设置为conn_write,则状态机进入conn_write分支执行输出
  148.     c->write_and_go = conn_new_cmd; //当状态机完成输出后要切换到的状态为conn_new_cmd
  149.     return;
  150. }
  151. /**
  152. 无法分配内存时经常会调用此函数,例如内存空间已满,但又无法淘汰旧的item时,则向客户端响应一个错误
  153. */
  154. static void out_of_memory(conn *c, char *ascii_error) {
  155.     const static char error_prefix[] = "SERVER_ERROR ";
  156.     const static int error_prefix_len = sizeof(error_prefix) - 1;
  157.     if (c->protocol == binary_prot) {
  158.         /* Strip off the generic error prefix; it‘s irrelevant in binary */
  159.         if (!strncmp(ascii_error, error_prefix, error_prefix_len)) {
  160.             ascii_error += error_prefix_len;
  161.         }
  162.         write_bin_error(c, PROTOCOL_BINARY_RESPONSE_ENOMEM, ascii_error, 0);
  163.     } else {
  164.         out_string(c, ascii_error);
  165.     }
  166. }
  167. /*
  168.  * 解析文本协议时,执行set/add/replace 命令并把value数据塞到item之后,调用此函数。
  169.  * 这个函数的作用比较简单,主要是做一些收尾工作。
  170.  *
  171.  */
  172. static void complete_nread_ascii(conn *c) {
  173.     assert(c != NULL);
  174.     item *it = c->item;
  175.     int comm = c->cmd;
  176.     enum store_item_type ret;
  177.     pthread_mutex_lock(&c->thread->stats.mutex);
  178.     c->thread->stats.slab_stats[it->slabs_clsid].set_cmds++;
  179.     pthread_mutex_unlock(&c->thread->stats.mutex);
  180.     if (strncmp(ITEM_data(it) + it->nbytes - 2, "\r\n", 2) != 0) {
  181.         out_string(c, "CLIENT_ERROR bad data chunk");
  182.     } else {
  183.       ret = store_item(it, comm, c);
  184.     }
  185.       switch (ret) {
  186.       case STORED:
  187.           out_string(c, "STORED");
  188.           break;
  189.       //省略其它
  190.       default:
  191.           out_string(c, "SERVER_ERROR Unhandled storage type.");
  192.       }
  193.     }
  194.     //释放引用,注意仅仅是一个释放引用的逻辑,不一定把内存空间给释放掉
  195.     //具体是否要把内存空间释放,说见item_remove和do_item_remove函数。而在此处,
  196.     //一般都只是释放引用而已。
  197.     item_remove(c->item); /* release the c->item reference */
  198.     c->item = 0;
  199. }
  200. //重置命令处理
  201. static void reset_cmd_handler(conn *c) {
  202.     c->cmd = -1;
  203.     c->substate = bin_no_state;
  204.     if(c->item != NULL) {
  205.         item_remove(c->item);
  206.         c->item = NULL;
  207.     }
  208.     conn_shrink(c);
  209.     /**
  210.     不知道你有没有在下面这个地方困惑过。。反正我是纠结了很久。。。
  211.    
  212.     这个c->rbytes到底什么时候开始不为0了?为0的话,怎么执行这次的命令?
  213.     回顾一下我们是怎么会调用到reset_cmd_handler这个地方:
  214.     1)主线程监听到listen fd有连接上来,进入drive_machine的conn_listen状态,
  215.         然后accept连接得到client fd,再dispatch_conn_new把client fd丢给某个worker线程
  216.     2)某个worker线程收到主线程丢过来的client fd之后,把它加到那个worker线程自己的
  217.         event_base,然后注册event_handler(event_handler主要是调drive_machine)作为事件处理函数,
  218.         在注册event_handler的同时,初始化了一个conn对象作为drive_machine的参数,
  219.         而这个对象中的rbytes为0。
  220.     3)当worker线程监听的client fd第一次有命令请求过来时(注意是第一次),例如set key 0 0 4\r\n,
  221.         worker线程的收到通知,然后陷入了event_handler再到drive_machine中去。。
  222.         而此时,传过来的conn* c,c->state 为conn_new_cmd,而c->rbytes确实为0!!
  223.         (再次强调是第一次有命令请求过来时)
  224.     就在这个时候就进入reset_cmd_handler,当前你看到的这个地方了。继续分析之后做了些啥:
  225.     4)第3)点得知,c->rbytes由此确实为0,所以在这种情况,必然会进入下面的else分支,
  226.         状态机进入conn_waiting状态。。。。
  227.     5)进入conn_waiting状态做了啥?就是把连接状态由conn_waiting变成conn_read,然后stop = true,退出
  228.         状态机。没错,退出状态机了!结束本次event_handler了!这就是比较纠结之处,这次命令请求触发的
  229.         event_handler居然只做了把状态由conn_new_cmd变成conn_read,然后再等待下次事件通知。
  230.         (详见conn_waiting分支代码)
  231.     6)那刚才那个命令请求怎么办?压根没有去读?例如set key 0 0 4\r\n这个命令的实质作用被忽略了吗?
  232.         其实没有。。原因是libevent默认的是水平触发,也就是说,这个命令还没被读,下次继续触发。。。
  233.         下次event_base会因为刚才那个命令再触发通知告诉worker线程,再次进入drive_machine,只是此时
  234.         c->state是conn_read状态,conn_read分支才真正把这个命令执行!
  235.     也就是说,当worker线程监听的client fd "第一次"有命令过来的时候,会触发两次event_base的通知!!
  236.     */
  237.     //这个rbytes大于0的情况,是当一次event中包含多个命令,
  238.     //或者说多个\r\n时候,程序执行到第二个及以后命令的时候出现。
  239.     if (c->rbytes > 0) {
  240.         conn_set_state(c, conn_parse_cmd);
  241.     } else {
  242.         conn_set_state(c, c
  243.             onn_waiting);
  244.     }
  245. }
  246. enum store_item_type do_store_item(item *it, int comm, conn *c, const uint32_t hv) {
  247.     char *key = ITEM_key(it);
  248.     item *old_it = do_item_get(key, it->nkey, hv); //拿出旧的item
  249.     enum store_item_type stored = NOT_STORED;
  250.     item *new_it = NULL;
  251.     int flags;
  252.     if (old_it != NULL && comm == NREAD_ADD) {
  253.         do_item_update(old_it); //更新item信息,其实主要是更新最近使用信息,即lru链表,。
  254.     } else if (!old_it && (comm == NREAD_REPLACE
  255.         || comm == NREAD_APPEND || comm == NREAD_PREPEND))
  256.     {
  257.     } else if (comm == NREAD_CAS) { //省略
  258.     } else {
  259.         if (comm == NREAD_APPEND || comm == NREAD_PREPEND) {
  260.             if (ITEM_get_cas(it) != 0) {
  261.                 // CAS much be equal
  262.                 if (ITEM_get_cas(it) != ITEM_get_cas(old_it)) {
  263.                     stored = EXISTS;
  264.                 }
  265.             }
  266.             if (stored == NOT_STORED) {
  267.                 flags = (int) strtol(ITEM_suffix(old_it), (char **) NULL, 10);
  268.                 new_it = do_item_alloc(key, it->nkey, flags, old_it->exptime, it->nbytes + old_it->nbytes - 2 /* CRLF */, hv);
  269.                 if (new_it == NULL) {
  270.                     if (old_it != NULL)
  271.                         do_item_remove(old_it);
  272.                     return NOT_STORED;
  273.                 }
  274.                 if (comm == NREAD_APPEND) {
  275.                     memcpy(ITEM_data(new_it), ITEM_data(old_it), old_it->nbytes);
  276.                     memcpy(ITEM_data(new_it) + old_it->nbytes - 2 /* CRLF */, ITEM_data(it), it->nbytes);
  277.                 } else {
  278.                     memcpy(ITEM_data(new_it), ITEM_data(it), it->nbytes);
  279.                     memcpy(ITEM_data(new_it) + it->nbytes - 2 /* CRLF */, ITEM_data(old_it), old_it->nbytes);
  280.                 }
  281.                 it = new_it;
  282.             }
  283.         }
  284.         //SET 命令会直接跑来这里,不仔细看还真不知道 -_-||
  285.         /**
  286.             会跑来这里的,NOT_STORED的都是还没执行"link"操作的情况。
  287.             像NREAD_APPEND这些命令都是有link过的。
  288.             至于什么是link,具体往下看。
  289.         */
  290.         if (stored == NOT_STORED) {
  291.             if (old_it != NULL)
  292.                 /**
  293.                 如果要SET 的key已经存在,则用新的item覆盖新的。
  294.                 详见thread::item_replace和items::do_item_replace
  295.                 */
  296.                 item_replace(old_it, it, hv);
  297.             else
  298.             /**
  299.                 如果要SET 的不存在,则把item链接起来,这链接主要是做了一些关于这个item杂七
  300.                 杂八的工作,其实比较重要的两点:
  301.                 1)把item放到hash table ,作用就是为了能够很快通过key拿到item啦。
  302.                 2)插入到lru链表
  303.                 详见items::do_item_link
  304.             */
  305.                 do_item_link(it, hv);
  306.             c->cas = ITEM_get_cas(it);
  307.             stored = STORED;
  308.         }
  309.     }
  310.     if (old_it != NULL)
  311.         do_item_remove(old_it); /* release our reference */
  312.     if (new_it != NULL)
  313.         do_item_remove(new_it);
  314.     if (stored == STORED) {
  315.         c->cas = ITEM_get_cas(it);
  316.     }
  317.     return stored;
  318. }
  319. static void process_update_command(conn *c, token_t *tokens, const size_t ntokens, int comm, bool handle_cas) {
  320.     char *key;
  321.     size_t nkey;
  322.     unsigned int flags;
  323.     int32_t exptime_int = 0;
  324.     time_t exptime;
  325.     int vlen;
  326.     uint64_t req_cas_id=0;
  327.     item *it;
  328.     assert(c != NULL);
  329.     set_noreply_maybe(c, tokens, ntokens);
  330.     if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH) {
  331.         out_string(c, "CLIENT_ERROR bad command line format"); //key过长,out_string函数的作用是输出响应,
  332.         //详见out_string定义处
  333.         return;
  334.     }
  335.     key = tokens[KEY_TOKEN].value; //键名
  336.     nkey = tokens[KEY_TOKEN].length; //键长度
  337.     if (! (safe_strtoul(tokens[2].value, (uint32_t *)&flags)
  338.            && safe_strtol(tokens[3].value, &exptime_int)
  339.            && safe_strtol(tokens[4].value, (int32_t *)&vlen))) {
  340.         out_string(c, "CLIENT_ERROR bad command line format");
  341.         return;
  342.     }
  343.     exptime = exptime_int;
  344.     if (exptime < 0)
  345.         exptime = REALTIME_MAXDELTA + 1;
  346.     if (handle_cas) {
  347.         if (!safe_strtoull(tokens[5].value, &req_cas_id)) {
  348.             out_string(c, "CLIENT_ERROR bad command line format");
  349.             return;
  350.         }
  351.     }
  352.     vlen += 2;
  353.     if (vlen < 0 || vlen - 2 < 0) {
  354.         out_string(c, "CLIENT_ERROR bad command line format");
  355.         return;
  356.     }
  357.     if (settings.detail_enabled) {
  358.         stats_prefix_record_set(key, nkey);
  359.     }
  360.     it = item_alloc(key, nkey, flags, realtime(exptime), vlen); //在这里执行内存分配工作。详见item_alloc
  361.     if (it == 0) {
  362.         //略
  363.         return;
  364.     }
  365.     ITEM_set_cas(it, req_cas_id);
  366.     c->item = it; //将item指针指向分配的item空间
  367.     c->ritem = ITEM_data(it); //将 ritem 指向 it->data中要存放 value 的位置
  368.     c->rlbytes = it->nbytes; //data的大小
  369.     c->cmd = comm; //命令类型
  370.     conn_set_state(c, conn_nread); //继续调用状态机,执行命令的另一半工作。
  371. }
  372. /**
  373. 这里就是对命令的解析和执行了,准确来说,这里只是执行了命令的一半,
  374. 然后根据命令类型再次改变conn_state使程序再次进入状态机,完成命令的
  375. 另一半工作
  376. command此时的指针值等于conn的rcurr
  377. */
  378. static void process_command(conn *c, char *command) {
  379.     token_t tokens[MAX_TOKENS];
  380.     size_t ntokens;
  381.     int comm; //命令类型
  382.     assert(c != NULL);
  383.     MEMCACHED_PROCESS_COMMAND_START(c->sfd, c->rcurr, c->rbytes);
  384.     if (settings.verbose > 1)
  385.         fprintf(stderr, "<%d %s\n", c->sfd, command);
  386.     c->msgcurr = 0;
  387.     c->msgused = 0;
  388.     c->iovused = 0;
  389.     if (add_msghdr(c) != 0) {
  390.         out_of_memory(c, "SERVER_ERROR out of memory preparing response");
  391.         return;
  392.     }
  393.     /**
  394.     下面这个tokenize_command是一个词法分析,把command分解成一个个token
  395.     */
  396.     ntokens = tokenize_command(command, tokens, MAX_TOKENS);
  397.     //下面是对上面分解出来的token再进行语法分析,解析命令,下面的comm变量为最终解析出来命令类型
  398.     if (ntokens >= 3 &&
  399.         ((strcmp(tokens[COMMAND_TOKEN].value, "get") == 0) ||
  400.          (strcmp(tokens[COMMAND_TOKEN].value, "bget") == 0))) {
  401.         process_get_command(c, tokens, ntokens, false);
  402.     } else if ((ntokens == 6 || ntokens == 7) &&xx//略
  403.         //add/set/replace/prepend/append为“更新”命令,调用同一个函数执行命令。详见process_update_command定义处
  404.         process_update_command(c, tokens, ntokens, comm, false);
  405.     } else if ((ntokens == 7 || ntokens == 8) && (strcmp(tokens[COMMAND_TOKEN].value, "cas") == 0 && (comm = NREAD_CAS))) {
  406.         process_update_command(c, tokens, ntokens, comm, true);
  407.     } else if ((ntokens == 4 || ntokens == 5) && (strcmp(tokens[COMMAND_TOKEN].value, "incr") == 0)) {
  408.         process_arithmetic_command(c, tokens, ntokens, 1);
  409.     } else if (ntokens >= 3 && (strcmp(tokens[COMMAND_TOKEN].value, "gets") == 0)) {
  410.         //执行get 命令
  411.         process_get_command(c, tokens, ntokens, true);
  412.     } else if ((ntokens == 4 || ntokens == 5) && (strcmp(tokens[COMMAND_TOKEN].value, "decr") == 0)) {
  413.         process_arithmetic_command(c, tokens, ntokens, 0);
  414.     } else if (ntokens >= 3 && ntokens <= 5 && (strcmp(tokens[COMMAND_TOKEN].value, "delete") == 0)) {
  415.         //执行delete 删除命令
  416.         process_delete_command(c, tokens, ntokens);
  417.     }elseif{//省略其它命令。。。
  418.     }
  419.     return;
  420. }
  421. /*
  422.  * if we have a complete line in the buffer, process it.
  423.  */
  424. static int try_read_command(conn *c) {
  425.     assert(c != NULL);
  426.     assert(c->rcurr <= (c->rbuf + c->rsize));
  427.     assert(c->rbytes > 0);
  428.    //省略掉UDP和二进制协议的逻辑
  429.     if (c->protocol == binary_prot) {
  430.     } else {
  431.         char *el, *cont;
  432.         if (c->rbytes == 0) //读buffer没有待解析的数据
  433.             return 0;
  434.         el = memchr(c->rcurr, ‘\n‘, c->rbytes); //找第一个命令的末尾,即换行符
  435.         if (!el) {
  436.             if (c->rbytes > 1024) {
  437.                 char *ptr = c->rcurr;
  438.                 while (*ptr == ‘ ‘) { /* ignore leading whitespaces */
  439.                     ++ptr;
  440.                 }
  441.                 if (ptr - c->rcurr > 100 ||
  442.                     (strncmp(ptr, "get ", 4) && strncmp(ptr, "gets ", 5))) {
  443.                     conn_set_state(c, conn_closing);
  444.                     return 1;
  445.                 }
  446.             }
  447.             /*
  448.             如果没有找到换行符,则说明读到的数据还不足以成为一个完整的命令,
  449.             返回0
  450.             */
  451.             return 0;
  452.         }
  453.         cont = el + 1; //下一个命令的开头
  454.         /*
  455.         下面这个if的作用是把el指向当前命令最后一个有效字符的下一个字符,即\r
  456.         目的是为了在命令后面插上一个\0,字符串结束符。
  457.         例如 GET abc\r\n******,变成GET abc\0\n*****,这样以后读出的字符串就是一个命令。
  458.         */
  459.         if ((el - c->rcurr) > 1 && *(el - 1) == ‘\r‘) {
  460.             el--;
  461.         }
  462.         *el = ‘\0‘;
  463.         assert(cont <= (c->rcurr + c->rbytes));
  464.         c->last_cmd_time = current_time;
  465.         process_command(c, c->rcurr); //执行命令。分析详见process_command
  466.         //当前命令执行完之后,把当前指针rcurr指向 下一个命令的开头,并调用rbytes(剩余未处理字节数大小)
  467.         //逻辑上相当于把已处理的命令去掉。
  468.         c->rbytes -= (cont - c->rcurr);
  469.         c->rcurr = cont;
  470.         assert(c->rcurr <= (c->rbuf + c->rsize));
  471.     }
  472.     return 1;
  473. }
  474. static enum try_read_result try_read_network(conn *c) {
  475.     enum try_read_result gotdata = READ_NO_DATA_RECEIVED;
  476.     int res;
  477.     int num_allocs = 0;
  478.     assert(c != NULL);
  479.     if (c->rcurr != c->rbuf) {
  480.         if (c->rbytes != 0) /* otherwise there‘s nothing to copy */
  481.             memmove(c->rbuf, c->rcurr, c->rbytes);
  482.         c->rcurr = c->rbuf;
  483.     }
  484.     while (1) {
  485.         if (c->rbytes >= c->rsize) {//读buffer空间扩充
  486.             if (num_allocs == 4) {
  487.                 return gotdata;
  488.             }
  489.             ++num_allocs;
  490.             char *new_rbuf = realloc(c->rbuf, c->rsize * 2);
  491.             if (!new_rbuf) {
  492.                //失败的情况,省略
  493.             }
  494.             c->rcurr = c->rbuf = new_rbuf;
  495.             c->rsize *= 2;
  496.         }
  497.         int avail = c->rsize - c->rbytes; //读buffer的空间还剩余多少大小可以用
  498.         res = read(c->sfd, c->rbuf + c->rbytes, avail); //往剩下的可用的地方里塞
  499.         if (res > 0) {
  500.             pthread_mutex_lock(&c->thread->stats.mutex);
  501.             c->thread->stats.bytes_read += res;
  502.             pthread_mutex_unlock(&c->thread->stats.mutex);
  503.             gotdata = READ_DATA_RECEIVED;
  504.             /**
  505.             rbytes是当前指针rcurr至读buffer末尾的数据大小,这里可简单地理解为对rbytes的初始化。
  506.             */
  507.             c->rbytes += res;
  508.             if (res == avail) { //可能还没读完,此时读buffer可用空间满了,那么下次循环会进行读buffer空间扩充
  509.                 continue;
  510.             } else {
  511.                 break; //socket的可读数据都读完了
  512.             }
  513.         }
  514.         if (res == 0) {
  515.             return READ_ERROR;
  516.         }
  517.         if (res == -1) {
  518.             if (errno == EAGAIN || errno == EWOULDBLOCK) {
  519.                 break;
  520.             }
  521.             return READ_ERROR;
  522.         }
  523.     }
  524.     return gotdata;
  525. }
  526. /**
  527. 通过状态机的方式处理网络请求/命令,后面的代码可以看到,
  528. 实质这里是根据
  529. 1)不同的conn连接对象,包括来自主线程的监听listen fd的连接或来自worker线程监听的client fd的连接
  530. 2)或者说是conn连接不同的状态,例如主线程监听listen fd连接通常是conn_listenning状态,
  531.    而worker线程监听的client fd的连接 可能是conn_new_cmd,conn_read等状态
  532. 来进行不同的业务处理
  533. */
  534. static void drive_machine(conn *c) {
  535.     bool stop = false;
  536.     int sfd;
  537.     socklen_t addrlen;
  538.     struct sockaddr_storage addr;
  539.     int nreqs = settings.reqs_per_event; //每个连接可处理的最大请求数
  540.     int res;
  541.     const char *str;
  542. #ifdef HAVE_ACCEPT4
  543.     static int use_accept4 = 1;
  544. #else
  545.     static int use_accept4 = 0;
  546. #endif
  547.     assert(c != NULL);
  548.     /**
  549.      这里是事件被触发后处理业务逻辑的核心
  550.      这个while循环里面有一个巨大的switch case,根据连接对象 conn当前
  551.      的连接状态conn_state,进入不同的case,而每个case可能会改变conn的连接状态,
  552.      也就是说在这个while+switch中,conn会不断的发生状态转移,最后被分发到合适的case上作处理。
  553.      可以理解为,这里是一个有向图,每个case是一个顶点,有些case通过改变conn对象的连接状态让程序在
  554.      下一次循环中进入另一个case,几次循环后程序最终进入到“无出度的顶点”然后结束状态机,
  555.      这里的无出度的顶点就是带设置stop=true的case分支
  556.     */
  557.     while (!stop) {
  558.         switch(c->state) {
  559.         case conn_listening: //此case只有当listen fd有事件到达后触发主线程执行
  560.             sfd = accept(c->sfd, (struct sockaddr *)&addr, &addrlen); //accept,得到client fd
  561.             if (sfd == -1) {
  562.             } else {
  563.                 /**
  564.                 accept成功后,调用dispatch_conn_new,把client fd交给 worker线程处理
  565.                 必须注意dispatch_conn_new 函数第二个参数:init_state,也就是
  566.                 创建连接对象的初始化状态,通过主线程分发给worker线程的client fd,最终
  567.                 建立的连接对象初始化状态为conn_new_cmd (当然这里只说的是TCP socket的情况,UDP socket暂不作分析)
  568.                 */
  569.                 dispatch_conn_new(sfd, conn_new_cmd, EV_READ | EV_PERSIST,
  570.                                      DATA_BUFFER_SIZE, tcp_transport);
  571.             }
  572.             stop = true;
  573.             break;
  574.         case conn_waiting: //等待数据
  575.             if (!update_event(c, EV_READ | EV_PERSIST)) {
  576.                 if (settings.verbose > 0)
  577.                     fprintf(stderr, "Couldn‘t update event\n");
  578.                 conn_set_state(c, conn_closing);
  579.                 break;
  580.             }
  581.             conn_set_state(c, conn_read);  
  582.             stop = true;
  583.             break;
  584.         case conn_read: //执行读数据
  585.             res = IS_UDP(c->transport) ? try_read_udp(c) : try_read_network(c); //从网络上读取数据
  586.             switch (res) {
  587.             case READ_NO_DATA_RECEIVED:
  588.                 conn_set_state(c, conn_waiting); //没有数据再继续等待
  589.                 break;
  590.             case READ_DATA_RECEIVED: //成功接收数据则进入conn_parse_cmd分支,解析命令
  591.                 conn_set_state(c, conn_parse_cmd);
  592.                 break;
  593.             case READ_ERROR:
  594.                 conn_set_state(c, conn_closing);
  595.                 break;
  596.             case READ_MEMORY_ERROR: /* Failed to allocate more memory */
  597.                 /* State already set by try_read_network */
  598.                 break;
  599.             }
  600.             break;
  601.         case conn_parse_cmd :
  602.             /**
  603.             try_read_network后,到达conn_parse_cmd状态,但try_read_network并不确保每次到达
  604.             的数据都足够一个完整的cmd(ascii协议情况下往往是没有"\r\n",即回车换行),
  605.             所以下面的try_read_command之所以叫try就是这个原因,
  606.             当读到的数据还不够成为一个cmd的时候,返回0,conn继续进入conn_waiting状态等待更多的数据到达。
  607.             */
  608.             if (try_read_command(c) == 0) {
  609.                 /* wee need more data! */
  610.                 conn_set_state(c, conn_waiting);
  611.             }
  612.             break;
  613.         case conn_new_cmd:
  614.             /* Only process nreqs at a time to avoid starving other
  615.                connections */
  616.             /*
  617.              这里的reqs是请求的意思,其实叫“命令”更准确。一次event发生,有可能包含多个命令,
  618.              从client fd里面read到的一次数据,不能保证这个数据只是包含一个命令,有可能是多个
  619.              命令数据堆在一起的一次事件通知。这个nreqs是用来控制一次event最多能处理多少个命令。
  620.             */
  621.             --nreqs;
  622.             if (nreqs >= 0) {
  623.                 /**
  624.                 准备执行命令。为什么叫reset cmd,reset_cmd_handler其实做了一些解析执行命令之前
  625.                 的初始化动下一个,都会重新进入这个case作。而像上面说的,一次event有可能有多个命令,每执行一个命令,如果还有
  626.                  conn_new_cmd,reset一下再执行下一个命令。
  627.                 */
  628.                 reset_cmd_handler(c);
  629.             } else {
  630.                 //省略
  631.                 stop = true;
  632.             }
  633.             break;
  634.         case conn_nread:
  635.             /**
  636.             由process_update_command执行后进入此状态,process_update_command函数只执行了add/set/replace 等命令的一半,
  637.             剩下的一半由这里完成。
  638.             在这插一下memcached的命令解析协议,add/set/replace等这种写类型的命令,分为两块,或者说要有两次的“回车”,
  639.             如:
  640.             1)set key 0 0 4\r\n
  641.             2)haha\r\n
  642.             第1)行是命令,第2)行是数据块。
  643.             例如如果是上面的set命令,process_update_command只完成了第1)行,分配了item空间,但还没有把value塞到对应的
  644.             item中去。因此,在这一半要完成的动作就是把第2)行代表value的数据读出来,塞到刚拿到的item空间中去
  645.             */
  646.             /*
  647.             下面的rlbytes字段表示要读的value数据还剩下多少字节
  648.             如果是第一次由process_update_command进入到此,rlbytes此时在process_update_command中被初始化为item->nbytes,
  649.             即value的总字节数,由第1)行命令中声明,即上面例子第1)行的"4"
  650.             */
  651.             if (c->rlbytes == 0) {
  652.                 /**
  653.                 如果要读value数据的都读完了,就调用complete_nread完成收尾工作,程序会跟着complete_nread进入下一个
  654.                 状态。所以执行完complete_nread会break;
  655.                 */
  656.                 complete_nread(c);
  657.                 break;
  658.             }
  659.             //如果还有数据没读完,继续往下执行。可知,下面的动作就是继续从buffer中读value数据往item中的data的value位置塞。
  660.             /* Check if rbytes < 0, to prevent crash */
  661.             if (c->rlbytes < 0) {
  662.                 if (settings.verbose) {
  663.                     fprintf(stderr, "Invalid rlbytes to read: len %d\n", c->rlbytes);
  664.                 }
  665.                 conn_set_state(c, conn_closing);
  666.                 break;
  667.             }
  668.             /* first check if we have leftovers in the conn_read buffer */
  669.             if (c->rbytes > 0) {
  670.                 /**
  671.                 取rbytes与rlbytes中最小的值。
  672.                 为啥?
  673.                 因为这里我们的目的是剩下的还没读的value的字节,而rlbytes代表的是还剩下的字节数
  674.                 如果rlbytes比rbytes小,只读rlbytes长度就够了,rbytes中多出来的部分不是我们这个时候想要的
  675.                 如果rbytes比rlbytes小,即使你要rlbytes这么多,但buffer中没有这么多给你读。
  676.                 */
  677.                 int tocopy = c->rbytes > c->rlbytes ? c->rlbytes : c->rbytes;
  678.                 if (c->ritem != c->rcurr) {
  679.                     memmove(c->ritem, c->rcurr, tocopy); //往分配的item中塞,即为key设置value的过程
  680.                 }
  681.                 c->ritem += tocopy;
  682.                 c->rlbytes -= tocopy;
  683.                 c->rcurr += tocopy;
  684.                 c->rbytes -= tocopy;
  685.                 if (c->rlbytes == 0) {
  686.                     break;
  687.                 }
  688.             }
  689.             //这里往往是我们先前读到buffer的数据还没足够的情况下,从socket中读。
  690.             /* now try reading from the socket */
  691.             res = read(c->sfd, c->ritem, c->rlbytes);//往分配的item中塞,即为key设置value的过程
  692.             if (res > 0) {
  693.                 pthread_mutex_lock(&c->thread->stats.mutex);
  694.                 c->thread->stats.bytes_read += res;
  695.                 pthread_mutex_unlock(&c->thread->stats.mutex);
  696.                 if (c->rcurr == c->ritem) {
  697.                     c->rcurr += res;
  698.                 }
  699.                 c->ritem += res;
  700.                 c->rlbytes -= res;
  701.                 break;
  702.             }
  703.             if (res == 0) { /* end of stream */
  704.                 conn_set_state(c, conn_closing);
  705.                 break;
  706.             }
  707.             if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
  708.                 if (!update_event(c, EV_READ | EV_PERSIST)) {
  709.                     if (settings.verbose > 0)
  710.                         fprintf(stderr, "Couldn‘t update event\n");
  711.                     conn_set_state(c, conn_closing);
  712.                     break;
  713.                 }
  714.                 stop = true;
  715.                 break;
  716.             }
  717.             /* otherwise we have a real error, on which we close the connection */
  718.             if (settings.verbose > 0) {
  719.             }
  720.             conn_set_state(c, conn_closing);
  721.             break;
  722.         case conn_swallow:
  723.             //省略
  724.             conn_set_state(c, conn_closing);
  725.             break;
  726.         case conn_write:
  727.             if (c->iovused == 0 || (IS_UDP(c->transport) && c->iovused == 1)) {
  728.                 if (add_iov(c, c->wcurr, c->wbytes) != 0) {
  729.                 }
  730.             }
  731.             /* fall through... */ //直接进入conn_mwrite分支
  732.         case conn_mwrite:
  733.           if (IS_UDP(c->transport) && c->msgcurr == 0 && build_udp_headers(c) != 0) {
  734.             if (settings.verbose > 0)
  735.               fprintf(stderr, "Failed to build UDP headers\n");
  736.             conn_set_state(c, conn_closing);
  737.             break;
  738.           }
  739.             switch (transmit(c)) { //执行transmit发送数据到客户端
  740.             case TRANSMIT_COMPLETE:
  741.                 if (c->state == conn_mwrite) {
  742.                     conn_release_items(c);
  743.                     if(c->protocol == binary_prot) {
  744.                         conn_set_state(c, c->write_and_go);
  745.                     } else {
  746.                         conn_set_state(c, conn_new_cmd); //重新回到conn_new_cmd分支等待新命令
  747.                     }
  748.                 } else if (c->state == conn_write) {
  749.                     if (c->write_and_free) {
  750.                         free(c->write_and_free);
  751.                         c->write_and_free = 0;
  752.                     }
  753.                     conn_set_state(c, c->write_and_go);
  754.                 } else {
  755.                     if (settings.verbose > 0)
  756.                         fprintf(stderr, "Unexpected state %d\n", c->state);
  757.                     conn_set_state(c, conn_closing);
  758.                 }
  759.                 break;
  760.             case TRANSMIT_INCOMPLETE:
  761.             case TRANSMIT_HARD_ERROR:
  762.                 break; /* Continue in state machine. */
  763.             case TRANSMIT_SOFT_ERROR:
  764.                 stop = true;
  765.                 break;
  766.             }
  767.             break;
  768.         case conn_closing:
  769.             if (IS_UDP(c->transport))
  770.                 conn_cleanup(c);
  771.             else
  772.                 conn_close(c);
  773.             stop = true;
  774.             break;
  775.         case conn_closed:
  776.             /* This only happens if dormando is an idiot. */
  777.             abort();
  778.             break;
  779.         case conn_max_state:
  780.             assert(false);
  781.             break;
  782.         }
  783.     }
  784.     return;
  785. }
  786. //事件处理器主要调用drive_machine状态机执行事件处理
  787. void event_handler(const int fd, const short which, void *arg) {
  788.     conn *c;
  789.     c = (conn *)arg;
  790.     assert(c != NULL);
  791.     c->which = which;
  792.     if (fd != c->sfd) {
  793.         if (settings.verbose > 0)
  794.             fprintf(stderr, "Catastrophic: event fd doesn‘t match conn fd!\n");
  795.         conn_close(c);
  796.         return;
  797.     }
  798.     drive_machine(c); //调用drive_machine处理事件发生后的业务逻辑。
  799.     return;
  800. }
  801. //创建socket
  802. static int new_socket(struct addrinfo *ai) {
  803.     int sfd;
  804.     int flags;
  805.     if ((sfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) {
  806.         return -1;
  807.     }
  808.     if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
  809.         fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {
  810.         perror("setting O_NONBLOCK");
  811.         close(sfd);
  812.         return -1;
  813.     }
  814.     return sfd;
  815. }
  816. static int server_sockets(int port, enum network_transport transport,
  817.                           FILE *portnumber_file) {
  818.     if (settings.inter == NULL) { //如果没有指定ip
  819.         return server_socket(settings.inter, port, transport, portnumber_file);
  820.     } else {
  821.         //省略指定的情况
  822.         return ret;
  823.     }
  824. }
  825. //main函数
  826. int main (int argc, char **argv) {
  827.     //省略一些变量定义,下面使用到时会说明
  828.     settings_init(); //初始化配置
  829.     setbuf(stderr, NULL);
  830.     //memcached启动时指定的启动参数配置,在此省略
  831.     while (-1 != (c = getopt(argc, argv, xxx
  832.     }
  833.     if (hash_init(hash_type) != 0) { //哈希算法初始化
  834.         fprintf(stderr, "Failed to initialize hash_algorithm!\n");
  835.         exit(EX_USAGE);
  836.     }
  837.    //身份验证的代码
  838.     if (getuid() == 0 || geteuid() == 0) {
  839.     }
  840.    //以常驻进程方式运行
  841.     if (do_daemonize) {
  842.         if (daemonize(maxcore, settings.verbose) == -1) {
  843.         }
  844.     }
  845.  
  846.     main_base = event_init(); //全局的main_base变量
  847.     stats_init(); //初始化全局统计
  848.     assoc_init(settings.hashpower_init); //初始化哈希表
  849.     conn_init(); //初始化连接对象,配置
  850.     slabs_init(settings.maxbytes, settings.factor, preallocate); //初始化slabs,这里会对一些内存管理进行初始化
  851.     //初始化主线程,参数是worker线程个数,和当前主线程的event_base
  852.     thread_init(settings.num_threads, main_base);
  853.     if (start_assoc_maintenance_thread() == -1) {//启动哈希表维护线程,详见assoc::start_assoc_maintenance_thread
  854.         exit(EXIT_FAILURE);
  855.     }
  856.     /**
  857.     如果开启了slab可重分配,则启动slab维护线程
  858.     注意slab维护线程(下面简称s)与哈希表维护线程(下面简称哈)逻辑上不一样的地方:
  859.     s是只要用户开启了slab_reassign,那么启动memcached服务的时候这个线程就同时启动的
  860.     而哈虽然一开始就启动了,但是处理睡眠态,在需要的时候,memcached才发送信息唤醒。
  861.     详见assoc::start_assoc_maintenance_thread和slabs::start_slab_maintenance_thread
  862.     */
  863.     if (settings.slab_reassign &&
  864.         start_slab_maintenance_thread() == -1) {
  865.         exit(EXIT_FAILURE);
  866.     }
  867.     init_lru_crawler(); //初始化item爬虫
  868.      //这里省略unix socket监听方式和UDP的代码
  869.     /*
  870.         睡眠一下,只是提供一定的时候让socket打开而已
  871.      */
  872.     usleep(1000);
  873.     if (stats.curr_conns + stats.reserved_fds >= settings.maxconns - 1) {
  874.         fprintf(stderr, "Maxconns setting is too low, use -c to increase.\n");
  875.         exit(EXIT_FAILURE);
  876.     }
  877.     if (pid_file != NULL) {
  878.         save_pid(pid_file); //创建pid文件
  879.     }
  880.     drop_privileges();
  881.    //进入事件循环
  882.     if (event_base_loop(main_base, 0) != 0) {
  883.         retval = EXIT_FAILURE;
  884.     }
  885.     stop_assoc_maintenance_thread(); //进程退出之前停止哈希表维护线程
  886.     //进程退出的收尾工作
  887.     return retval;
  888. }

以上是关于Memcached源码分析之memcached.c的主要内容,如果未能解决你的问题,请参考以下文章

Memcached源码分析之memcached.h

Memcached源码分析之线程模型

Memcached源码分析之从SET命令开始说起

Memcached源码分析之slabs.c

Memcached源码分析之assoc.c

Memcached源码分析之thread.c