- PA0是配环境和获取源码,教你如何使用一些简单的linux命令和工具,也非常重要,但是本文不赘述。
 
  复制代码 
拿到一个工程首先就是要读源码,比如从main函数一步一步看工程到底在干什么事情。(nemu不是从main开始的,但是不知道这个不耽误你理解nemu,这个在pa3.1有涉及) 
那我这里顺便带着大伙读一读nemu源码。 
nemu-main.c- #include <common.h>
 - void init_monitor(int, char *[]);
 - void am_init_monitor();
 - void engine_start();
 - int is_exit_status_bad();
 - int main(int argc, char *argv[]) {
 -   /* Initialize the monitor. */
 - #ifdef CONFIG_TARGET_AM
 -   am_init_monitor();
 - #else
 -   init_monitor(argc, argv);
 - #endif
 -   /* Start engine. */
 -   engine_start();
 -   return is_exit_status_bad();
 - }
 
  复制代码 #ifdef CONFIG_TARGET_AM这个东西是当你在am启动nemu的时候会定义的一个宏,理解nemu操作不需要这个,所以可以省略,所以一开始进入main之后就到了init_monitor(argc, argv)的函数了。- void init_monitor(int argc, char *argv[]) {
 -   /* Perform some global initialization. */
 -   /* Parse arguments.通过getopt_long传进来的参数决定后面的行为 */
 -   parse_args(argc, argv);
 -   /* parse elf file*/
 -   //printf("%s!!",elf_file);
 -   #ifdef CONFIG_FTRACE
 -                 parse_elf(elf_file);
 -   #endif
 -  // parse_elf(elf_file);
 -   /* Set random seed. */
 -   init_rand();
 -   /* Open the log file. */
 -   init_log(log_file);
 -   /* Initialize memory. */
 -   init_mem();
 -   /* Initialize devices. */
 -   IFDEF(CONFIG_DEVICE, init_device());//如果定义了device,那就初始化device,晚点看。
 -   /* Perform ISA dependent initialization. */
 -   init_isa();
 -   /* Load the image to memory. This will overwrite the built-in image. */
 -   long img_size = load_img();
 -   /* Initialize differential testing. */
 -   init_difftest(diff_so_file, img_size, difftest_port);
 -   // printf("diff_so_file = %s\n",diff_so_file);
 -   // printf("img_size = %ld\n",img_size);
 -   /* Initialize the simple debugger. */
 -   init_sdb();
 -   IFDEF(CONFIG_ITRACE, init_disasm());
 -   /*parse ftrace*/
 -   /* Display welcome message. */
 -   welcome();
 - }
 
  复制代码 就是一些初始化的,比如传参处理,加载日志文件,内存,isa,ftrace处理,启动处理等等。 
