文章列表:

《Memcached源代码分析 - Memcached源代码分析之基于Libevent的网络模型(1)》

《Memcached源代码分析 - Memcached源代码分析之命令解析(2)》

《Memcached源代码分析 - Memcached源代码分析之消息回应(3)  》

《Memcached源代码分析 - Memcached源代码分析之HashTable(4) 》

《Memcached源代码分析 - Memcached源代码分析之增删改查操作(5) 》

《Memcached源代码分析 - Memcached源代码分析之LRU算法(6)》

《Memcached源代码分析 - Memcached源代码分析之存储机制Slabs(7)》

《Memcached源代码分析 - Memcached源代码分析之总结篇(8)》

前言

上一章《Memcached源代码分析 - Memcached源代码分析之命令解析(2)》。我们花了非常大的力气去解说Memcached怎样从client读取命令,而且解析命令,然后处理命令而且向client回应消息。

这一章,我们主要来解说Memcached回应消息的技术细节

本章前,我们先须要了解几个知识点(msghdr和iovc)。

msghdr结构:

  1. struct msghdr {
  2. void *msg_name;
  3. socklen_t msg_namelen;
  4. struct iovec *msg_iov;
  5. size_t msg_iovlen;
  6. void *msg_control;
  7. size_t msg_controllen;
  8. int msg_flags;
  9. };

iovc结构:

  1. #include <sys/uio.h>
  2. /* Structure for scatter/gather I/O. */
  3. struct iovec {
  4. void *iov_base; /* Pointer to data. */
  5. size_t iov_len; /* Length of data. */
  6. };

Memcached是通过sendmsg函数向client发送数据的,就会用到上面的结构,不了解这个结构的。建议先了解之后再继续往下看。

Memcached消息回应源代码分析

数据结构

我们继续看一下conn这个结构。

conn结构我们上一期说过,主要是存储单个client的连接详情信息。

每个client连接到Memcached都会有这么一个数据结构。

  1. typedef struct conn conn;
  2. struct conn {
  3. //....
  4.  /* data for the mwrite state */
  5. //iov主要存储iov的数据结构
  6. //iov数据结构会在conn_new中初始化,初始化的时候,系统会分配400个iovec的结构,最高水位600个
  7.  struct iovec *iov;
  8. //iov的长度
  9.  int iovsize; /* number of elements allocated in iov[] */
  10. //iovused 这个主要记录iov使用了多少
  11.  int iovused; /* number of elements used in iov[] */
  12.  
  13. //msglist主要存储msghdr的列表数据结构
  14. //msglist数据结构在conn_new中初始化的时候。系统会分配10个结构
  15.   struct msghdr *msglist;
  16. //msglist的长度。初始化为10个,最高水位100。不够用的时候会realloc。每次扩容都会扩容一倍
  17.  int msgsize; /* number of elements allocated in msglist[] */
  18. //msglist已经使用的长度
  19.  int msgused; /* number of elements used in msglist[] */
  20. //这个參数主要帮助记录那些msglist已经发送过了,哪些没有发送过。
  21.  int msgcurr; /* element in msglist[] being transmitted now */
  22. int msgbytes; /* number of bytes in current msg */
  23. }

我们能够看一下conn_new这种方法。这种方法应该在第一章节的时候讲到过。

