Memcached源码阅读

Memcached源码阅读之服务器资源调整

本篇博客作为Memcached源码分析的开篇,这次阅读的源码版本为:1.4.15,开源软件各个版本之间差异比较大,同学们学习时,记得核对版本。

memcached的main函数位于memcached.c文件中,从main函数启动之后,会初始化一些资源和申请一些服务器资源,如下面所示:

1 Core文件大小和进程打开文件个数限制的调整。

[cpp]  view plain  copy
 
  1. if (maxcore != 0)  
  2. {  
  3.     struct rlimit rlim_new;  
  4.     //获取当前Core文件大小的配置值  
  5.     if (getrlimit(RLIMIT_CORE, &rlim) == 0)  
  6.     {  
  7.                 //变量初始化为无限制  
  8.                 rlim_new.rlim_cur = rlim_new.rlim_max = RLIM_INFINITY;  
  9.         if (setrlimit(RLIMIT_CORE, &rlim_new) != 0)//如果设置失败  
  10.         {  
  11.             //变量初始化为当前值的最大值  
  12.             rlim_new.rlim_cur = rlim_new.rlim_max = rlim.rlim_max;  
  13.             (void) setrlimit(RLIMIT_CORE, &rlim_new);//重新进行设置  
  14.         }  
  15.     }  
  16.       
  17.         //再次确认Core文件允许的大小,如果当前的Core文件的大小为0,则不允许Core文件产生,和maxcore!=0不符,程序结束  
  18.     if ((getrlimit(RLIMIT_CORE, &rlim) != 0) || rlim.rlim_cur == 0)  
  19.     {  
  20.         fprintf(stderr, "failed to ensure corefile creation\n");  
  21.         exit(EX_OSERR);  
  22.     }  
  23. }  
  24. //读取进程允许打开的文件数信息,读取失败,程序退出  
  25. if (getrlimit(RLIMIT_NOFILE, &rlim) != 0)  
  26. {  
  27.     fprintf(stderr, "failed to getrlimit number of files\n");  
  28.     exit(EX_OSERR);  
  29. }  
  30. else  
  31. {       //按memcached启动时的指定的最大连接数进行设置  
  32.     rlim.rlim_cur = settings.maxconns;  
  33.     rlim.rlim_max = settings.maxconns;  
  34.     if (setrlimit(RLIMIT_NOFILE, &rlim) != 0)  
  35.     {  
  36.         fprintf(stderr,  
  37.                 "failed to set rlimit for open files. Try starting as root or requesting smaller maxconns value.\n");  
  38.         exit(EX_OSERR);  
  39.     }  
  40. }  

2 启动用户的选择。

[cpp]  view plain  copy
 
  1. //uid==0表示以root运行程序  
  2. if (getuid() == 0 || geteuid() == 0)  
  3. {       //以root运行程序,同时未指定新的用户名称  
  4.         if (username == 0 || *username == '\0')  
  5.     {  
  6.         fprintf(stderr, "can't run as root without the -u switch\n");  
  7.         exit(EX_USAGE);  
  8.     }  
  9.         //判断是否存在指定的用户名称  
  10.         if ((pw = getpwnam(username)) == 0)  
  11.     {  
  12.         fprintf(stderr, "can't find the user %s to switch to\n", username);  
  13.         exit(EX_NOUSER);  
  14.     }  
  15.         //按新的用户修改memcached的执行权限位  
  16.         if (setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0)  
  17.     {  
  18.         fprintf(stderr, "failed to assume identity of user %s\n", username);  
  19.         exit(EX_OSERR);  
  20.     }  
  21. }  
3 以daemon的方式启动,daemon的实现如下,该daemon没有进行2次fork,APUE上面也有说第二次fork不是必须的。

