上文分析了memcached的autoconf过程以及configure, make过程,可以看到,memcached可执行文件是由memcached-memcached.o以及其他文件连接后编译出来的。其main函数在memcached.c中定义。
找到main,在其函数中可以得到他做了一下几件事情:
- 变量声明
- settings_init();使用默认值初始化settings
- while (-1 != (c = getopt(argc, argv, … 获取传入的变量并做相应的操作。如使用-h调用usage并退出;-m则覆盖settings.maxbytes的值,-p指定端口;及最大的内存使用量;-d则设置daemon模式,并在下方继续处理等等。
- if (settings.sasl) 检查settings.sasl是否启用。 当加了-S且系统支持sasl的情况下将启用。 SASL全称Simple Authentication and Security Layer,是一种用来扩充C/S模式验证能力的机制。在Postfix可以利用SASL来判断用户是否有权使用转发服务,或是辨认谁在使用你的服务器。
- if (tcp_specified && !udp_specified) 检查端口及协议启动选项,设定settings.udpport, settings.port
- if (maxcore != 0) 检查时候使用maxcore,如果是,则做相应的core file设置。 core file 是一种特殊的文件,当程序发生异常退出时,他的整个内存将被镜像到文件中。在debug比较管用。参考 core file
- getrlimit(RLIMIT_NOFILE, &rlim) 检查RLIMIT_NOFILE和max_conn, RLIMIT_NOFILE决定了系统能够同时打开的文件(包括网络链接)数量, 默认值是1024。即访问并发数无法超过这个数字。显然,对于负载量稍大的服务器而言,这个值会偏低。memcached可设定max_conn,默认1024, 可通过-c设定。如果设定值超过了RLIMIT_NOFILE,memcached将尝试提升系统的RLIMIT_NOFILE。
- if (getuid() == 0 || geteuid() == 0) 权限检查。 memcached不允许使用root运行,如果是root操作,必须通过-u指定其运行用户
- if (settings.sasl) 如果开启sasl,则初始化sasl
- if (do_daemonize) 如果以daemon模式运行,调用daemonize. 该参数有-d指定。 此时父进程退出,fork一个子进程,并重定向其标准输出,错误输出到/dev/null, 并继续工作。
- if (lock_memory) 检查lock_memory。该值由-k设定。 若开启,则将memcached的内存固定在物理内存中,不容许交换到swap中去。(另还需系统支持,可检查config.h中是否有HAVE_MLOCKALL定义)
- main_base = event_init(); 初始化event_init
- stats_init 初始化状态信息。
- assoc_init 初始化hash表。建立64K个void指针
- conn_init 初始化连接池。 这里可以看出来,memcached的连接池数freetotal=200. 这是memcached的最大并发。
- slabs_init(settings.maxbytes, settings.factor, preallocate); 初始化slab。 这里传入了三个参数,maxbytes是通过-m指定的,memcached的最大内存使用量, 最大不得超过2G. factor是递增因子,可通过-f参数设置。默认是1.25. 最大slab数量为1024 * 1024,最大值可通过-I修改,但不建议超过1024 * 1024。
- sigignore(SIGPIPE) 忽略SIGPIPE信号。 在Linux下写socket的程序的时候,如果尝试send到一个disconnected socket上,底层将抛出一个SIGPIPE信号。如果程序没有处理或没有忽略,这个信号将导致程序退出。这不是我们需要的。
- thread_init(settings.num_threads, main_base); 初始化线程。线程数量可有-t设定。在这里会建立四个互斥锁, 其中两个锁比较重要: cache_lock将在新分配slab等影响全局结构操作时用到。cache_lock锁在新建立客户连接等时用到。
- start_assoc_maintenance_thread() 建立主线程
- clock_handler
- 根据启动模式,建立socket服务或者tcp/utp服务
- socket模式 server_socket_unix(settings.socketpath,settings.access)
- tcp/udp模式 server_sockets(settings.port, tcp_transport, portnumber_file)
- drop_privileges();资源释放
- event_base_loop(main_base, 0);进入event无限循环。程序将不再继续往下一行执行了。除非收到终端结束信号
- stop_assoc_maintenance_thread(); 关闭主线程
- remove_pidfile(pid_file); 移除pid文件
- 其他资源释放
再分析一下settings的定义,这可以瞥见影响memcached运行状态的设定。
/** * Globally accessible settings as derived from the commandline. */ struct settings { size_t maxbytes; //最大内存, 默认64M,最大2G。通过-m 设定 int maxconns; //最大连接数,默认1024 通过-c设定 int port; //tcp 端口号,通过-p 设置 int udpport; //ucp 端口号,通过-U 设置 char *inter; //监听IP或SOCKET地址 ,通过-l设定 int verbose; //是否输出debug信息。由-v,-vvv参数设定 rel_time_t oldest_live; /* ignore existing items older than this */ //时间设定,当使用flsuh时,只需要修改本值,当取出的值时间小于本值时,将被忽略。 int evict_to_free; //当内存存满时,是否淘汰老数据。默认是是。可用-M修改为否。此时内容耗尽时,新插入数据时将返回失败。 char *socketpath; /* path to unix socket if using local socket */ //socket模式,使用-s设定。 int access; /* access mask (a la chmod) for unix domain socket */ //socket文件的文件权限,使用-a设定 double factor; /* chunk size growth factor */ //slab分配增量因子,默认围1.25, 可通过-f设定 int chunk_size; //给一个key+value+flags 分配的最小字节数。 默认值为48. 可通过-n修改。 int num_threads; /* number of worker (without dispatcher) libevent threads to run */ //工作线程数。默认围4, 可通过-t设定 char prefix_delimiter; /* character that marks a key prefix (for stats) */ //状态详情的key前缀 int detail_enabled; /* nonzero if we're collecting detailed stats */ //开启状态详情记录 int reqs_per_event; /* Maximum number of io to process on each io-event. */ //每个event处理的请求数 bool use_cas; //开启cas,"cas"是一个存储检查操作。用来检查脏数据的存操作。在取出数据后,如果没有其他人修改此数据时,本进程才能够存储数据。默认为开启。这里是新发现的亮点函数php使用参考: memcached_cas enum protocol binding_protocol; //使用协议, 试过-B参数设定。 可能值为:ascii, binary, or auto int backlog; int item_size_max; /* Maximum item size, and upper end for slabs */ //单个item最大字计数。默认1M。可通过-I参数修改。纠正一个过期的观点,这个值实际上可以大于1M,必须小于128M。但nencached会抛出警告,大于1M将导致整体运行内存的增加和内存性能的降低。 bool sasl; /* SASL on/off */ //是否开启sasl };
本文代码基于memcache 1.4.5_14_gaf48b10 版本分析