这边主要看一下iov和msglist两个參数初始化的过程。

  1. conn *conn_new(const int sfd, enum conn_states init_state,
  2. const int event_flags, const int read_buffer_size,
  3. enum network_transport transport, struct event_base *base) {
  4. //...
  5. c->rbuf = c->wbuf = 0;
  6. c->ilist = 0;
  7. c->suffixlist = 0;
  8. c->iov = 0;
  9. c->msglist = 0;
  10. c->hdrbuf = 0;
  11.  
  12. c->rsize = read_buffer_size;
  13. c->wsize = DATA_BUFFER_SIZE;
  14. c->isize = ITEM_LIST_INITIAL;
  15. c->suffixsize = SUFFIX_LIST_INITIAL;
  16. c->iovsize = IOV_LIST_INITIAL; //初始化400
  17. c->msgsize = MSG_LIST_INITIAL; //初始化10
  18. c->hdrsize = 0;
  19.  
  20. c->rbuf = (char *) malloc((size_t) c->rsize);
  21. c->wbuf = (char *) malloc((size_t) c->wsize);
  22. c->ilist = (item **) malloc(sizeof(item *) * c->isize);
  23. c->suffixlist = (char **) malloc(sizeof(char *) * c->suffixsize);
  24. c->iov = (struct iovec *) malloc(sizeof(struct iovec) * c->iovsize); //初始化iov
  25. c->msglist = (struct msghdr *) malloc(
  26. sizeof(struct msghdr) * c->msgsize); //初始化msglist
  27. //...
  28. }

数据结构关系图(iov和msglist之间的关系):

从process_get_command開始

我们继续从process_get_command,获取memcached的缓存数据这种方法開始。