[cpp]  view plain  copy
 
  1. int daemonize(int nochdir, int noclose)  
  2. {  
  3.     int fd;  
  4.     //首先fork一次  
  5.     switch (fork()) {  
  6.     case -1://fork失败,程序结束  
  7.         return (-1);  
  8.     case 0://子进程执行下面的流程  
  9.         break;  
  10.     default://父进程安全退出  
  11.         _exit(EXIT_SUCCESS);  
  12.     }  
  13.     //setsid调用成功之后,返回新的会话的ID,调用setsid函数的进程成为新的会话的领头进程,并与其父进程的会话组和进程组脱离  
  14.     if (setsid() == -1)  
  15.         return (-1);  
  16.   
  17.     if (nochdir == 0) {  
  18.         //进程的当前目录切换到根目录下,根目录是一直存在的,其他的目录就不保证  
  19.         if(chdir("/") != 0) {  
  20.             perror("chdir");  
  21.             return (-1);  
  22.         }  
  23.     }  
  24.   
  25.     if (noclose == 0 && (fd = open("/dev/null", O_RDWR, 0)) != -1) {  
  26.         if(dup2(fd, STDIN_FILENO) < 0) {//将标准输入重定向到/dev/null下  
  27.             perror("dup2 stdin");  
  28.             return (-1);  
  29.         }  
  30.         if(dup2(fd, STDOUT_FILENO) < 0) {//将标准输出重定向到/dev/null下  
  31.             perror("dup2 stdout");  
  32.             return (-1);  
  33.         }  
  34.         if(dup2(fd, STDERR_FILENO) < 0) {//将标准错误重定向到/dev/null下  
  35.             perror("dup2 stderr");  
  36.             return (-1);  
  37.         }  
  38.   
  39.         if (fd > STDERR_FILENO) {  
  40.             if(close(fd) < 0) {//大于2的描述符都可以关闭  
  41.                 perror("close");  
  42.                 return (-1);  
  43.             }  
  44.         }  
  45.     }  
  46.     return (0);  
  47. }  

4 锁定内存,默认分配的内存都是虚拟内存,在程序执行过程中可以按需换出,如果内存充足的话,可以锁定内存,不让系统将该进程所持有的内存换出。

[cpp]  view plain  copy
 
  1. if (lock_memory)  
  2. {  
  3. #ifdef HAVE_MLOCKALL  
  4.     int res = mlockall(MCL_CURRENT | MCL_FUTURE);  
  5.     if (res != 0)  
  6.     {  
  7.         fprintf(stderr, "warning: -k invalid, mlockall() failed: %s\n",  
  8.                 strerror(errno));  
  9.     }  
  10. #else  
  11.     fprintf(stderr,  
  12.             "warning: -k invalid, mlockall() not supported on this platform.  proceeding without.\n");  
  13. #endif  
  14. }  

5 忽略PIPE信号,PIPE信号是当网络连接一端已经断开,这时发送数据,会进行RST的重定向,再次发送数据,会触发PIPE信号,而PIPE信号的默认动作是退出进程,所以需要忽略该信号。

[cpp]  view plain  copy
 
  1. if (sigignore(SIGPIPE) == -1)  
  2. {  
  3.     perror("failed to ignore SIGPIPE; sigaction");  
  4.     exit(EX_OSERR);  
  5. }  
6 保存daemon进程的进程id到文件中,这样便于控制程序,读取文件内容,即可得到进程ID。 

[cpp]  view plain  copy
 
  1. if (pid_file != NULL)  
  2. {  
  3.     save_pid(pid_file);  
  4. }  

Memcached源码阅读之初始化参数解析

Memcached启动时,有很多配置参数可以选择,这些配置参数严重影响着Memcached的使用,下面分析下这些参数的意义,开源软件版本之间差异比较大,我这次分析是基于1.4.15进行分析的,大家学习时记得核对版本。

