GRUB(五)stage2.c注释 您所在的位置:网站首页 找不到grub GRUB(五)stage2.c注释

GRUB(五)stage2.c注释

2022-12-27 20:20| 来源: 网络整理| 查看: 265

接上一篇,最后调用的函数是cmain(),这个函数在stage2.c中定义。

在cmain()中主要做以下事情:

寻找启动菜单配置文件。 如找不到则尝试寻找程序中的内置菜单。 如果找到则尝试解析并执行该启动菜单配置中的脚本。 如两者都没有找到,则进入命令行模式。

主要函数如下

//void //reset (void); static int config_len, menu_len, num_entries; /* menu_entries数组存放"title"命令的内容 config_entries数组既存放在menu中的命令也存放title下的命令,它们之间以两个NULL分割 如: timeout xxx(NULL)default /default(NULL)(NULL) [title 0]命令1(NULL)命令2(NULL)(NULL) [title 1]命令1(NULL)命令2(NULL)(NULL) */ static char *config_entries, *menu_entries, *cur_entry; static void reset(void) { count_lines = -1; config_len = 0; menu_len = 0; num_entries = 0; config_entries = (char *)mbi.drives_addr + mbi.drives_length; cur_entry = config_entries; menu_entries = (char *)MENU_BUF; init_config(); } /* 从cmdline中找到下一个词语并返回它的指针,如果参数after_equal 不等于零, 则字符"="被假设为一个空格看待. 注意:此假设是为了向后兼容。*/ extern char * skip_to(int after_equal, char *cmdline); /* This is the starting function in C. */ void cmain(void) { char *kill_buf = (char *)KILL_BUF; /* #define KILL_BUF (CMDLINE_BUF + CMDLINE_BUFLEN) */ /* grub_setjmp见setjmp.S,这个函数保存栈的内容到restart_env以便将来重启Stage 2的时候用到。 */ grub_setjmp(restart_env); /* Initialize the kill buffer. */ *kill_buf = 0; debug = 1; /* Never return. */ for (;;) { extern int use_config_file; int is_opened, is_preset; reset(); /* Here load the configuration file. */ //#ifdef GRUB_UTIL /* 如果启用了配置文件则加载它(在启动过程中如果按下了C键会跳过配置文件的加载进入命令行模式,详见asm.S)*/ if (use_config_file) //#endif /* GRUB_UTIL */ { /* 存放默认文件名"/boot/grub/default"的地方. #define DEFAULT_FILE_BUF (PASSWORD_BUF + PASSWORD_BUFLEN) */ char *default_file = (char *)DEFAULT_FILE_BUF; int i; /* Get a saved default entry if possible. */ saved_entryno = 0; /* 当config_file不为0时将读取该处的字符串,否则读取0x0800处。见asm.S*/ if (*config_file) { *default_file = 0; /* initialise default_file */ grub_strncat(default_file, config_file, DEFAULT_FILE_BUFLEN); /* 截取字符串,丢弃最后一个'/'之后的内容*/ for (i = grub_strlen(default_file); i >= 0; i--) if (default_file[i] == '/') { //i++; break; } default_file[++i] = 0; /* 然后 + "default" */ grub_strncat(default_file + i, "default", DEFAULT_FILE_BUFLEN - i); if (debug) grub_printf("Open %s ... ", default_file); // i=grub_open (default_file); // printf("default_file ok=%s\n", default_file); // for (;;); /* 打开文件读取十个字节,然后将之从字符串转为数字,保存到saved_entryno。 */ if (grub_open(default_file)) /* grub_open见disk_io.c */ { char buf[10]; /* This is good enough. */ char *p = buf; int len; if (debug) grub_printf("Read file: ", default_file); len = grub_read(buf, sizeof(buf)); if (debug) grub_printf("len=%d\n", len); if (len > 0) { buf[sizeof(buf) - 1] = 0; safe_parse_maxint(&p, &saved_entryno); /*见char_io.c*/ } grub_close(); } else if (debug) grub_printf("failure.\n", default_file); } errnum = ERR_NONE; #ifdef GRUB_UTIL do #endif /* GRUB_UTIL */ { /* STATE 0: 在title命令前. STATE 1: 在一个title中. STATE >1: 在一个title命令后的entry中. */ int state = 0, prev_config_len = 0, prev_menu_len = 0; char *cmdline; is_preset = is_opened = 0; /* 首先尝试获取位于0x800处的预置命令行菜单. */ if (preset_menu == (char *)0x0800) { is_opened = is_preset = open_preset_menu(); } /* 再尝试打开config_file指定的文件 */ if (!is_opened) { /* Try config_file */ if (*config_file) is_opened = grub_open(config_file); } errnum = ERR_NONE; if (!is_opened) { /* Try the preset menu. This will succeed at most once, * because close_preset_menu disables the preset menu. */ is_opened = is_preset = open_preset_menu(); } /* 找不到预置菜单,也打不开config_file指定的文件,结束程序。*/ if (!is_opened) break; /* 重置命令行环境,这是必须的因为菜单一定被覆盖了。 */ reset(); /* 读配置文件或者读预置菜单(已加载到内存),并逐个解释 */ cmdline = (char *)CMDLINE_BUF; /* get_line_from_config函数读一行到cmdline指向的缓冲区,忽略#开头的注释,并自动连接用反斜杠"\"连接的两行。 */ while (get_line_from_config(cmdline, NEW_HEAPSIZE, !is_preset)) { /* (1) 关于builtin结构 // builtin的标志位 #define BUILTIN_CMDLINE 0x1 /* 可在命令行中运行. #define BUILTIN_MENU 0x2 /* 可在菜单中运行. #define BUILTIN_TITLE 0x4 /* 仅用于title命令. #define BUILTIN_SCRIPT 0x8 /* 可在脚本中运行. #define BUILTIN_NO_ECHO 0x10 /* 启动过程中不回显命令 #define BUILTIN_HELP_LIST 0x20 /* Show help in listing. // The table for a builtin. struct builtin { char *name; // 命令名. int(*func) (char *, int); // 回调函数 int flags; //上列标志位的组合. char *short_doc; // 文档的简短版本. char *long_doc; // 文档的长版。 }; // (2) 以下是一个menu.lst的示例 # utility under DOS/Win9x or Linux. 以#开头的是注释 color black/cyan yellow/cyan func) (arg, BUILTIN_MENU); errnum = 0; #endif char *ptr = cmdline; /* 拷贝这条在menu下中运行的命令到config_entries数组(包括NULL)。 */ while ((config_entries[config_len++] = *ptr++) != 0) ; prev_config_len = config_len; } else /* Ignored. */ continue; } else //state != 0 && 0 == builtin->flags & BUILTIN_TITLE { char *ptr = cmdline; /* state == 1 意味着这条命令是第一个TITLE之下的命令, 而 num_entries == 0则意味着TITLE是也是第一个(再遇到title命令时num_entries递增). */ if (num_entries == 0 && state == 1) { /* 往config_entries追加一个零以表示上一组命令到此结束 */ config_entries[config_len++] = 0; } state++; /* 拷贝这行title下的命令到config_entries数组中,包括结尾的NULL. */ while ((config_entries[config_len++] = *ptr++) != 0) ; } } /* End while(get_line_from_config)*/ /* 这里必须关闭文件,因为menu相关的命令也可能会使用GRUB_OPEN命令 */ if (is_preset) close_preset_menu(); else grub_close(); /* 为config_entries列表和menu_entries列表追加结束标记0表示一块的结束. */ if (state > 1) { num_entries++; config_entries[config_len++] = 0; } else// if (state) { menu_len = prev_menu_len; config_len = prev_config_len; } //else ///* Finish the menu-specific commands. */ //config_entries[config_len++] = 0; menu_entries[menu_len++] = 0; config_entries[config_len++] = 0; /* 将menu_entries续接到config_entries之后,并重置menu_entries指针的值*/ grub_memmove(config_entries + config_len, menu_entries, menu_len); menu_entries = config_entries + config_len; /* 首先运行menu中可执行的命令 */ ///* Run an entry from the script SCRIPT. HEAP is used for the // command-line buffer. If an error occurs, return non-zero, otherwise // return zero. */ //int //run_script (char *script, char *heap) { char *old_entry; /* heap缓冲区紧跟在menu_entries之后 */ char *heap = menu_entries + menu_len; #if 1 /* 初始化 */ extern void init_cmdline(void); init_cmdline(); #else /* 下面这段被预编译命令禁用了。 */ saved_drive = boot_drive; // 启动磁盘(启动设备) saved_partition = install_partition; // 启动分区 current_drive = GRUB_INVALID_DRIVE; errnum = 0; count_lines = -1; /* Restore memory probe state. */ mbi.mem_upper = saved_mem_upper; if (mbi.mmap_length) mbi.flags |= MB_INFO_MEM_MAP; /* 为builtin命令初始化数据. */ init_builtins(); #endif // 下面这个循环将逐条执行config_entries数组中所有可在menu中执行的命令。 while (1) { struct builtin *builtin; char *arg; // print_error (); // // if (errnum) // { // errnum = ERR_NONE; // // /* If a fallback entry is defined, don't prompt a user's // intervention. */ // if (fallback_entryno < 0) // { // grub_printf ("\nPress any key to continue..."); // (void) getkey (); // } // // break;//return 1; // } /* 从cur_entry拷贝第一条字符串到HEAP。 注:在reset()函数中设定cur_entry = config_entries; */ old_entry = cur_entry; while (*cur_entry++) ; grub_memmove(heap, old_entry, (int)cur_entry - (int)old_entry); //printf("errnum=%d, heap=%s, old_entry=%s, cur_entry=%s\n", errnum, heap, old_entry, cur_entry); /*读到了两个零, 在config_entries中前面几条总是menu的命令且每条以0分割, 再其后又追加一个0表示menu所有命令的结束。。*/ if (!*heap) { /* 已经执行完了所有的menu命令. */ /* 如果内核尚未加载则退出执行menu命令的循环。 */ if (kernel_type == KERNEL_TYPE_NONE) break;//return 0; /* 否则执行boot命令 */ grub_memmove(heap, "boot", 5); } /* 命令行转builtin. */ builtin = find_command(heap); if (!builtin) { grub_printf("%s\n", old_entry); continue; } if (!(builtin->flags & BUILTIN_NO_ECHO)) grub_printf("%s\n", old_entry); /* 如果builtin不能在menu中执行则跳过它 */ if (!(builtin->flags & BUILTIN_MENU)) { // errnum = ERR_UNRECOGNIZED; continue; } #if 0 /* Invalidate the cache, because the user may exchange removable disks. */ buf_drive = -1; #endif /* 执行这条builtin对应的回调函数 */ extern int commandline_func(char *arg1, int flags); arg = (builtin->func) == commandline_func ? heap : skip_to(1, heap); (builtin->func) (arg, BUILTIN_MENU); //printf ("OK *old_entry=%x, %s\n", *old_entry, heap); //for(i=0;i= 0) { for (i = 0; i < MAX_FALLBACK_ENTRIES; i++) { if (fallback_entries[i] < 0) break; if (fallback_entries[i] >= num_entries) { grub_memmove(fallback_entries + i, fallback_entries + i + 1, ((MAX_FALLBACK_ENTRIES - i - 1) * sizeof(int))); i--; } } if (fallback_entries[0] < 0) fallback_entryno = -1; } /* 检查默认选项是否存在 */ if (default_entry >= num_entries) { // 如不存在则在“回退”入口存在时将默认设置为设置为“回退” if (fallback_entryno >= 0) { default_entry = fallback_entries[0]; fallback_entryno++; if (fallback_entryno >= MAX_FALLBACK_ENTRIES || fallback_entries[fallback_entryno] < 0) fallback_entryno = -1; } else default_entry = 0; /* 如均不存在则设为0 */ } } #ifdef GRUB_UTIL while (is_preset); #endif /* GRUB_UTIL */ } /*End if (use_config_file), 如果没有配置文件则会直接进入下一行*/ debug = 0; /* 继续并保证终端已建立 */ if (current_term->startup) (*current_term->startup)(); if (!num_entries) { /* 如果没有可执行的配置文件,那么进入命令行模式 */ enter_cmdline(config_entries, 1); } else { /* 执行menu接口 */ run_menu(menu_entries, cur_entry, num_entries, menu_entries + menu_len, default_entry); } } //END for (;;) }

 

在cmain的最后,如果没有菜单或者菜单是无效的,则调用enter_cmdline进入命令行模式,否则会调用run_menu()函数打印前面中解析出来的各个菜单项,如果设置了倒计时还会显示倒计时信息。也就是以下内容:

Use the ↑ and ↓ keys to select which entry is highlighted.

Press enter to boot the selected OS, \'e\' to edit the commands before booting, or \'c\' for a command-line.

启动菜单项1

启动菜单项2

……

启动菜单项N

The highlighted entry will be booted automatically in %d seconds.

run_menu除了显示这些信息外,还响应用户输入,例如选择菜单项,编辑菜单项以及进入命令行模式等。如果用户在选择的菜单项上敲下回车键,这个函数会定位到这个菜单项下的命令列表(脚本列表)然后调用run_script逐个执行。run_script很简单,它只是从命令行清单中逐个执行该命令对应函数,注意在旧版grub4dos 0.4.2中这个函数放在cmdline.c且不支持&&和||逻辑操作,所以只能一行一个命令,而在0.4.5中放在了stage2.c,通过对逻辑与和逻辑或的支持,在新版中就可以一行脚本中执行多条命令。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有