在这种方法中。我们主要看add_iov这种方法。Memcached主要是通过add_iov方法,将须要发送给client的数据装到iov和msglist结构中去的。

  1. /* ntokens is overwritten here... shrug.. */
  2. //处理GET请求的命令
  3. static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens,
  4. bool return_cas) {
  5. //处理GET命令
  6. char *key;
  7. size_t nkey;
  8. int i = 0;
  9. item *it;
  10. //&tokens[0] 是操作的方法
  11. //&tokens[1] 为key
  12. //token_t 存储了value和length
  13. token_t *key_token = &tokens[KEY_TOKEN];
  14. char *suffix;
  15. assert(c != NULL);
  16.  
  17. do {
  18. //假设key的长度不为0
  19. while (key_token->length != 0) {
  20.  
  21. key = key_token->value;
  22. nkey = key_token->length;
  23.  
  24. //推断key的长度是否超过了最大的长度。memcache key的最大长度为250
  25. //这个地方须要很注意,我们在寻常的使用中。还是要注意key的字节长度的
  26. if (nkey > KEY_MAX_LENGTH) {
  27. //out_string 向外部输出数据
  28. out_string(c, "CLIENT_ERROR bad command line format");
  29. while (i-- > 0) {
  30. item_remove(*(c->ilist + i));
  31. }
  32. return;
  33. }
  34. //这边是从Memcached的内存存储快中去取数据
  35. it = item_get(key, nkey);
  36. if (settings.detail_enabled) {
  37. //状态记录,key的记录数的方法
  38. stats_prefix_record_get(key, nkey, NULL != it);
  39. }
  40. //假设获取到了数据
  41. if (it) {
  42. //c->ilist 存放用于向外部写数据的buf
  43. //假设ilist太小,则又一次分配一块内存
  44. if (i >= c->isize) {
  45. item **new_list = realloc(c->ilist,
  46. sizeof(item *) * c->isize * 2);
  47. if (new_list) {
  48. //存放须要向client写数据的item的列表的长度
  49. c->isize *= 2;
  50. //存放须要向client写数据的item的列表,这边支持
  51. c->ilist = new_list;
  52. } else {
  53. STATS_LOCK();
  54. stats.malloc_fails++;
  55. STATS_UNLOCK();
  56. item_remove(it);
  57. break;
  58. }
  59. }
  60.  
  61. /*
  62. * Construct the response. Each hit adds three elements to the
  63. * outgoing data list:
  64. * "VALUE "
  65. * key
  66. * " " + flags + " " + data length + "\r\n" + data (with \r\n)
  67. */
  68. //初始化返回出去的数据结构
  69. if (return_cas) {
  70. //......
  71. } else {
  72. MEMCACHED_COMMAND_GET(c->sfd, ITEM_key(it), it->nkey,
  73. it->nbytes, ITEM_get_cas(it));
  74. //将须要返回的数据填充到IOV结构中
  75. //命令:get userId
  76. //返回的结构:
  77. //VALUE userId 0 5
  78. //55555
  79. //END
  80. if (<strong><span style="color:#FF0000;">add_iov</span></strong>(c, "VALUE ", 6) != 0
  81. || <strong><span style="color:#FF0000;">add_iov</span></strong>(c, ITEM_key(it), it->nkey) != 0
  82. || <strong><span style="color:#FF0000;">add_iov</span></strong>(c, ITEM_suffix(it),
  83. it->nsuffix + it->nbytes) != 0) {
  84. item_remove(it);
  85. break;
  86. }
  87. }
  88.  
  89. if (settings.verbose > 1) {
  90. int ii;
  91. fprintf(stderr, ">%d sending key ", c->sfd);
  92. for (ii = 0; ii < it->nkey; ++ii) {
  93. fprintf(stderr, "%c", key[ii]);
  94. }
  95. fprintf(stderr, "\n");
  96. }
  97.  
  98. /* item_get() has incremented it->refcount for us */
  99. pthread_mutex_lock(&c->thread->stats.mutex);
  100. c->thread->stats.slab_stats[it->slabs_clsid].get_hits++;
  101. c->thread->stats.get_cmds++;
  102. pthread_mutex_unlock(&c->thread->stats.mutex);
  103. item_update(it);
  104. *(c->ilist + i) = it;
  105. i++;
  106.  
  107. } else {
  108. pthread_mutex_lock(&c->thread->stats.mutex);
  109. c->thread->stats.get_misses++;
  110. c->thread->stats.get_cmds++;
  111. pthread_mutex_unlock(&c->thread->stats.mutex);
  112. MEMCACHED_COMMAND_GET(c->sfd, key, nkey, -1, 0);
  113. }
  114.  
  115. key_token++;
  116. }
  117.  
  118. /*
  119. * If the command string hasn't been fully processed, get the next set
  120. * of tokens.
  121. */
  122. //假设命令行中的命令没有所有被处理,则继续下一个命令
  123. //一个命令行中,能够get多个元素
  124. if (key_token->value != NULL) {
  125. ntokens = tokenize_command(key_token->value, tokens, MAX_TOKENS);
  126. key_token = tokens;
  127. }
  128.  
  129. } while (key_token->value != NULL);
  130.  
  131. c->icurr = c->ilist;
  132. c->ileft = i;
  133. if (return_cas) {
  134. c->suffixcurr = c->suffixlist;
  135. c->suffixleft = i;
  136. }
  137.  
  138. if (settings.verbose > 1)
  139. fprintf(stderr, ">%d END\n", c->sfd);
  140.  
  141. /*
  142. If the loop was terminated because of out-of-memory, it is not
  143. reliable to add END\r\n to the buffer, because it might not end
  144. in \r\n. So we send SERVER_ERROR instead.
  145. */
  146. //加入结束标志符号
  147. if (key_token->value != NULL || <strong><span style="color:#FF0000;">add_iov</span></strong>(c, "END\r\n", 5) != 0
  148. || (IS_UDP(c->transport) && build_udp_headers(c) != 0)) {
  149. out_of_memory(c, "SERVER_ERROR out of memory writing get response");
  150. } else {
  151. //将状态改动为写,这边读取到item的数据后,又開始须要往client写数据了。
  152.  
  153. conn_set_state(c, conn_mwrite);
  154. c->msgcurr = 0;
  155. }
  156. }

add_iov 方法

add_iov方法,主要作用:

1. 将Memcached须要发送的数据。分成N多个IOV的块

2. 将IOV块加入到msghdr的结构中去。

  1. static int add_iov(conn *c, const void *buf, int len) {
  2. struct msghdr *m;
  3. int leftover;
  4. bool limit_to_mtu;
  5.  
  6. assert(c != NULL);
  7.  
  8. do {
  9. //消息数组 msglist 存储msghdr结构
  10. //这边是获取最新的msghdr数据结构指针
  11. m = &c->msglist[c->msgused - 1];
  12.  
  13. /*
  14. * Limit UDP packets, and the first payloads of TCP replies, to
  15. * UDP_MAX_PAYLOAD_SIZE bytes.
  16. */
  17. limit_to_mtu = IS_UDP(c->transport) || (1 == c->msgused);
  18.  
  19. /* We may need to start a new msghdr if this one is full. */
  20. //假设msghdr结构中的iov满了。则须要使用更新的msghdr数据结构
  21. if (m->msg_iovlen == IOV_MAX
  22. || (limit_to_mtu && c->msgbytes >= UDP_MAX_PAYLOAD_SIZE)) {
  23. //加入msghdr,这种方法中回去推断初始化的时候10个msghdr结构是否够用。不够用的话会扩容
  24. add_msghdr(c);
  25. //指向下一个新的msghdr数据结构
  26. m = &c->msglist[c->msgused - 1];
  27. }
  28.  
  29. //确认IOV的空间大小,初始化默认是400个,水位600
  30. //假设IOV也不够用了。就会去扩容
  31. if (ensure_iov_space(c) != 0)
  32. return -1;
  33.  
  34. /* If the fragment is too big to fit in the datagram, split it up */
  35. if (limit_to_mtu && len + c->msgbytes > UDP_MAX_PAYLOAD_SIZE) {
  36. leftover = len + c->msgbytes - UDP_MAX_PAYLOAD_SIZE;
  37. len -= leftover;
  38. } else {
  39. leftover = 0;
  40. }
  41.  
  42. m = &c->msglist[c->msgused - 1];
  43. //m->msg_iov參数指向c->iov这个结构。
  44. //详细m->msg_iov怎样指向到c->iov这个结构的,须要看一下add_msghdr这种方法
  45. //向IOV中填充BUF
  46. m->msg_iov[m->msg_iovlen].iov_base = (void *) buf;
  47. //buf的长度
  48. m->msg_iov[m->msg_iovlen].iov_len = len; //填充长度
  49.  
  50. c->msgbytes += len;
  51. c->iovused++;
  52. m->msg_iovlen++; //msg_iovlen + 1
  53.  
  54. buf = ((char *) buf) + len;
  55. len = leftover;
  56. } while (leftover > 0);
  57.  
  58. return 0;
  59. }

add_msghdr 方法 msghdr扩容

在add_iov方法中,我们能够看到。当IOV块加入满了之后,会调用这种方法扩容msgdhr的个数。

这种方法主要两个作用:

1. 检查c->msglist列表长度是否够用。

2. 使用最新的c->msglist中的一个msghdr元素,而且将msghdr->msg_iov指向c->iov最新未使用的那个iov的指针地址。

  1. static int add_msghdr(conn *c) {
  2. //c->msglist 这个列表用来存储msghdr结构
  3. struct msghdr *msg;
  4.  
  5. assert(c != NULL);
  6.  
  7. //假设msglist的长度和已经使用的长度相等的时候,说明msglist已经用完了,须要扩容
  8. if (c->msgsize == c->msgused) {
  9. //扩容两倍
  10. msg = realloc(c->msglist, c->msgsize * 2 * sizeof(struct msghdr));
  11. if (!msg) {
  12. STATS_LOCK();
  13. stats.malloc_fails++;
  14. STATS_UNLOCK();
  15. return -1;
  16. }
  17. c->msglist = msg; //将c->msglist指向当前新的列表
  18. c->msgsize *= 2; //size也会跟着添加
  19. }
  20.  
  21. //msg又一次指向未使用的msghdr指针位置
  22. msg = c->msglist + c->msgused;
  23.  
  24. /* this wipes msg_iovlen, msg_control, msg_controllen, and
  25. msg_flags, the last 3 of which aren't defined on solaris: */
  26. //将新的msghdr块初始化设置为0
  27. memset(msg, 0, sizeof(struct msghdr));
  28.  
  29. //新的msghdr的msg_iov指向 struct iovec *iov结构
  30. msg->msg_iov = &c->iov[c->iovused];
  31.  
  32. if (IS_UDP(c->transport) && c->request_addr_size > 0) {
  33. msg->msg_name = &c->request_addr;
  34. msg->msg_namelen = c->request_addr_size;
  35. }
  36.  
  37. c->msgbytes = 0;
  38. c->msgused++;
  39.  
  40. if (IS_UDP(c->transport)) {
  41. /* Leave room for the UDP header, which we'll fill in later. */
  42. return add_iov(c, NULL, UDP_HEADER_SIZE);
  43. }
  44.  
  45. return 0;
  46. }