[cpp]  view plain  copy
 
  1. "a:" //unix socket的权限位信息,unix socket的权限位信息和普通文件的权限位信息一样  
  2. "p:" //memcached监听的TCP端口值,默认是11211  
  3. "s:" //unix socket监听的socket文件路径  
  4. "U:" //memcached监听的UDP端口值,默认是11211  
  5. "m:" //memcached使用的最大内存值,默认是64M  
  6. "M"  //当memcached的内存使用完时,不进行LRU淘汰数据,直接返回错误,该选项就是关闭LRU  
  7. "c:" //memcached的最大连接数,如果不指定,按系统的最大值进行  
  8. "k"  //是否锁定memcached所持有的内存,如果锁定了内存,其他业务持有的内存就会减小  
  9. "hi" //帮助信息  
  10. "r"  //core文件的大小,如果不指定,按系统的最大值进行  
  11. "v"  //调试信息  
  12. "d"  //设定以daemon方式运行  
  13. "l:" //绑定的ip信息,如果服务器有多个ip,可以在多个ip上面启动多个Memcached实例,注意:这个不是可接收的IP地址  
  14. "u:" //memcached运行的用户,如果以root启动,需要指定用户,否则程序错误,退出。  
  15. "P:" //memcached以daemon方式运行时,保存pid的文件路径信息  
  16. "f:" //内存的扩容因子,这个关系到Memcached内部初始化空间时的一个变化,后面详细说明  
  17. "n:" //chunk的最小大小(byte),后续的增长都是该值*factor来进行增长的  
  18. "t:" //内部worker线程的个数,默认是4个,最大值推荐不超过64个  
  19. "D:" //内部数据存储时的分割符  
  20. "L"  //指定内存页的大小,默认内存页大小为4K,页最大不超过2M,调大页的大小,可有效减小页表的大小,提高内存访问的效率  
  21. "R:" //单个worker的最大请求个数  
  22. "C"  //禁用业务的cas,即compare and set  
  23. "b:" //listen操作缓存连接个数  
  24. "B:" //memcached内部使用的协议,支持二进制协议和文本协议,早期只有文本协议,二进制协议是后续加上的  
  25. "I:" //单个item的最大值,默认是1M,可以修改,修改的最小值为1k,最大值不能超过128M  
  26. "S"  //打开sasl安全协议  
  27. "o:" //有四个参数项可以设置:  
  28.  maxconns_fast(如果连接数超过最大连接数,立即关闭新的连接)  
  29.  hashpower(hash表的大小的指数值,是按1<<hashpower来创建hash表的,默认的hashpower为16,配置值建议不超过64)  
  30.  slab_reassign(是否调整/平衡各个slab所占的内存)  
  31.  slab_automove(是否自动移动各个slab,如果该选项打开,会有专门的线程来进行slab的调整)  

Memcached内部是通过settings来抽象上面的这些初始化参数。

[cpp]  view plain  copy
 
  1. struct settings {  
  2.     size_t maxbytes;  
  3.     int maxconns;  
  4.     int port;  
  5.     int udpport;  
  6.     char *inter;  
  7.     int verbose;  
  8.     rel_time_t oldest_live; /* ignore existing items older than this */  
  9.     int evict_to_free;  
  10.     char *socketpath;   /* path to unix socket if using local socket */  
  11.     int access;  /* access mask (a la chmod) for unix domain socket */  
  12.     double factor;          /* chunk size growth factor */  
  13.     int chunk_size;  
  14.     int num_threads;        /* number of worker (without dispatcher) libevent threads to run */  
  15.     int num_threads_per_udp; /* number of worker threads serving each udp socket */  
  16.     char prefix_delimiter;  /* character that marks a key prefix (for stats) */  
  17.     int detail_enabled;     /* nonzero if we're collecting detailed stats */  
  18.     int reqs_per_event;     /* Maximum number of io to process on each 
  19.                                io-event. */  
  20.     bool use_cas;  
  21.     enum protocol binding_protocol;  
  22.     int backlog;  
  23.     int item_size_max;        /* Maximum item size, and upper end for slabs */  
  24.     bool sasl;              /* SASL on/off */  
  25.     bool maxconns_fast;     /* Whether or not to early close connections */  
  26.     bool slab_reassign;     /* Whether or not slab reassignment is allowed */  
  27.     int slab_automove;     /* Whether or not to automatically move slabs */  
  28.     int hashpower_init;     /* Starting hash power level */  
  29. };  

