【原创】memcached 中的命令行参数解析

      本文主要是以 memcached 源码为例,讲解如何在 linux 下解析命令行参数。

安装 memcached 后,查看其可用选项:
[root@Betty ~]# memcached -h
memcached 1.4.14
-p <num>      TCP port number to listen on (default: 11211)
-U <num>      UDP port number to listen on (default: 11211, 0 is off)
-s <file>     UNIX socket path to listen on (disables network support)
-a <mask>     access mask for UNIX socket, in octal (default: 0700)
-l <addr>     interface to listen on (default: INADDR_ANY, all addresses)
              <addr> may be specified as host:port. If you don't specify
              a port number, the value you specified with -p or -U is
              used. You may specify multiple addresses separated by comma
              or by using -l multiple times
-d            run as a daemon
-r            maximize core file limit
-u <username> assume identity of <username> (only when run as root)
-m <num>      max memory to use for items in megabytes (default: 64 MB)
-M            return error on memory exhausted (rather than removing items)
-c <num>      max simultaneous connections (default: 1024)
-k            lock down all paged memory.  Note that there is a
              limit on how much memory you may lock.  Trying to
              allocate more than that would fail, so be sure you
              set the limit correctly for the user you started
              the daemon with (not for -u <username> user;
              under sh this is done with 'ulimit -S -l NUM_KB').
-v            verbose (print errors/warnings while in event loop)
-vv           very verbose (also print client commands/reponses)
-vvv          extremely verbose (also print internal state transitions)
-h            print this help and exit
-i            print memcached and libevent license
-P <file>     save PID in <file>, only used with -d option
-f <factor>   chunk size growth factor (default: 1.25)
-n <bytes>    minimum space allocated for key+value+flags (default: 48)
-L            Try to use large memory pages (if available). Increasing
              the memory page size could reduce the number of TLB misses
              and improve the performance. In order to get large pages
              from the OS, memcached will allocate the total item-cache
              in one large chunk.
-D <char>     Use <char> as the delimiter between key prefixes and IDs.
              This is used for per-prefix stats reporting. The default is
              ":" (colon). If this option is specified, stats collection
              is turned on automatically; if not, then it may be turned on
              by sending the "stats detail on" command to the server.
-t <num>      number of threads to use (default: 4)
-R            Maximum number of requests per event, limits the number of
              requests process for a given connection to prevent 
              starvation (default: 20)
-C            Disable use of CAS
-b            Set the backlog queue limit (default: 1024)
-B            Binding protocol - one of ascii, binary, or auto (default)
-I            Override the size of each slab page. Adjusts max item size
              (default: 1mb, min: 1k, max: 128m)
-o            Comma separated list of extended or experimental options
              - (EXPERIMENTAL) maxconns_fast: immediately close new
                connections if over maxconns limit
              - hashpower: An integer multiplier for how large the hash
                table should be. Can be grown at runtime if not big enough.
                Set this based on "STAT hash_power_level" before a 
                restart.