ensure_iov_space 方法 IOV扩容

这种方法主要检查c->iov是否还有剩余空间。假设不够用了。则扩容2倍。

  1. static int ensure_iov_space(conn *c) {
  2. assert(c != NULL);
  3.  
  4. //假设IOV也使用完了....IOV,分配新的IOV
  5. if (c->iovused >= c->iovsize) {
  6. int i, iovnum;
  7. struct iovec *new_iov = (struct iovec *) realloc(c->iov,
  8. (c->iovsize * 2) * sizeof(struct iovec));
  9. if (!new_iov) {
  10. STATS_LOCK();
  11. stats.malloc_fails++;
  12. STATS_UNLOCK();
  13. return -1;
  14. }
  15. c->iov = new_iov;
  16. c->iovsize *= 2; //扩容两倍
  17.  
  18. /* Point all the msghdr structures at the new list. */
  19. for (i = 0, iovnum = 0; i < c->msgused; i++) {
  20. c->msglist[i].msg_iov = &c->iov[iovnum];
  21. iovnum += c->msglist[i].msg_iovlen;
  22. }
  23. }
  24.  
  25. return 0;
  26. }

conn_mwrite

conn_mwrite状态在drive_machine这种方法中。

主要就是向client写数据了。

从上面的add_iov方法中,我们知道Memcached会将须要待发送的数据写入c->msglist结构中。