改结构的初始化:

[cpp]  view plain  copy
 
  1. static void settings_init(void)  
  2. {  
  3.     settings.use_cas = true;  
  4.     settings.access = 0700;  
  5.     settings.port = 11211;  
  6.     settings.udpport = 11211;  
  7.     /* By default this string should be NULL for getaddrinfo() */  
  8.     settings.inter = NULL;  
  9.     settings.maxbytes = 64 * 1024 * 1024; /* default is 64MB */  
  10.     settings.maxconns = 1024; /* to limit connections-related memory to about 5MB */  
  11.     settings.verbose = 0;  
  12.     settings.oldest_live = 0;  
  13.     settings.evict_to_free = 1; /* push old items out of cache when memory runs out */  
  14.     settings.socketpath = NULL; /* by default, not using a unix socket */  
  15.     settings.factor = 1.25;  
  16.     settings.chunk_size = 48; /* space for a modest key and value */  
  17.     settings.num_threads = 4; /* N workers */  
  18.     settings.num_threads_per_udp = 0;  
  19.     settings.prefix_delimiter = ':';  
  20.     settings.detail_enabled = 0;  
  21.     settings.reqs_per_event = 20;  
  22.     settings.backlog = 1024;  
  23.     settings.binding_protocol = negotiating_prot;  
  24.     settings.item_size_max = 1024 * 1024; /* The famous 1MB upper limit. */  
  25.     settings.maxconns_fast = false;  
  26.     settings.hashpower_init = 0;  
  27.     settings.slab_reassign = false;  
  28.     settings.slab_automove = 0;  
  29. }  
这些值都是一些默认值,后续按启动时所指定的进行修改,比如对监听端口号的修改:

[cpp]  view plain  copy
 
  1.         case 'a'://修改unix socket的权限位信息  
  2.         settings.access = strtol(optarg, NULL, 8);  
  3.         break;  
  4.     case 'U'://udp端口信息  
  5.         settings.udpport = atoi(optarg);  
  6.         udp_specified = true;  
  7.         break;  
  8.     case 'p'://tcp端口信息  
  9.         settings.port = atoi(optarg);  
  10.         tcp_specified = true;  
  11.         break;  

Memcached源码阅读之资源初始化

Memcached内部有hash表,各种统计信息,工作线程,网络,连接,内存结构等,在memcached启动时(执行main函数),会对这些资源进行初始化的,网络和内存的初始化操作放到后续分析,这次分析hash表,统计信息,工作线程,网络连接的初始化过程。

1 hash表的初始化 

[cpp]  view plain  copy
 
  1. //hash表的初始化,传入的参数是启动时传入的  
  2. assoc_init(settings.hashpower_init);  
  3. //hashsize的实现  
  4. #define hashsize(n) ((ub4)1<<(n))  
  5. //主hash表结构定义,在hash表扩容时,会有次hash表,所以有主次hash表区分,该结构是指针的指针,也即相当于数组指针  
  6. static item** primary_hashtable = 0;  
  7.   
  8. void assoc_init(const int hashtable_init) {  
  9.     if (hashtable_init) {//如果设置了初始化参数,则按设置的参数进行初始化  
  10.         hashpower = hashtable_init;  
  11.     }  
  12.     //hashpower的默认值为16,如果未设置新值,则按默认值进行初始化  
  13.     primary_hashtable = calloc(hashsize(hashpower), sizeof(void *));  
  14.     if (! primary_hashtable) {  
  15.         fprintf(stderr, "Failed to init hashtable.\n");  
  16.         exit(EXIT_FAILURE);  
  17.     }  
  18.     STATS_LOCK();//全局统计信息加锁,保证数据同步  
  19.     stats.hash_power_level = hashpower;  
  20.     stats.hash_bytes = hashsize(hashpower) * sizeof(void *);  
  21.     STATS_UNLOCK();  
  22. }  