[root@Betty ~]#
那么,memcached 到底是如何解析上述参数的呢?具体代码如下:
/* process arguments */
    while (-1 != (c = getopt(argc, argv,
          "a:"  /* access mask for unix socket */
          "p:"  /* TCP port number to listen on */
          "s:"  /* unix socket path to listen on */
          "U:"  /* UDP port number to listen on */
          "m:"  /* max memory to use for items in megabytes */
          "M"   /* return error on memory exhausted */
          "c:"  /* max simultaneous connections */
          "k"   /* lock down all paged memory */
          "hi"  /* help, licence info */
          "r"   /* maximize core file limit */
          "v"   /* verbose */
          "d"   /* daemon mode */
          "l:"  /* interface to listen on */
          "u:"  /* user identity to run as */
          "P:"  /* save PID in file */
          "f:"  /* factor? */
          "n:"  /* minimum space allocated for key+value+flags */
          "t:"  /* threads */
          "D:"  /* prefix delimiter? */
          "L"   /* Large memory pages */
          "R:"  /* max requests per event */
          "C"   /* Disable use of CAS */
          "b:"  /* backlog queue limit */
          "B:"  /* Binding protocol */
          "I:"  /* Max item size */
          "S"   /* Sasl ON */
          "o:"  /* Extended generic options */
        ))) {
        switch (c) {
        case 'a':
            /* access for unix domain socket, as octal mask (like chmod)*/
            settings.access= strtol(optarg,NULL,8);
            break;

        case 'U':
            settings.udpport = atoi(optarg);
            udp_specified = true;
            break;
        case 'p':
            settings.port = atoi(optarg);
            tcp_specified = true;
            break;
        case 's':
            settings.socketpath = optarg;
            break;
        case 'm':
            settings.maxbytes = ((size_t)atoi(optarg)) * 1024 * 1024;
            break;
        case 'M':
            settings.evict_to_free = 0;
            break;
        case 'c':
            settings.maxconns = atoi(optarg);
            break;
        case 'h':
            usage();
            exit(EXIT_SUCCESS);
        case 'i':
            usage_license();
            exit(EXIT_SUCCESS);
        case 'k':
            lock_memory = true;
            break;
        case 'v':
            settings.verbose++;
            break;
        case 'l':
            if (settings.inter != NULL) {
                size_t len = strlen(settings.inter) + strlen(optarg) + 2;
                char *p = malloc(len);
                if (p == NULL) {
                    fprintf(stderr, "Failed to allocate memory\n");
                    return 1;
                }
                snprintf(p, len, "%s,%s", settings.inter, optarg);
                free(settings.inter);
                settings.inter = p;
            } else {
                settings.inter= strdup(optarg);
            }
            break;
        case 'd':
            do_daemonize = true;
            break;
        case 'r':
            maxcore = 1;
            break;
        case 'R':
            settings.reqs_per_event = atoi(optarg);
            if (settings.reqs_per_event == 0) {
                fprintf(stderr, "Number of requests per event must be greater than 0\n");
                return 1;
            }
            break;
        case 'u':
            username = optarg;
            break;
        case 'P':
            pid_file = optarg;
            break;
        case 'f':
            settings.factor = atof(optarg);
            if (settings.factor <= 1.0) {
                fprintf(stderr, "Factor must be greater than 1\n");
                return 1;
            }
            break;
        case 'n':
            settings.chunk_size = atoi(optarg);
            if (settings.chunk_size == 0) {
                fprintf(stderr, "Chunk size must be greater than 0\n");
                return 1;
            }
            break;
        case 't':
            settings.num_threads = atoi(optarg);
            if (settings.num_threads <= 0) {
                fprintf(stderr, "Number of threads must be greater than 0\n");
                return 1;
            }
            /* There're other problems when you get above 64 threads.
             * In the future we should portably detect # of cores for the
             * default.
             */
            if (settings.num_threads > 64) {
                fprintf(stderr, "WARNING: Setting a high number of worker"
                                "threads is not recommended.\n"
                                " Set this value to the number of cores in"
                                " your machine or less.\n");
            }
            break;
        case 'D':
            if (! optarg || ! optarg[0]) {
                fprintf(stderr, "No delimiter specified\n");
                return 1;
            }
            settings.prefix_delimiter = optarg[0];
            settings.detail_enabled = 1;
            break;
        case 'L' :
            if (enable_large_pages() == 0) {
                preallocate = true;
            } else {
                fprintf(stderr, "Cannot enable large pages on this system\n"
                    "(There is no Linux support as of this version)\n");
                return 1;
            }
            break;
        case 'C' :
            settings.use_cas = false;
            break;
        case 'b' :
            settings.backlog = atoi(optarg);
            break;
        case 'B':
            protocol_specified = true;
            if (strcmp(optarg, "auto") == 0) {
                settings.binding_protocol = negotiating_prot;
            } else if (strcmp(optarg, "binary") == 0) {
                settings.binding_protocol = binary_prot;
            } else if (strcmp(optarg, "ascii") == 0) {
                settings.binding_protocol = ascii_prot;
            } else {
                fprintf(stderr, "Invalid value for binding protocol: %s\n"
                        " -- should be one of auto, binary, or ascii\n", optarg);
                exit(EX_USAGE);
            }
            break;
        case 'I': // 设置可以存储的item 的最大值
            unit = optarg[strlen(optarg)-1];
            // 可以有单位
            if (unit == 'k' || unit == 'm' ||
                unit == 'K' || unit == 'M') {
                optarg[strlen(optarg)-1] = '\0';
                size_max = atoi(optarg);
                if (unit == 'k' || unit == 'K')
                    size_max *= 1024;
                if (unit == 'm' || unit == 'M')
                    size_max *= 1024 * 1024;
                settings.item_size_max = size_max;
            } else { // 可以没有单位
                settings.item_size_max = atoi(optarg);
            }
            if (settings.item_size_max < 1024) {  // 不允许小于1KB
                fprintf(stderr, "Item max size cannot be less than 1024 bytes.\n");
                return 1;
            }
            if (settings.item_size_max > 1024 * 1024 * 128) {  // 不允许大于128MB
                fprintf(stderr, "Cannot set item size limit higher than 128 mb.\n");
                return 1;
            }
            if (settings.item_size_max > 1024 * 1024) {  // 不建议大于1MB
                fprintf(stderr, "WARNING: Setting item max size above 1MB is not"
                    " recommended!\n"
                    " Raising this limit increases the minimum memory requirements\n"
                    " and will decrease your memory efficiency.\n"
                );
            }
            break;
        case 'S': /* set Sasl authentication to true. Default is false */
#ifndef ENABLE_SASL
            fprintf(stderr, "This server is not built with SASL support.\n");
            exit(EX_USAGE);
#endif
            settings.sasl = true;
            break;
        case 'o': /* It's sub-opts time! */
            subopts = optarg;

            while (*subopts != '\0') {
	            switch (getsubopt(&subopts, subopts_tokens, &subopts_value)) {
		            case MAXCONNS_FAST:
		                settings.maxconns_fast = true;
		                break;
		            case HASHPOWER_INIT:
		                if (subopts_value == NULL) {
		                    fprintf(stderr, "Missing numeric argument for hashpower\n");
		                    return 1;
		                }
		                settings.hashpower_init = atoi(subopts_value);
		                if (settings.hashpower_init < 12) {
		                    fprintf(stderr, "Initial hashtable multiplier of %d is too low\n",
		                        settings.hashpower_init);
		                    return 1;
		                } else if (settings.hashpower_init > 64) {
		                    fprintf(stderr, "Initial hashtable multiplier of %d is too high\n"
		                        "Choose a value based on \"STAT hash_power_level\" from a running instance\n",
		                        settings.hashpower_init);
		                    return 1;
		                }
		                break;
		            case SLAB_REASSIGN:
		                settings.slab_reassign = true;
		                break;
		            case SLAB_AUTOMOVE:
		                if (subopts_value == NULL) {
		                    settings.slab_automove = 1;
		                    break;
		                }
		                settings.slab_automove = atoi(subopts_value);
		                if (settings.slab_automove < 0 || settings.slab_automove > 2) {
		                    fprintf(stderr, "slab_automove must be between 0 and 2\n");
		                    return 1;
		                }
		                break;
		            default:
		                printf("Illegal suboption \"%s\"\n", subopts_value);
		                return 1;
		     }
            }
            break;
        default:
            fprintf(stderr, "Illegal argument \"%c\"\n", c);
            return 1;
        }
    }