真正写数据的方法是transmit

  1. //drive_machine方法
  2. //这个conn_mwrite是向client写数据
  3. case conn_mwrite:
  4. if (IS_UDP(c->transport) && c->msgcurr == 0
  5. && build_udp_headers(c) != 0) {
  6. if (settings.verbose > 0)
  7. fprintf(stderr, "Failed to build UDP headers\n");
  8. conn_set_state(c, conn_closing);
  9. break;
  10. }
  11. //transmit这种方法很重要,主要向client写数据的操作都在这种方法中进行
  12. //返回transmit_result枚举类型。用于推断是否写成功,假设失败,则关闭连接
  13. switch (transmit(c)) {
  14.  
  15. //假设向client发送数据成功
  16. case TRANSMIT_COMPLETE:
  17. if (c->state == conn_mwrite) {
  18. conn_release_items(c);
  19. /* XXX: I don't know why this wasn't the general case */
  20. if (c->protocol == binary_prot) {
  21. conn_set_state(c, c->write_and_go);
  22. } else {
  23. //这边是TCP的状态
  24. //状态又会切回到conn_new_cmd这个状态
  25. //conn_new_cmd主要是继续解析c->rbuf容器中剩余的命令參数
  26. conn_set_state(c, conn_new_cmd);
  27. }
  28. } else if (c->state == conn_write) {
  29. if (c->write_and_free) {
  30. free(c->write_and_free);
  31. c->write_and_free = 0;
  32. }
  33. conn_set_state(c, c->write_and_go);
  34. } else {
  35. if (settings.verbose > 0)
  36. fprintf(stderr, "Unexpected state %d\n", c->state);
  37. conn_set_state(c, conn_closing);
  38. }
  39. break;

transmit 方法

这种方法主要作用:向client发送数据

  1. //这种方法主要向client写数据
  2. //假设数据没有发送完,则会一直循环conn_mwrite这个状态,直到数据发送完毕为止
  3. static enum transmit_result transmit(conn *c) {
  4. assert(c != NULL);
  5.  
  6. //每次发送之前,都会来校验前一次的数据是否发送完了
  7. //假设前一次的msghdr结构体内的数据已经发送完了,则c->msgcurr指针就会往后移动一位,
  8. //移动到下一个等待发送的msghdr结构体指针上
  9. //c->msgcurr初始值为:0
  10. if (c->msgcurr < c->msgused && c->msglist[c->msgcurr].msg_iovlen == 0) {
  11. /* Finished writing the current msg; advance to the next. */
  12. c->msgcurr++;
  13. }
  14.  
  15. //假设c->msgcurr(已发送)小于c->msgused(已使用),则就能够知道还没发送完,则须要继续发送
  16. //假设c->msgcurr(已发送)等于c->msgused(已使用),则说明已经发送完了。返回TRANSMIT_COMPLETE状态
  17. if (c->msgcurr < c->msgused) {
  18. ssize_t res;
  19.  
  20. //从c->msglist取出一个待发送的msghdr结构
  21. struct msghdr *m = &c->msglist[c->msgcurr];
  22. //向client发送数据
  23. res = sendmsg(c->sfd, m, 0);
  24. //发送成功的情况
  25. if (res > 0) {
  26. pthread_mutex_lock(&c->thread->stats.mutex);
  27. c->thread->stats.bytes_written += res;
  28. pthread_mutex_unlock(&c->thread->stats.mutex);
  29.  
  30. /* We've written some of the data. Remove the completed
  31. iovec entries from the list of pending writes. */
  32. //这边会检查发送了多少
  33. while (m->msg_iovlen > 0 && res >= m->msg_iov->iov_len) {
  34. res -= m->msg_iov->iov_len;
  35. m->msg_iovlen--;
  36. m->msg_iov++;
  37. }
  38.  
  39. /* Might have written just part of the last iovec entry;
  40. adjust it so the next write will do the rest. */
  41. if (res > 0) {
  42. m->msg_iov->iov_base = (caddr_t) m->msg_iov->iov_base + res;
  43. m->msg_iov->iov_len -= res;
  44. }
  45. return TRANSMIT_INCOMPLETE;
  46. }
  47. //发送失败的情况
  48. if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
  49. if (!update_event(c, EV_WRITE | EV_PERSIST)) {
  50. if (settings.verbose > 0)
  51. fprintf(stderr, "Couldn't update event\n");
  52. conn_set_state(c, conn_closing);
  53. return TRANSMIT_HARD_ERROR;
  54. }
  55. return TRANSMIT_SOFT_ERROR;
  56. }
  57. /* if res == 0 or res == -1 and error is not EAGAIN or EWOULDBLOCK,
  58. we have a real error, on which we close the connection */
  59. if (settings.verbose > 0)
  60. perror("Failed to write, and not due to blocking");
  61.  
  62. if (IS_UDP(c->transport))
  63. conn_set_state(c, conn_read);
  64. else
  65. conn_set_state(c, conn_closing);
  66. return TRANSMIT_HARD_ERROR;
  67. } else {
  68. return TRANSMIT_COMPLETE;
  69. }
  70. }

conn_shrink 方法

当数据发送成功后。会跳转到conn_new_cmd这个状态继续处理,然后进入reset_cmd_handler方法,然后进入conn_shrink方法。

