目标文件:mjpg-stream/mjpg-stream.c
这一篇的主要难点是get_long(),get_long_only().
新手写,有不对的请大神指正,鼓励。
本人参考文章:
http://www.360doc.com/content/13/0913/13/13876325_314174121.shtml
http://blog.sina.com.cn/s/blog_a31ff26901013l22.html
---------------------------------------------------------------------------------------------------------------------------
一:直接对主函数main分析。遇到具体情况,具体往下延伸。
//char *input = "input_uvc.so --resolution 640x480 --fps 5 --device /dev/video0";
char *input[MAX_INPUT_PLUGINS];/* 这里MAX_INPUT_PLUGINS在.h文件中定为10 */
char *output[MAX_OUTPUT_PLUGINS]; /* 一个输入最大可以对应10个输出 */
int daemon = 0, i;/* 守护进程变量,守护进程作为重点,下一篇详述 */
size_t tmp = 0;
output[0] = "output_http.so --port 8080";/* 默认情况下将video0作为输入,http的8080端口作为输出 */
global.outcnt = 0;/* 代码中 参数行操作的计数 */
---------------------------------------------------------------------------------------------------------------------------
二:下面是一个while()循环,来解析main()函数后面所带的参数
/* parameter parsing */
while(1) {
int option_index = 0, c=0;
static struct option long_options[] = \ /* 长选项表,进行长选项的比对 */
{
{"h", no_argument, 0, 0}, /* 第一个参数为选项名,前面没有短横线。譬如"help"、"verbose"之类 */
{"help", no_argument, 0, 0}, /* 第二个参数描述了选项是否有选项参数 |no_argument 0 选项没有参数|required_argument 1 选项需要参数|optional_argument 2 选项参数可选|*/
{"i", required_argument, 0, 0}, /* 第三个参数指明长选项如何返回,如果flag为NULL,则getopt_long返回val。
{"input", required_argument, 0, 0}, * 否则返回0,flag指向一个值为val的变量。如果该长选项没有发现,flag保持不变.
{"o", required_argument, 0, 0}, */
{"output", required_argument, 0, 0}, /* 第四个参数是发现了长选项时的返回值,或者flag不是NULL时载入*flag中的值 */
{"v", no_argument, 0, 0},
{"version", no_argument, 0, 0},
{"b", no_argument, 0, 0}, /* 每个长选项在长选项表中都有一个单独条目,该条目里需要填入正确的数值。数组中最后的元素的值应该全是0。
{"background", no_argument, 0, 0}, *数组不需要排序,getopt_long()会进行线性搜索。但是,根据长名字来排序会使程序员读起来更容易.
{0, 0, 0, 0} */
};
---------------------------------------------------------------------------------------------------------------------------
三:接下来是getopt_long_only函数,重点分析
---------------------------------------------------------------------------------------------------------------------------
c = getopt_long_only(argc, argv, "", long_options, &option_index);
由于和后面的代码有关,故一起分析。
---------------------------------------------------------------------------------------------------------------------------
/* no more options to parse */
if (c == -1) break;
/* unrecognized option */
if(c=='?'){ help(argv[0]); return 0; }
switch (option_index) {
/* h, help */
case 0:
case 1:
help(argv[0]);
return 0;
break;
/* i, input */
case 2:
case 3:
input = strdup(optarg);
break;
/* o, output */
case 4:
case 5:
output[global.outcnt++] = strdup(optarg);
break;
/* v, version */
case 6:
case 7:
printf("MJPG Streamer Version: %s\n" \
"Compilation Date.....: %s\n" \
"Compilation Time.....: %s\n", SOURCE_VERSION, __DATE__, __TIME__);
return 0;
break;
/* b, background */
case 8:
case 9:
daemon=1;
break;
default:
help(argv[0]);
return 0;
}
}
---------------------------------------------------------------------------------------------------------------------------
代码里出现了help自定义函数,一个switch选择结构,c为flag作为判断标准,同时c也是getopt_long_only的返回值。
strdup函数,变量optarg,变量daemon。
3.1 get_long_only函数:
#include <getopt.h>
int getopt_long_only(int argc, char * const argv[],
const char *optstring, const struct option *longopts, int *longindex);
说明:函数中的参数说明
~1+2 argc和argv通常直接从main()到两个参数传递而来
~3 optsting是选项参数组成的字符串,如果该字符串里任一字母后有冒号,那么这个选项就要求有参数
~4 下一个参数是指向数组的指针,这个数组是option结构数组,option结构称为长选项表,
其声明如下:
struct option
{
const char *name;
int has_arg;
int *flag;
int val;
};
_1 const char *name:选项名,前面没有短横线。譬如"help"、"verbose"之类。 _2 int has_arg:描述长选项是否有选项参数,如果有,是哪种类型的参数,其值见下表:
----------------------------------------------------
符号常量
no_argument
required_argument
optional_argument
----------------------------------------------------
_3 int *flag:
如果该指针为NULL,那么getopt_long返回val字段的值;
如果该指针不为NULL,那么会使得它所指向的结构填入val字段的值,同时getopt_long返回0
_4 int val:
如果flag是NULL,那么val通常是个字符常量,如果短选项和长选项一致,那么该字符就应该与optstring中出现的这个选项的参数相同;
对于options类型参数可以有两种方式:
1)短选项(short options):顾名思义,就是短小参数。它们通常包含一个连字号和一个字母(大写或小写字母)。例如:-s,-h等。
2)长选项(long options):长选项,包含了两个连字号和一些大小写字母组成的单词。例如,--size,--help等。
记住几种常见返回值:
(a)每次调用该函数,它都会分析一个选项,并且返回它的短选项,如果分析完毕,即已经没有选项了,则会返回-1。
(b)如果getopt_long_only或者getopt_long在分析选项时,遇到一个没有定义过的选项,则返回值为‘?’,此时,程序员可以打印出所定义命令行的使用信息给用户。
(c)当处理一个带参数的选项时,全局变量optarg会指向它的参数
(d)当函数分析完所有参数时,全局变量optind(into argv)会指向第一个‘非选项’的位置
讲了这么多。还是抽象。上实例解析getopt:
(getopt_long和getopt_long_only类似,以getlong讲解,参数是一样的)
实例1:
#include <stdio.h>
#include <getopt.h>
char *l_opt_arg;
char* const short_options = "nbl:";
struct option long_options[] = {
{ "name",
{ "bf_name",
{ "love",
{
};
int main(int argc, char *argv[])
{
int c;
while((c = getopt_long (argc, argv, short_options, long_options, NULL)) != -1)
{
switch (c)
{
case 'n':
printf("My name is XL.\n");
break;
case 'b':
printf("His name is ST.\n");
break;
case 'l':
l_opt_arg = optarg;
printf("Our love is %s!\n", l_opt_arg);
break;
}
}
return 0;
}
编译并运行:
[root@localhost liuxltest]# gcc -o getopt getopt.c
[root@localhost liuxltest]# ./getopt -n -b -l forever
My name is XL.
His name is ST.
Our love is forever!
[root@localhost liuxltest]#
[root@localhost liuxltest]# ./getopt -nb -l forever
My name is XL.
His name is ST.
Our love is forever!
[root@localhost liuxltest]# ./getopt -nbl forever
My name is XL.
His name is ST.
Our love is forever!
编译结果分析:
char* const short_options = "nbl:";这一句,表示nb不需要参数,l需要参数
无冒号不要参数,一个冒号需要参数,两个冒号参数可选.
所以forever是-l命令的参数.故需要char *l_opt_arg;
如我有一个程序,有两个选项,-a, -b, 我输入的时候是
这就解释了-nbl的输入
现在对于处理命令行参数的操作是不是明朗一点了。
再看个例子
实例2:
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
char *l_opt_arg;
const char* const short_options = "myl:";
struct option long_options[] = {
};
int main(int argc, char *argv[])
{
}
注意,此程序可接收的的选项有三个, 一个是m ,不带参数, y 不带参数, l
那如果-m 不带参数,如果我写了参数,会怎么样呢?下面看测试
第一种:
$ ./a.out -m -y
before:
arg:1 : -m
arg:2 : -y
My name is A.
His name is B.
optind:3
after:
arg:1 : -m
arg:2 : -y
第二种:
$ ./a.out -m -y -l banana
before:
arg:1 : -m
arg:2 : -y
arg:3 : -l
arg:4 : banana
My name is A.
His name is B.
Our love is banana!
optind:5
after:
arg:1 : -m
arg:2 : -y
arg:3 : -l
arg:4 : banana
第三种
$./a.out -m lisi -y zhangsan
before:
arg:1 : -m
arg:2 : lisi
arg:3 : -y
arg:4 : zhangsan
arg:5 : -l
arg:6 : banana
arg:7 : aaa
My name is A.
His name is B.
Our love is banana!
optind:5
after:
arg:1 : -m
arg:2 : -y
arg:3 : -l
arg:4 : banana
arg:5 : lisi
arg:6 : zhangsan
arg:7 : aaa
第三种里:注意 argv 里面值的顺序已经和原来不一样了,对命令行的参数重新组织了一下顺序,也就是不认识的命令行参数,都放在了argv的最后,其中 optind 指向了这些没有被解释的参数的第一个。
optind有作用吧!因为5,6,7不通过解析。
附:如果是长参数,则使用 --, 如 --help, 因为 -help时,(选项不需要参数的情况) 会把它当成 四个选项, -h -e -l -p. 所以使用长参数时,要用两个 横线 --。
小结末尾。提一下optind,optarg,opterr三个参数.
optind:每当解析完一个argv,optind就会递增
optarg:如果选项带参数,参数保存在optarg中。如果选项带可选参数,而实际无参数时,optarg为NULL
opterr:当解析错误时,如果opterr为1则自动打印一条错误消息(默认),否则不打印。
这里是getopt.h中的注释。有兴趣的自己翻译翻译哦.
/* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns EOF, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int optind; /* Callers store zero here to inhibit the error message `getopt' prints for unrecognized options. */ extern int opterr;
3.2 Help函数
就在这个c文件里.代码就不全贴了。就只贴一部分
---------------------------------------------------------------------------------------------------------------------------
fprintf(stderr, "Usage: %s\n" \
" -i | --input \"<input-plugin.so> [parameters]\"\n" \
" -o | --output \"<output-plugin.so> [parameters]\"\n" \
" [-h | --help ]........: display this help\n" \
" [-v | --version ].....: display version information\n" \
" [-b | --background]...: fork to the background, daemon mode\n", progname);
---------------------------------------------------------------------------------------------------------------------------
大家应该很熟悉的格式,就是Linux的help结构 一个命令的延伸,如ls -l ,ls -c,ls -u等等。
总之,他就是一个Switch帮助函数。
3.3 strdup函数
/* i, input */
case 2:
case 3:
input = strdup(optarg);
break;
没见过strdup这个函数。故度娘之~
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
extern char *strdup(char *s);
头文件:string.h
功 能: 将串拷贝到新建的位置处
strdup()在内部调用了malloc()为变量分配内存,不需要使用返回的字符串时,需要用free()释放相应的内存空间,否则会造成内存泄漏。
返回一个指针,指向为复制字符串分配的空间;如果分配空间失败,则返回NULL值。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
既然提到malloc,那就比较一下,而且都是和free()搭配使用的。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
extern void *malloc(unsigned int num_bytes);
如果分配成功则返回指向被分配内存的指针(此存储区中的初始值不确定),否则返回空指针NULL.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
可以看到区别,前者返回字符串,后者返回的是一个空指针,故malloc范围比strdup大。同时malloc中运用类型强制转换
也是家常便饭。
OK,继续下一段代码解析。
四:系统记录相关
--------------------------------------------------------------------------------------------------------------------
openlog("MJPG-streamer ", LOG_PID | LOG_CONS, LOG_USER);
//openlog("MJPG-streamer ", LOG_PID|LOG_CONS|LOG_PERROR, LOG_USER);
syslog(LOG_INFO, "starting application");
--------------------------------------------------------------------------------------------------------------------
2次度娘后。
--------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------
更多可能参数值,度娘~
由于接下来的代码和守护进程有关,而本人又不熟悉。故在下一篇中详细学习以及总结。
转自:http://blog.csdn.net/s419101357/article/details/11827699