其中用到的几个主要函数为:
#include <unistd.h>
int getopt(int argc, char * const argv[], const char *optstring);

- Parse command-line options
#include <stdlib.h>
int getsubopt(char **optionp, char * const *keylistp, char **valuep);

- parse suboption arguments from a string
#include <stdlib.h>
long int strtol(const char *nptr, char **endptr, int base);

- convert a string to a long integer
#include <stdlib.h>
int atoi(const char *nptr);

- convert a string to an integer
#include <stdlib.h>
double atof(const char *nptr);

- convert a string to a double
涉及到的全局变量为:
extern char *optarg;
extern int optind, opterr, optopt;
以上函数和变量的具体信息可以参考 linux man 手册。

解析模型:
#include <stdlib.h>
...
char *tokens[] = {"HOME", "PATH", "LOGNAME", (char *) NULL };
char *value;
int opt, index;

while ((opt = getopt(argc, argv, "e:")) != -1) {
  switch(opt)  {
  case 'e' :
	  while ((index = getsubopt(&optarg, tokens, &value)) != -1) {
		  switch(index) {
...
	  }
	  break;
...
  }
}
...
解析范例:
#include <stdio.h>
#include <stdlib.h>

int do_all;
const char *type;
int read_size;
int write_size;
int read_only;

enum
{
	RO_OPTION = 0,
	RW_OPTION,
	READ_SIZE_OPTION,
	WRITE_SIZE_OPTION
};

const char *mount_opts[] =
{
	[RO_OPTION] = "ro",
	[RW_OPTION] = "rw",
	[READ_SIZE_OPTION] = "rsize",
	[WRITE_SIZE_OPTION] = "wsize",
	NULL
};

int
main(int argc, char *argv[])
{
	char *subopts, *value;
	int opt;

	while ((opt = getopt(argc, argv, "at:o:")) != -1)
		switch(opt)
		{
			case 'a':
				do_all = 1;
				break;
			case 't':
				type = optarg;
				break;
			case 'o':
				subopts = optarg;
				while (*subopts != '\0')
					switch(getsubopt(&subopts, mount_opts, &value))
					{
						case RO_OPTION:
							read_only = 1;
							break;
						case RW_OPTION:
							read_only = 0;
							break;
						case READ_SIZE_OPTION:
							if (value == NULL)
								abort();
							read_size = atoi(value);
							break;
						case WRITE_SIZE_OPTION:
							if (value == NULL)
								abort();
							write_size = atoi(value);
							break;
						default:
							/* Unknown suboption. */
							printf("Unknown suboption '%s'\n", value);
							break;
					}
				break;
			default:
				abort();
		}

	/* Do the real work. */

	return 0;
}

值得留意的点:
1.函数 getopt() 的参数  optstring 中,某一个选项后一个冒号和两个冒号的含义;
2.全局变量 optarg 为 char * 类型,用于指向当前选项的参数(如果有),当选项非法时, optarg 的值为“?”
3.函数  strtol() 和 atoi() 的相同点和不同点;
4.函数  getsubopt() 的用法。






转载于:https://my.oschina.net/moooofly/blog/119283

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值