conn_shrink主要是用于检查buf的大小,是否超过了预定的水位,假设超过了,则须要又一次realloc。

  1. //又一次设置命令handler
  2. static void reset_cmd_handler(conn *c) {
  3. c->cmd = -1;
  4. c->substate = bin_no_state;
  5. if (c->item != NULL) {
  6. item_remove(c->item);
  7. c->item = NULL;
  8. }
  9. conn_shrink(c); //这种方法是检查c->rbuf容器的大小
  10. //假设剩余未解析的命令 > 0的话,继续跳转到conn_parse_cmd解析命令
  11. if (c->rbytes > 0) {
  12. conn_set_state(c, conn_parse_cmd);
  13. } else {
  14. //假设命令都解析完毕了。则继续等待新的数据到来
  15. conn_set_state(c, conn_waiting);
  16. }
  17. }
  1. //检查rbuf的大小
  2. static void conn_shrink(conn *c) {
  3. assert(c != NULL);
  4.  
  5. if (IS_UDP(c->transport))
  6. return;
  7.  
  8. //假设bufsize大于READ_BUFFER_HIGHWAT(8192)的时候须要又一次处理
  9. //DATA_BUFFER_SIZE等于2048,所以我们能够看到之前的代码中对rbuf最多仅仅能进行4次recalloc
  10. if (c->rsize > READ_BUFFER_HIGHWAT && c->rbytes < DATA_BUFFER_SIZE) {
  11. char *newbuf;
  12.  
  13. if (c->rcurr != c->rbuf)
  14. memmove(c->rbuf, c->rcurr, (size_t) c->rbytes); //内存移动
  15.  
  16. newbuf = (char *) realloc((void *) c->rbuf, DATA_BUFFER_SIZE);
  17.  
  18. if (newbuf) {
  19. c->rbuf = newbuf;
  20. c->rsize = DATA_BUFFER_SIZE;
  21. }
  22. /* TODO check other branch... */
  23. c->rcurr = c->rbuf;
  24. }
  25.  
  26. if (c->isize > ITEM_LIST_HIGHWAT) {
  27. item **newbuf = (item**) realloc((void *) c->ilist,
  28. ITEM_LIST_INITIAL * sizeof(c->ilist[0]));
  29. if (newbuf) {
  30. c->ilist = newbuf;
  31. c->isize = ITEM_LIST_INITIAL;
  32. }
  33. /* TODO check error condition? */
  34. }
  35.  
  36. //假设大于c->msglist的水位了。则又一次realloc
  37. if (c->msgsize > MSG_LIST_HIGHWAT) {
  38. struct msghdr *newbuf = (struct msghdr *) realloc((void *) c->msglist,
  39. MSG_LIST_INITIAL * sizeof(c->msglist[0]));
  40. if (newbuf) {
  41. c->msglist = newbuf;
  42. c->msgsize = MSG_LIST_INITIAL;
  43. }
  44. /* TODO check error condition? */
  45. }
  46.  
  47. //假设大于c->iovsize的水位了,则又一次realloc
  48. if (c->iovsize > IOV_LIST_HIGHWAT) {
  49. struct iovec *newbuf = (struct iovec *) realloc((void *) c->iov,
  50. IOV_LIST_INITIAL * sizeof(c->iov[0]));
  51. if (newbuf) {
  52. c->iov = newbuf;
  53. c->iovsize = IOV_LIST_INITIAL;
  54. }
  55. /* TODO check return value */
  56. }
  57. }