2 统计信息的初始化

Memcached内部有很多全局的统计信息,用于实时获取各个资源的使用情况,后面将会看到,所有对统计信息的更新都需要加锁,而这些信息的更新是和Memcached的操作次数同数量级的,所以,在一定程度来说,这些统计信息对性能有影响。

stats结构是对统计信息的一个抽象,各个字段都比较好理解,不做解释。

[cpp]  view plain  copy
 
  1. struct stats {  
  2.     pthread_mutex_t mutex;  
  3.     unsigned int  curr_items;  
  4.     unsigned int  total_items;  
  5.     uint64_t      curr_bytes;  
  6.     unsigned int  curr_conns;  
  7.     unsigned int  total_conns;  
  8.     uint64_t      rejected_conns;  
  9.     unsigned int  reserved_fds;  
  10.     unsigned int  conn_structs;  
  11.     uint64_t      get_cmds;  
  12.     uint64_t      set_cmds;  
  13.     uint64_t      touch_cmds;  
  14.     uint64_t      get_hits;  
  15.     uint64_t      get_misses;  
  16.     uint64_t      touch_hits;  
  17.     uint64_t      touch_misses;  
  18.     uint64_t      evictions;  
  19.     uint64_t      reclaimed;  
  20.     time_t        started;          /* when the process was started */  
  21.     bool          accepting_conns;  /* whether we are currently accepting */  
  22.     uint64_t      listen_disabled_num;  
  23.     unsigned int  hash_power_level; /* Better hope it's not over 9000 */  
  24.     uint64_t      hash_bytes;       /* size used for hash tables */  
  25.     bool          hash_is_expanding; /* If the hash table is being expanded */  
  26.     uint64_t      expired_unfetched; /* items reclaimed but never touched */  
  27.     uint64_t      evicted_unfetched; /* items evicted but never touched */  
  28.     bool          slab_reassign_running; /* slab reassign in progress */  
  29.     uint64_t      slabs_moved;       /* times slabs were moved around */  
  30. };  

统计信息的初始化也就是对stats变量的一个初始化。

[cpp]  view plain  copy
 
  1. //全局对象的定义  
  2. struct stats stats;  
  3. //全局变量的初始化,该全局变量在memcached启动之后,一直使用  
  4. static void stats_init(void)  
  5. {  
  6.     stats.curr_items = stats.total_items = stats.curr_conns =  
  7.             stats.total_conns = stats.conn_structs = 0;  
  8.     stats.get_cmds = stats.set_cmds = stats.get_hits = stats.get_misses =  
  9.             stats.evictions = stats.reclaimed = 0;  
  10.     stats.touch_cmds = stats.touch_misses = stats.touch_hits =  
  11.             stats.rejected_conns = 0;  
  12.     stats.curr_bytes = stats.listen_disabled_num = 0;  
  13.     stats.hash_power_level = stats.hash_bytes = stats.hash_is_expanding = 0;  
  14.     stats.expired_unfetched = stats.evicted_unfetched = 0;  
  15.     stats.slabs_moved = 0;  
  16.     stats.accepting_conns = true/* assuming we start in this state. */  
  17.     stats.slab_reassign_running = false;  
  18.   
  19.     /* make the time we started always be 2 seconds before we really 
  20.      did, so time(0) - time.started is never zero.  if so, things 
  21.      like 'settings.oldest_live' which act as booleans as well as 
  22.      values are now false in boolean context... */  
  23.     process_started = time(0) - 2;  
  24.     stats_prefix_init();  
  25. }  

3 工作线程的初始化

