本文主要是以 memcached 源码为例,讲解如何在
linux 下解析命令行参数。
安装 memcached 后,查看其可用选项:
解析模型:
值得留意的点:
1.函数 getopt() 的参数 optstring 中,某一个选项后一个冒号和两个冒号的含义;
2.全局变量 optarg 为 char * 类型,用于指向当前选项的参数(如果有),当选项非法时, optarg 的值为“?” ;
3.函数 strtol() 和 atoi() 的相同点和不同点;
4.函数 getsubopt() 的用法。
安装 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() 的用法。
来源:oschina
链接:https://my.oschina.net/u/617889/blog/119283