歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> memcached命令行參數解析

memcached命令行參數解析

日期:2017/2/27 16:00:47   编辑:Linux教程
文主要是以 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.

那麼,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() 的用法
Copyright © Linux教程網 All Rights Reserved