Memcached采用了典型的Master-Worker的线程模式,Master就是由main线程来充当,而Worker线程则是通过Pthread创建的。

[cpp]  view plain  copy
 
  1. //传入线程个数和libevent的main_base实例  
  2. thread_init(settings.num_threads, main_base);  
[cpp]  view plain  copy
 
  1. //工作线程初始化  
  2. void thread_init(int nthreads, struct event_base *main_base) {  
  3.     int         i;  
  4.     int         power;  
  5.     //初始化各种锁和条件变量  
  6.     pthread_mutex_init(&cache_lock, NULL);  
  7.     pthread_mutex_init(&stats_lock, NULL);  
  8.   
  9.     pthread_mutex_init(&init_lock, NULL);  
  10.     pthread_cond_init(&init_cond, NULL);  
  11.   
  12.     pthread_mutex_init(&cqi_freelist_lock, NULL);  
  13.     cqi_freelist = NULL;  
  14.      
  15.     //Memcached对hash桶的锁采用分段锁,按线程个数来分段,默认总共是1<<16个hash桶,而锁的数目是1<<power个   
  16.     /* Want a wide lock table, but don't waste memory */  
  17.     if (nthreads < 3) {  
  18.         power = 10;  
  19.     } else if (nthreads < 4) {  
  20.         power = 11;  
  21.     } else if (nthreads < 5) {  
  22.         power = 12;  
  23.     } else {  
  24.         /* 8192 buckets, and central locks don't scale much past 5 threads */  
  25.         power = 13;  
  26.     }  
  27.   
  28.     item_lock_count = hashsize(power);  
  29.     //申请1<<power个pthread_mutex_t锁,保存在item_locks数组。  
  30.     item_locks = calloc(item_lock_count, sizeof(pthread_mutex_t));  
  31.     if (! item_locks) {  
  32.         perror("Can't allocate item locks");  
  33.         exit(1);  
  34.     }  
  35.     //对这些锁进行初始化,这部分可参考APUE的线程部分  
  36.     for (i = 0; i < item_lock_count; i++) {  
  37.         pthread_mutex_init(&item_locks[i], NULL);  
  38.     }  
  39.     /*创建线程的局部变量,该局部变量的名称为item_lock_type_key,用于保存主hash表所持有的锁的类型 
  40.     主hash表在进行扩容时,该锁类型会变为全局的锁,否则(不在扩容过程中),则是局部锁*/  
  41.     pthread_key_create(&item_lock_type_key, NULL);  
  42.     pthread_mutex_init(&item_global_lock, NULL);  
  43.     //申请nthreds个工作线程,LIBEVENT_THREAD是Memcached内部对工作线程的一个封装  
  44.     threads = calloc(nthreads, sizeof(LIBEVENT_THREAD));  
  45.     if (! threads) {  
  46.         perror("Can't allocate thread descriptors");  
  47.         exit(1);  
  48.     }  
  49.     /*分发线程的初始化,分发线程的base为main_base 
  50.     线程id为main线程的线程id*/  
  51.     dispatcher_thread.base = main_base;  
  52.     dispatcher_thread.thread_id = pthread_self();  
  53.     //工作线程的初始化,工作线程和主线程(main线程)是通过pipe管道进行通信的  
  54.     for (i = 0; i < nthreads; i++) {  
  55.         int fds[2];  
  56.         if (pipe(fds)) {//初始化pipe管道  
  57.             perror("Can't create notify pipe");  
  58.             exit(1);  
  59.         }  
  60.   
  61.         threads[i].notify_receive_fd = fds[0];//读管道绑定到工作线程的接收消息的描述符  
  62.         threads[i].notify_send_fd = fds[1];//写管道绑定到工作线程的发送消息的描述符  
  63.   
  64.         setup_thread(&threads[i]);//添加工作线程到libevent中  
  65.         /* Reserve three fds for the libevent base, and two for the pipe */  
  66.         stats.reserved_fds += 5;//统计信息更新  
  67.     }  
  68.   
  69.     //创建工作线程  
  70.     for (i = 0; i < nthreads; i++) {  
  71.         create_worker(worker_libevent, &threads[i]);  
  72.     }  
  73.   
  74.     //等待所有工作线程创建完毕  
  75.     pthread_mutex_lock(&init_lock);  
  76.     wait_for_thread_registration(nthreads);  
  77.     pthread_mutex_unlock(&init_lock);  
  78. }  
  79. //Memcached内部工作线程的封装  
  80. typedef struct {  
  81.     pthread_t thread_id;       //线程ID  
  82.     struct event_base *base;   //libevent的不是线程安全的,每个工作线程持有一个libevent实例,用于pipe管道通信和socket通信  
  83.     struct event notify_event; //用于监听pipe管道的libevent事件  
  84.     int notify_receive_fd;      //接收pipe管道消息描述符  
  85.     int notify_send_fd;         //发送pipe管道消息描述符  
  86.     struct thread_stats stats;  //每个线程对应的统计信息  
  87.     struct conn_queue *new_conn_queue; //每个线程都有一个工作队列,主线程接受的连接,挂载到该消息队列中  
  88.     cache_t *suffix_cache;      //后缀cache  
  89.     uint8_t item_lock_type;     //线程操作hash表持有的锁类型,有局部锁和全局锁  
  90. } LIBEVENT_THREAD;  
  91.   
  92. //分发线程的封装  
  93. typedef struct {  
  94.     pthread_t thread_id;        //线程id  
  95.     struct event_base *base;    //libevent实例  
  96. } LIBEVENT_DISPATCHER_THREAD;  
  97.   
  98. //工作线程绑定到libevent实例  
  99. static void setup_thread(LIBEVENT_THREAD *me) {  
  100.     me->base = event_init();//创建libevent实例  
  101.     if (! me->base) {  
  102.         fprintf(stderr, "Can't allocate event base\n");  
  103.         exit(1);  
  104.     }  
  105.   
  106.     //创建管道读的libevent事件,事件的回调函数处理具体的业务信息,关于回调函数的处理,后续分析  
  107.     event_set(&me->notify_event, me->notify_receive_fd,  
  108.               EV_READ | EV_PERSIST, thread_libevent_process, me);  
  109.     event_base_set(me->base, &me->notify_event);//设置libevent实例  
  110.     //添加事件到libevent中  
  111.     if (event_add(&me->notify_event, 0) == -1) {  
  112.         fprintf(stderr, "Can't monitor libevent notify pipe\n");  
  113.         exit(1);  
  114.     }  
  115.     //创建消息队列,用于接受主线程连接  
  116.     me->new_conn_queue = malloc(sizeof(struct conn_queue));  
  117.     if (me->new_conn_queue == NULL) {  
  118.         perror("Failed to allocate memory for connection queue");  
  119.         exit(EXIT_FAILURE);  
  120.     }  
  121.     cq_init(me->new_conn_queue);//消息队列初始化  
  122.   
  123.     if (pthread_mutex_init(&me->stats.mutex, NULL) != 0) {  
  124.         perror("Failed to initialize mutex");  
  125.         exit(EXIT_FAILURE);  
  126.     }  
  127.     //创建线程的后缀cache,没搞懂这个cache有什么作用。  
  128.     me->suffix_cache = cache_create("suffix", SUFFIX_SIZE, sizeof(char*),  
  129.                                     NULL, NULL);  
  130.     if (me->suffix_cache == NULL) {  
  131.         fprintf(stderr, "Failed to create suffix cache\n");  
  132.         exit(EXIT_FAILURE);  
  133.     }  
  134. }   
  135.   
  136. //创建工作线程  
  137. static void create_worker(void *(*func)(void *), void *arg) {  
  138.     pthread_t       thread;  
  139.     pthread_attr_t  attr;  
  140.     int             ret;  
  141.   
  142.     pthread_attr_init(&attr);//Posix线程部分,线程属性初始化  
  143.     //通过pthread_create创建线程,线程处理函数是通过外部传入的处理函数为worker_libevent  
  144.     if ((ret = pthread_create(&thread, &attr, func, arg)) != 0) {  
  145.         fprintf(stderr, "Can't create thread: %s\n",  
  146.                 strerror(ret));  
  147.         exit(1);  
  148.     }  
  149. }  
  150. //线程处理函数  
  151. static void *worker_libevent(void *arg) {  
  152.     LIBEVENT_THREAD *me = arg;  
  153.     //默认的hash表的锁为局部锁  
  154.     me->item_lock_type = ITEM_LOCK_GRANULAR;  
  155.     pthread_setspecific(item_lock_type_key, &me->item_lock_type);//设定线程的属性  
  156.     //用于控制工作线程初始化,通过条件变量来控制  
  157.     register_thread_initialized();  
  158.     //工作线程的libevent实例启动  
  159.     event_base_loop(me->base, 0);  
  160.     return NULL;  
  161. }  
  162. //阻塞工作线程  
  163. static void wait_for_thread_registration(int nthreads) {  
  164.     while (init_count < nthreads) {  
  165.         pthread_cond_wait(&init_cond, &init_lock);//在条件变量init_cond上面阻塞,阻塞个数为nthreads-init_count  
  166.     }  
  167. }  
  168. //唤醒工作线程  
  169. static void register_thread_initialized(void) {  
  170.     pthread_mutex_lock(&init_lock);  
  171.     init_count++;  
  172.     pthread_cond_signal(&init_cond);  
  173.     pthread_mutex_unlock(&init_lock);  
  174. }  
  175.   
  176. //每个线程持有的统计信息  
  177. struct thread_stats {  
  178.     pthread_mutex_t   mutex;  
  179.     uint64_t          get_cmds;  
  180.     uint64_t          get_misses;  
  181.     uint64_t          touch_cmds;  
  182.     uint64_t          touch_misses;  
  183.     uint64_t          delete_misses;  
  184.     uint64_t          incr_misses;  
  185.     uint64_t          decr_misses;  
  186.     uint64_t          cas_misses;  
  187.     uint64_t          bytes_read;  
  188.     uint64_t          bytes_written;  
  189.     uint64_t          flush_cmds;  
  190.     uint64_t          conn_yields; /* # of yields for connections (-R option)*/  
  191.     uint64_t          auth_cmds;  
  192.     uint64_t          auth_errors;  
  193.     struct slab_stats slab_stats[MAX_NUMBER_OF_SLAB_CLASSES];  
  194. };   
  195. //每个slab的统计信息  
  196. struct slab_stats {  
  197.     uint64_t  set_cmds;  
  198.     uint64_t  get_hits;  
  199.     uint64_t  touch_hits;  
  200.     uint64_t  delete_hits;  
  201.     uint64_t  cas_hits;  
  202.     uint64_t  cas_badval;  
  203.     uint64_t  incr_hits;  
  204.     uint64_t  decr_hits;  
  205. };  

4 连接的初始化

[cpp]  view plain  copy
 
  1. static conn **freeconns;//空闲连接列表  
  2. //连接初始化  
  3. static void conn_init(void)  
  4. {  
  5.     freetotal = 200;//空闲连接总数  
  6.     freecurr = 0;//当前空闲的索引  
  7.         //申请200个空间  
  8.         if ((freeconns = calloc(freetotal, sizeof(conn *))) == NULL)  
  9.     {  
  10.         fprintf(stderr, "Failed to allocate connection structures\n");  
  11.     }  
  12.     return;  
  13. }  


上文来自:http://blog.csdn.net/lcli2009?viewmode=contents

你可能感兴趣的:(C++,memcached,服务器,C++11)