Memcached源代码分析 - Memcached源代码分析之消息回应(3)的更多相关文章

  1. RTMPdump(libRTMP) 源代码分析 10: 处理各种消息(Message)

    ===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...

  2. 分析setting源代码获取sd卡大小

    分析setting源代码获取sd卡大小 android系统有一个特点,即开源,我们可以得到任何一个应用的源代码,比如我们不知道这样的android代码怎么写,我们可以打开模拟器里面的设置(settin ...

  3. 通过分析 JDK 源代码研究 TreeMap 红黑树算法实现

    本文转载自http://www.ibm.com/developerworks/cn/java/j-lo-tree/ 目录: TreeSet 和 TreeMap 的关系 TreeMap 的添加节点 Tr ...

  4. x264源代码简单分析:宏块分析(Analysis)部分-帧间宏块(Inter)

    ===================================================== H.264源代码分析文章列表: [编码 - x264] x264源代码简单分析:概述 x26 ...

  5. x264源代码简单分析:宏块分析(Analysis)部分-帧内宏块(Intra)

    ===================================================== H.264源代码分析文章列表: [编码 - x264] x264源代码简单分析:概述 x26 ...

  6. 使用Django.core.cache操作Memcached导致性能不稳定的分析过程

    使用Django.core.cache操作Memcached导致性能不稳定的分析过程 最近测试一项目,用到了Nginx缓存服务,那可真是快啊!2Gb带宽都轻易耗尽. 不过Api接口无法简单使用Ngin ...

  7. memcached +php环境配置和分析

    一.memcached 简介 在很多场合,我们都会听到 memcached 这个名字,但很多同学只是听过,并没有用过或实际了解过,只知道它是一个很不错的东东.这里简单介绍一下,memcached 是高 ...

  8. 详细分析Memcached缓存与Mongodb数据库的优点与作用

    http://www.mini188.com/showtopic-1604.aspx 本文详细讲下Memcached和Mongodb一些看法,以及结合应用有什么好处,希望看到大家的意见和补充. Mem ...

  9. Microsoft JScript 运行时错误: Sys.WebForms.PageRequestManagerParserErrorException无法分析从服务器收到的消息。之所以出现此错误,

    Microsoft JScript 运行时错误: Sys.WebForms.PageRequestManagerParserErrorException: 无法分析从服务器收到的消息.之所以出现此错误 ...

随机推荐

  1. istringstream和ostringstream的使用方法

    写程序用到istringstream和ostringstream,看了别人的博文,借鉴~~~~~~. iostream 标准库支持内存中的输入/输出,只要将流与存储在程序内存中的 string 对象捆 ...

  2. Svn服务启动的两种方式

    一.svn服务器启动 › cmd命令行启动:vsvnserve -d –r 文档仓库路径 -d 后台执行 › -r 版本库的根目录 二.›Windows服务自动启动     利用xp.2000 以上的 ...

  3. Java基础之垃圾回收

    /** * 对象在没有任何引用可以到达时,生命周期结束,成为垃圾. * 所有对象在被回收之前都会自动调用finalize()方法. * ******************************** ...

  4. Qt保证只有一个实例(将CreateMutex得到的handle通过转换得到值)

    使用CreateMutex 可以实现只启动一个应用程序实例 view plaincopy to clipboardprint?#include <QApplication>#include ...

  5. AIDE支持实时错误检查、代码重构、代码智能导航、生成APK

    AIDE是一个Android Java集成开发环境,可以在Android系统内进行Android软件和游戏的开发.它不仅仅是一个编辑器,而是支持编写-编译-调试运行整个周期,开发人员可以在Androi ...

  6. JAVA FILE or I/O学习 - 补充CopyFiles功能

    public class CopyFiles { public static void main(String[] args) { CopyFiles copyFiles = new CopyFile ...

  7. 使用vi编辑binary文件

    原理:使用xxd将当前文件转成hex格式,编辑,然后再转回去 /usr/bin/xxd xxd - make a hexdump or do the reverse 例子: 用binary模式启动vi ...

  8. docker进入容器

    进入容器的三种方式: sshd nsenter exec sshd 在容器中开启一个SSHD的服务,通过SSH的协议登录到容器中,把容器看出一个vm nsenter: nsenter包含在util-l ...

  9. 本地yum源安装GCC

    Linux环境下yum源安装GCC 前提条件是有Linux环境的安装盘ISO文件 在Linux系统中创建两个目录,一个是用来存放ISO文件,一个是用来挂载该ISO文件,如下: $mkdir /root ...

  10. 七日筑基——C#第一天(下)

    继续C#第一天的内容,昨天我们简单说了一下如何用C#代码来让学生做自我介绍,介绍的格式要求:“我叫威震天,今年20岁,我喜欢踢足球和上网,希望接下来的三年能跟大家一起成长.”威震天介绍完了,继续下一个 ...