首先我们来看一下parse_args(argc, argv)函数。- static int parse_args(int argc, char *argv[]) {
 -   const struct option table[] = {
 -     {"batch"    , no_argument      , NULL, 'b'},
 -     {"log"      , required_argument, NULL, 'l'},
 -     {"diff"     , required_argument, NULL, 'd'},
 -     {"port"     , required_argument, NULL, 'p'},
 -     {"ftrace"   , required_argument, NULL, 'f'},
 -     {"help"     , no_argument      , NULL, 'h'},
 -     {0          , 0                , NULL,  0 },
 -   };
 -   int o;
 -   while ( (o = getopt_long(argc, argv, "-bhl:d:p:f:e:", table, NULL)) != -1) {
 -     switch (o) {
 -       case 'b': sdb_set_batch_mode(); break;
 -       case 'p': sscanf(optarg, "%d", &difftest_port); break;
 -       case 'l': log_file = optarg; break;
 -       case 'f': elf_file = optarg; break;
 -       case 'd': diff_so_file = optarg; break;
 -       case 1: img_file = optarg; return 0;
 -       default:
 -         printf("Usage: %s [OPTION...] IMAGE [args]\n\n", argv[0]);
 -         printf("\t-b,--batch              run with batch mode\n");
 -         printf("\t-l,--log=FILE           output log to FILE\n");
 -         printf("\t-f,--ftrace=ELF_FILE    ftrace ELF to log\n");
 -         printf("\t-d,--diff=REF_SO        run DiffTest with reference REF_SO\n");
 -         printf("\t-p,--port=PORT          run DiffTest with port PORT\n");
 -         printf("\n");
 -         exit(0);
 -     }
 -   }
 -   return 0;
 - }
 
  复制代码 首先我们看到一个getopt_long,看到不认识的函数首先懒得查的话那就网上随便搜一下,或者是man getopt_long,你就能看到他给了一个example。- #include <getopt.h>
 -        #include <stdio.h>     /* for printf */
 -        #include <stdlib.h>    /* for exit */
 -        int main(int argc, char *argv[])
 -        {
 -            int c;
 -            int digit_optind = 0;
 -            while (1) {
 -                int this_option_optind = optind ? optind : 1;
 -                int option_index = 0;
 -                static struct option long_options[] = {
 -                    {"add",     required_argument, 0,  0 },
 -                    {"append",  no_argument,       0,  0 },
 -                    {"delete",  required_argument, 0,  0 },
 -                    {"verbose", no_argument,       0,  0 },
 -                    {"create",  required_argument, 0, 'c'},
 -                    {"file",    required_argument, 0,  0 },
 -                    {0,         0,                 0,  0 }
 -                };
 -           
 -                c = getopt_long(argc, argv, "abc:d:012",
 -                                long_options, &option_index);
 -                if (c == -1)
 -                    break;
 -                switch (c) {
 -                case 0:
 -                    printf("option %s", long_options[option_index].name);
 -                    if (optarg)
 -                        printf(" with arg %s", optarg);
 -                    printf("\n");
 -                    break;
 -                case '0':
 -                case '1':
 -                case '2':
 -                    if (digit_optind != 0 && digit_optind != this_option_optind)
 -                      printf("digits occur in two different argv-elements.\n");
 -                    digit_optind = this_option_optind;
 -                    printf("option %c\n", c);
 -                    break;
 -                case 'a':
 -                    printf("option a\n");
 -                    break;
 -                case 'b':
 -                    printf("option b\n");
 -                    break;
 -                case 'c':
 -                    printf("option c with value '%s'\n", optarg);
 -                    break;
 -                case 'd':
 -                    printf("option d with value '%s'\n", optarg);
 -                    break;
 -                case '?':
 -                    break;
 -                default:
 -                    printf("?? getopt returned character code 0%o ??\n", c);
 -                }
 -            }
 -            if (optind < argc) {
 -                printf("non-option ARGV-elements: ");
 -                while (optind < argc)
 -                    printf("%s ", argv[optind++]);
 -                printf("\n");
 -            }
 -            exit(EXIT_SUCCESS);
 -        }
 
  复制代码 长得可以非常相似,所以挺好理解的。 getopt_long() 是用来处理命令行参数的,不过规模大一点的程序一般第一步就是处理命令行参数,类似的函数是getopt,前者是用来处理长命令,后者是用于处理短命令。 
比如getopt用来处理命令行输入的 -l,getopt_long() 用来处理命令行输入的--log,因此你读懂makefile之后就可以在对应的位置加上参数以达到任务了。 
注意 getopt_long 的第三个参数是 -bhl:d:p:f:e:,开头的 - 字符很关键, 
当遇到非选项参数(即不以 - 开头的参数)时,返回 1,然后不再解析参数,因为后续已经return 0了。 
当遇到定义的选项(如-b  -l)那就正常返回选项字符。 
parse_elf 是后续C阶段的内容,这里可以先忽略,解析elf文件。 
init_rand(); 生成随机数种子。 
init_mem() 初始化内存空间。 
init_device() 初始化设备,注册mmio和初始化键盘等各种外设。 
init_difftest 初始化difftest,讲img文件传输给spike。 
init_sdb() 初始化sdb(simple debug)。 
init_disasm() 根据有无ITRACE选项决定是否将你的二进制文件反汇编回去,方便你看。 
welcome() 输出一些基本信息,是否开启trace,建立时间,客户端信息等等。 
此时init_monitor结束,开始engine_start了,然后进入到里面发现除了之前那个CONFIG_TARGET_AM,这个我们不会进行里面的函数,那么就只剩下sdb_mainloop了。 
那就到了我们nemu的第一个必做题了,欲知后事如何,请听下回分解。 
 
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除 
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |