内核启动分析(一)——u-boot启动内核

 

       我们可以看到在,start_armboot()函数的最后,在一个无限循环中调用了函数main_loop(),该函数在common/main.c文件中被定义,我们可以看到下面的一段代码:

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    s = getenv ("bootdelay");   //得到环境变量中bootdelay
    bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

    debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);

  如果定义了CONFIG_BOOTDELAY,则在没有CONFIG_BOOTDELAY秒中,串口上没有输入,则会进行自动的引导Linux内核。也就是执行bootcmd命令, run_command(s,0)

#ifdef CONFIG_BOOTCOUNT_LIMIT //启动次数的限制功能,如果到达一定次数,将不能启动u-boot.
    if (bootlimit && (bootcount > bootlimit)) {//检查是否超出启动次数限制
        printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
         (unsigned)bootlimit);
        s = getenv ("altbootcmd");//启动延时
    }
    else
#endif /* CONFIG_BOOTCOUNT_LIMIT */
        s = getenv ("bootcmd");// 获得启动参数

    debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "");
// 这里如果bootdelay大于0,并且中间没有被中断的话,执行命令行参数
    if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
# ifdef CONFIG_AUTOBOOT_KEYED
  int prev = disable_ctrlc(1); /* disable Control C checking */
# endif
# ifndef CONFIG_SYS_HUSH_PARSER
  run_command (s, 0); //运行启动的命令行,例如 可以使用tftp命令
# else
  parse_string_outer(s, FLAG_PARSE_SEMICOLON |
        FLAG_EXIT_FROM_LOOP);
# endif

   到这里我们就可以看到是怎么调用设置的命令行参数的在这里还要使用到bootm命令,先来看看bootm命令的实现,在common/cmd_bootm.c
   可以看出如果定义了CONFIG_BOOTM_LINUX这个宏的话,就会使用外部文件定义的do_bootm_linux函数,

先来看看bootm命令的实现,在common/cmd_bootm.c的第119行开始有:

#ifdef CONFIG_PPC

static boot_os_Fcn do_bootm_linux;

#else

extern boot_os_Fcn do_bootm_linux;

#endif

这里的预编译宏说明了,非 PPC体系结构的CPU的do_bootm_linux()函数都不是在这个文件内实现的(extern)。可想而知,这个函数的实现应该是和体系结构相关的,具体到arm体系结构的实现就是在lib_arm/armlinux.c这个文件当中。可以看到从lib_arm/armlinux.c中的第77 行开始就是do_bootm_linux()函数的实现。
 
其中第85行声明了这样一个函数指针theKernel:

void (*theKernel)(int zero, int arch, uint params);

  

 看看它的名字和参数的命名我们也可以猜到这个其实就是内核的入口函数的指针了。几个参数的命名也说明了下文提到的ARM Linux内核启动要求的第一条,因为根据ACPS(ARM/Thumb Procedure Call Standard)的规定,这三个参数就是依次使用r0,r1和r2来传递的。接下来第93行就是给这个函数指针赋值: 

 theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);

可以看到theKernel被赋值为hdr->ih_ep,这个hdr是指使用tools/mkimage工具程序制作uImage时加在linux.bin.gz前面的一个头部,而ih_ep结构体成员保存的就是使用mkimage时指定的-e参数的值,即内核的入口点(Entry Point)。 知道了hdr->ih_ep的意义之后,给theKernel赋这个值也就是理所当然的了。

theKernel (0, bi_arch_number, bd->bi_boot_params);

  

调用的时候对参数进行赋值,r0=0,r1=bd->bi_arch_number,r2=bd-> bi_boot_params,一个都不少。至此U-Boot的使命完成,开始进入ARM Linux的世界。

   要知道哪个地址是启动内核,哪个地址启动文件系统,要分析common/cmd_bootm.c中的函数 do_bootm,因为引导kernel就是bootm这条命令的工作,do_bootm是命令bootm的执行函数。现在我们来分析一下common/cmd_bootm.c中的函数do_bootm,这是bootm命令的处理函数.do_bootm()函数中的很多功能都是分成了函数的形式,而在以前的版本中没有这么有结构层次,因为这是一个在common文件夹下的文件,也就意味着,在引导别的操作系统时也会用到这个函数,而不单单是Linux操作系统.


do_bootm()函数分析



#ifndef CFG_BOOTM_LEN
#define CFG_BOOTM_LEN    0x800000    /* use 8MByte as default max gunzip size */
#endif

image_header_t header;
//这是很重要的全局变量, 会被armlinux.c 里面的do_bootm_linux()使用


ulong load_addr = CFG_LOAD_ADDR;        /* Default Load Address */
//定义在include/configs/*.h 中 ,比如ep7312.h 是这样定义的:

//#define    CFG_LOAD_ADDR        0xc0500000    /* default load address    */


int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
    ulong    iflag;
    ulong    addr;
    ulong    data, len, checksum;
    ulong *len_ptr;
    uint    unc_len = CFG_BOOTM_LEN;
    int    i, verify;
    char    *name, *s;
    int    (*appl)(int, char *[]);
    image_header_t *hdr = &header;
//之前定义的全局变量


    s = getenv ("verify");
    verify = (s && (*s == 'n')) ? 0 : 1;

//addr 就是kernel的临时下载的地址

    if (argc < 2) {
        addr = load_addr;
//我们的环境: load_addr = 0x33000000

    } else {
        addr = simple_strtoul(argv[1], NULL, 16);
    }

    SHOW_BOOT_PROGRESS (1);
    printf ("## Booting image at %08lx ...\n", addr);

//读取kernel image的header

    /* Copy header so we can blank CRC field for re-calculation */
#ifdef CONFIG_HAS_DATAFLASH
    if (addr_dataflash(addr)){
        read_dataflash(addr, sizeof(image_header_t), (char *)&header);
    } else
#endif
    memmove (&header, (char *)addr, sizeof(image_header_t));

//判断魔数,一般不会出错

    if (ntohl(hdr->ih_magic) != IH_MAGIC) {
#ifdef __I386__    /* correct image format not implemented yet - fake it */
        if (fake_header(hdr, (void*)addr, -1) != NULL) {
            /* to compensate for the addition below */
            addr -= sizeof(image_header_t);
            
/* turnof verify,
             * fake_header() does not fake the data crc
             */

            verify = 0;
        } else
#endif    /* __I386__ */
     {
        puts ("Bad Magic Number\n");
        SHOW_BOOT_PROGRESS (-1);
        return 1;
     }
    }
    SHOW_BOOT_PROGRESS (2);



    data = (ulong)&header;
    len = sizeof(image_header_t);
//header的长度0x40 (64个byte)


    checksum = ntohl(hdr->ih_hcrc);
    hdr->ih_hcrc = 0;

//crc校验

    if (crc32 (0, (uchar *)data, len) != checksum) {
        puts ("Bad Header Checksum\n");
        SHOW_BOOT_PROGRESS (-2);
        return 1;
    }
    SHOW_BOOT_PROGRESS (3);

#ifdef CONFIG_HAS_DATAFLASH
    if (addr_dataflash(addr)){
        len = ntohl(hdr->ih_size) + sizeof(image_header_t);
        read_dataflash(addr, len, (char *)CFG_LOAD_ADDR);
        addr = CFG_LOAD_ADDR;
    }
#endif


    /* for multi-file images we need the data part, too */
    print_image_hdr ((image_header_t *)addr);


//


    
//这里很关键的, 指向了后面的kernel的部分

    data = addr + sizeof(image_header_t);
//指向后面的kernel部分

    len = ntohl(hdr->ih_size);
//kernel的实际大小,就是编译后的大小


    if (verify) {
        puts (" Verifying Checksum ... ");
        if (crc32 (0, (uchar *)data, len) != ntohl(hdr->ih_dcrc)) {
            printf ("Bad Data CRC\n");
            SHOW_BOOT_PROGRESS (-3);
            return 1;
        }
        puts ("OK\n");
    }
    SHOW_BOOT_PROGRESS (4);

    len_ptr = (ulong *)data;

#if defined(__PPC__)
    if (hdr->ih_arch != IH_CPU_PPC)
#elif defined(__ARM__)
    if (hdr->ih_arch != IH_CPU_ARM)
#elif defined(__I386__)
    if (hdr->ih_arch != IH_CPU_I386)
#elif defined(__mips__)
    if (hdr->ih_arch != IH_CPU_MIPS)
#elif defined(__nios__)
    if (hdr->ih_arch != IH_CPU_NIOS)
#elif defined(__M68K__)
    if (hdr->ih_arch != IH_CPU_M68K)
#elif defined(__microblaze__)
    if (hdr->ih_arch != IH_CPU_MICROBLAZE)
#elif defined(__nios2__)
    if (hdr->ih_arch != IH_CPU_NIOS2)
#elif defined(__blackfin__)
    if (hdr->ih_arch != IH_CPU_BLACKFIN)
#elif defined(__avr32__)
    if (hdr->ih_arch != IH_CPU_AVR32)
#else
# error Unknown CPU type
#endif
    {
        printf ("Unsupported Architecture 0x%x\n", hdr->ih_arch);
        SHOW_BOOT_PROGRESS (-4);
        return 1;
    }
    SHOW_BOOT_PROGRESS (5);

    switch (hdr->ih_type) {
    case IH_TYPE_STANDALONE:
        name = "Standalone Application";
        /* A second argument overwrites the load address */
        if (argc > 2) {
            hdr->ih_load = htonl(simple_strtoul(argv[2], NULL, 16));
        }
        break;
    case IH_TYPE_KERNEL:
        name = "Kernel Image";
        break;
    case IH_TYPE_MULTI:
        name = "Multi-File Image";
        len = ntohl(len_ptr[0]);
        /* OS kernel is always the first image */
        data += 8; /* kernel_len + terminator */
        for (i=1; len_ptr[i]; ++i)
            data += 4;
        break;
    default: printf ("Wrong Image Type for %s command\n", cmdtp->name);
        SHOW_BOOT_PROGRESS (-5);
        return 1;
    }
    SHOW_BOOT_PROGRESS (6);

//执行kernel的准备工作


//--------进入最后的阶段

    
/*
     * We have reached the point of no return: we are going to
     * overwrite all exception vector code, so we cannot easily
     * recover from any failures any more...
     */


    iflag = disable_interrupts();

#ifdef CONFIG_AMIGAONEG3SE
    
/*
     * We've possible left the caches enabled during
     * bios emulation, so turn them off again
     */

    icache_disable();
    invalidate_l1_instruction_cache();
    flush_data_cache();
    dcache_disable();
#endif



    /* 最关键的地方: 搬运kernel到ih_load指定的地址上去*/


    switch (hdr->ih_comp) {
    case IH_COMP_NONE:
// -C none

        if(ntohl(hdr->ih_load) == addr) {
//判断download的地址是否和kernel规定的地址相同

            printf (" XIP %s ... ", name);
//XIP: 原地执行

        } else {
#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
            size_t l = len;
            void *to = (void *)ntohl(hdr->ih_load);
            void *from = (void *)data;

            printf (" Loading %s ... ", name);

            while (l > 0) {
                size_t tail = (l > CHUNKSZ) ? CHUNKSZ : l;
                WATCHDOG_RESET();
                memmove (to, from, tail);
                to += tail;
                from += tail;
                l -= tail;
            }
#else    /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
        
//这里就是搬运的代码

        memmove(void * dest, const void * src, size_t count) ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
#endif    /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
        }
        break;
    case IH_COMP_GZIP:
        printf (" Uncompressing %s ... ", name);
        if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,
// 把它解压到 ih_load的位置上去

             (uchar *)data, &len) != 0) {
            puts ("GUNZIP ERROR - must RESET board to recover\n");
            SHOW_BOOT_PROGRESS (-6);
            do_reset (cmdtp, flag, argc, argv);
        }
        break;
#ifdef CONFIG_BZIP2
    case IH_COMP_BZIP2:
        printf (" Uncompressing %s ... ", name);
        
/*
         * If we've got less than 4 MB of malloc() space,
         * use slower decompression algorithm which requires
         * at most 2300 KB of memory.
         */

        i = BZ2_bzBuffToBuffDecompress ((char*)ntohl(hdr->ih_load),
                        &unc_len, (char *)data, len,
                        CFG_MALLOC_LEN < (4096 * 1024), 0);
        if (i != BZ_OK) {
            printf ("BUNZIP2 ERROR %d - must RESET board to recover\n", i);
            SHOW_BOOT_PROGRESS (-6);
            udelay(100000);
            do_reset (cmdtp, flag, argc, argv);
        }
        break;
#endif /* CONFIG_BZIP2 */
    default:
        if (iflag)
            enable_interrupts();
        printf ("Unimplemented compression type %d\n", hdr->ih_comp);
        SHOW_BOOT_PROGRESS (-7);
        return 1;
    }

    
//搬运完毕

    puts ("OK\n");
    SHOW_BOOT_PROGRESS (7);



    switch (hdr->ih_type) {
    case IH_TYPE_STANDALONE:
        if (iflag)
            enable_interrupts();

        
/* load (and uncompress), but don't start if "autostart"
         * is set to "no"
         */

        if (((s = getenv("autostart")) != NULL) && (strcmp(s,"no") == 0)) {
            char buf[32];
            sprintf(buf, "%lX", len);
            setenv("filesize", buf);
            return 0;
        }
        appl = (int (*)(int, char *[]))ntohl(hdr->ih_ep);
        (*appl)(argc-1, &argv[1]);
        return 0;
    case IH_TYPE_KERNEL:
    case IH_TYPE_MULTI:
        /* handled below */
        break;
    default:
        if (iflag)
            enable_interrupts();
        printf ("Can't boot image type %d\n", hdr->ih_type);
        SHOW_BOOT_PROGRESS (-8);
        return 1;
    }
    SHOW_BOOT_PROGRESS (8);



    
//判断何种操作系统

    switch (hdr->ih_os) {
    default:            /* handled by (original) Linux case */
    case IH_OS_LINUX:
#ifdef CONFIG_SILENT_CONSOLE
     fixup_silent_linux();
#endif

//接下来就要调用do_bootm_linux() 函数了,这里要启动kernel

     do_bootm_linux (cmdtp, flag, argc, argv,
             addr, len_ptr, verify);
     break;
    case IH_OS_NETBSD:
     do_bootm_netbsd (cmdtp, flag, argc, argv,
             addr, len_ptr, verify);
     break;

#ifdef CONFIG_LYNXKDI
    case IH_OS_LYNXOS:
     do_bootm_lynxkdi (cmdtp, flag, argc, argv,
             addr, len_ptr, verify);
     break;
#endif

    case IH_OS_RTEMS:
     do_bootm_rtems (cmdtp, flag, argc, argv,
             addr, len_ptr, verify);
     break;

#if (CONFIG_COMMANDS & CFG_CMD_ELF)
    case IH_OS_VXWORKS:
     do_bootm_vxworks (cmdtp, flag, argc, argv,
             addr, len_ptr, verify);
     break;
    case IH_OS_QNX:
     do_bootm_qnxelf (cmdtp, flag, argc, argv,
             addr, len_ptr, verify);
     break;
#endif /* CFG_CMD_ELF */
#ifdef CONFIG_ARTOS
    case IH_OS_ARTOS:
     do_bootm_artos (cmdtp, flag, argc, argv,
             addr, len_ptr, verify);
     break;
#endif
    }

    SHOW_BOOT_PROGRESS (-9);
#ifdef DEBUG
    puts ("\n## Control returned to monitor - resetting...\n");
    do_reset (cmdtp, flag, argc, argv);
#endif
    return 1;
}

U_BOOT_CMD(
     bootm,    CFG_MAXARGS,    1,    do_bootm,
     "bootm - boot application image from memory\n",
     "[addr [arg ...]]\n - boot application image stored in memory\n"
     "\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
     "\t'arg' can be the address of an initrd image\n"
#ifdef CONFIG_OF_FLAT_TREE
    "\tWhen booting a Linux kernel which requires a flat device-tree\n"
    "\ta third argument is required which is the address of the of the\n"
    "\tdevice-tree blob. To boot that kernel without an initrd image,\n"
    "\tuse a '-' for the second argument. If you do not pass a third\n"
    "\ta bd_info struct will be passed instead\n"
#endif
);


 bootm命令是用来引导经过u-boot的工具mkimage打包后的kernel image的。

mkimage的用法

   uboot源代码的tools/目录下有mkimage工具,这个工具可以用来制作不压缩或者压缩的多种可启动映象文件。
   mkimage在制作映象文件的时候,是在原来的可执行映象文件的前面加上一个0x40字节的头,记录参数所指定的信息,这样uboot才能识别这个映象是针对哪个CPU体系结构的,哪个OS的,哪种类型,加载内存中的哪个位置, 入口点在内存的那个位置以及映象名是什么?到这里整个U-Boot是如何启动Linux内核的,基本上也就清楚了,特别是如何向Linux内核传送的参数。
bootm命令是用来引导经过u-boot的工具mkimage打包后的kernel image的,什么叫做经过u-boot的工具mkimage打包后的kernel image,这个就要看mkimage的代码,看看它做了些什么,虽然我很希望大家不要偷懒,认真地去看看,但是我知道还是有很多人懒得去做这件,那么我就j将分析mkimage代码后得到的总结告诉大家,mkimage做了些什么,怎么用这个工具。

mkimage的用法
uboot源代码的tools/目录下有mkimage工具,这个工具可以用来制作不压缩或者压缩的多种可启动映象文件。

mkimage在制作映象文件的时候,是在原来的可执行映象文件的前面加上一个0x40字节的头,记录参数所指定的信息,这样uboot才能识别这个映象是针对哪个CPU体系结构的,哪个OS的,哪种类型,加载内存中的哪个位置, 入口点在内存的那个位置以及映象名是什么

引用:
root@Glym:/tftpboot# ./mkimage
Usage: ./mkimage -l image
-l ==> list image header information
./mkimage -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image
-A ==> set architecture to 'arch'
-O ==> set operating system to 'os'
-T ==> set image type to 'type'
-C ==> set compression type 'comp'
-a ==> set load address to 'addr' (hex)
-e ==> set entry point to 'ep' (hex)
-n ==> set image name to 'name'
-d ==> use image data from 'datafile'
-x ==> set XIP (execute in place)


参数说明:

-A 指定CPU的体系结构:

取值 表示的体系结构
alpha Alpha
arm A RM
x86 Intel x86
ia64 IA64
mips MIPS
mips64 MIPS 64 Bit
ppc PowerPC
s390 IBM S390
sh SuperH
sparc SPARC
sparc64 SPARC 64 Bit
m68k MC68000

-O 指定操作系统类型,可以取以下值:
openbsd、netbsd、freebsd、4_4bsd、linux、svr4、esix、solaris、irix、sco、dell、ncr、lynxos、vxworks、psos、qnx、u-boot、rtems、artos

-T 指定映象类型,可以取以下值:
standalone、kernel、ramdisk、multi、firmware、script、filesystem

-C 指定映象压缩方式,可以取以下值:
none 不压缩
gzip 用gzip的压缩方式
bzip2 用bzip2的压缩方式

-a 指定映象在内存中的加载地址,映象下载到内存中时,要按照用mkimage制作映象时,这个参数所指定的地址值来下载

-e 指定映象运行的入口点地址,这个地址就是-a参数指定的值加上0x40(因为前面有个mkimage添加的0x40个字节的头)

-n 指定映象名

-d 指定制作映象的源文件

eg  :  mkimage -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008040 -n "Linux kernel Image by embedclub" -d zImage uImage             //由zImage ==>uImage

PS:下面是“ARM Linux Kernel Boot Requirements”,这篇文章中介绍的,引导Linux内核启动的必须要满足的几个条件:

* CPU register settings //这里也就是我们的theKernel中的作用
          o r0 = 0.
          o r1 = machine type number.
          o r2 = physical address of tagged list in system RAM.
    * CPU mode
          o All forms of interrupts must be disabled (IRQs and FIQs.)
          o The CPU must be in SVC mode. (A special exception exists for Angel.)
    * Caches, MMUs
          o The MMU must be off.
          o Instruction cache may be on or off.
          o Data cache must be off and must not contain any stale data.
    * Devices
          o DMA to/from devices should be quiesced.
    * The boot loader is expected to call the kernel image by jumping directly to the first instruction of the kernel image.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
相信许多liunxer初次听到“GRUB4DOS”这个名字,第一感觉就是:这个对我没用,因为我不用DOS,即使用也不会去通过它启动GRUB。先别急,再问一句:您是否用过grldr呢?是否用过MaxDos、一键 ghost、矮人DOS工具盘?如果答案是肯定的,那您已经在用GRUB4DOS了。 是它的名字给人们造成了误解,这也是历史遗留问题——一开始仅仅是“for dos”,而后来开发越来越深入,“FOR DOS”已远不能涵盖其全部。于是它的名字就成了一个障碍,新接触的人往往望名生意,以为它是专给DOS用的,便不去关注——我本人就是如此,很久之后才重新发现它,所以写下这篇文字,希望更多人能够了解并使用这一优秀作品,享受它带来的可靠与便捷。 首先搞清楚与 GNU GRUB 的关系。 GNU GRUB 分为 GNU GRUB Lagecy 和 GNU GRUB2 两代。GNU GRUB Lagecy 其实就是原来的 GNU GRUB 0.xx ,最新版是 2005 年发布的 GNU GRUB 0.97 。目前已停止开发,并改名为 GNU GRUB Lagecy 。GNU GRUB2 是第二代 GRUB,它将取代原来的 GNU GRUB (例如0.9x版),但目前还处于开发阶段,尚未发布正式版。 而 GRUB4DOS 则是对 GNU GRUB Lagecy 的二次开发。该项目最早由 不点 在2003年发起,目前主要由他、bean 和另外几位中国人维护,很多人贡献了代码,再加上广大网友热心帮助,使得 GRUB4DOS 不断完善。在当前 新的 GRUB2 没有到位、老的 GNU GRUB Lagecy 又有许多不完善 的情况下,GRUB4DOS 受到越来越多人的欢迎。 GRUB4DOS 在保留 GNU GRUB 0.97 原有功能的基础上,做了很多改进,简单归纳如下: 1、A20代码 和 bug修正 对A20的处理历来就是一个棘手问题。当初IBM引入这一设计,本意是为了让以往那些BUGGY软件能够运行,结果造成后世无穷无尽的隐患。当然不能全怪 IBM,真正应当责怪的是那些不遵守IBM标准的硬件厂商,它们生产的系统无法用常规的几种方法来控制A20,使得软件开发者可能在很多意想不到的地方碰上A20这个妖魔,除了造成死机之外,还导致执行效率的极度降低。在微软的XMS规范中就明确写着,A20的控制在有些机器上是非常耗时的一个操作。 GNU GRUB 0.97中的A20代码很明确是有问题的,这在GNU GRUB邮件列表档案中就有提到。很多用户报告在使用gnu grub 0.97时键盘失去响应。grub开发组在grub2中对A20代码打了补丁,以解决这些问题,但是这个补丁并没有打到我们目前广泛使用的0.97版上。为此GRUB4DOS用取自Linux内核的A20代码替换了原代码,并参考其他一些开源代码做了很多改进,基本上解决了这一问题。 A20只是改造的一个方面,在GRUB4DOS的开发过程中修复的GNU GRUB 0.97的bug还有很多,有些在Changelog中并没有提到。到目前为止,已经把那些严重的BUG排解殆尽了,应该可以比较平滑地使用GRUB4DOS了。 2、磁盘仿真(虚拟磁盘) 这是GRUB4DOS的一大特色功能,开发者下了不少功夫。这一功能是将 软盘镜像或硬盘镜像甚至某个硬盘分区 虚拟成 软驱或硬盘,进而运行其中的操作系统(DOS、win98),类似于以前流行的“虚拟软驱”(vfloppy),但功能要强得多。 用SYSLINUX中的memdisk也可实现虚拟磁盘。比较而言,GRUB4DOS功能更强,限制更少,用法更灵活,同时对bios的要求也要高那么一点点,在一些BIOS有缺陷的机子上可能会出问题(不过不用太担心,实践表明这种情况极其罕见)。 3、多种方式进入grub环境 1)通过dos 在dos下运行grub.exe即可进入纯粹的grub环境,并部分地支持退回到DOS。也可把grub.exe作为 CONFIG.SYS 里的设备驱动文件而运行。 2)通过Windows启动菜单 这是被广泛使用的一种用法。MaxDos、一键ghost硬盘版、矮人DOS工具盘之类的软件就是以这种方式启动grldr,再通过磁盘仿真虚拟出一个软驱,进而启动DOS的。 3)将GRUB4DOS引导代码写入硬盘MBR,开机直接进入grub环境 乍一看这不算什么改进——GNU GRUB本来就可以这样引导,但实际上在这里 GRUB4DOS 有相当大的革新。 GNU GRUB 的安装方法是: root (xxx,x) setup (xxx) 第一句是指定一个分区,该分区须存放有/boot/stage1和/boot/stage2文件,第二句是将引导代码写入硬盘MBR。这样安装的 GRUB,其MBR引导代码被设置为从固定的分区加载stage2。假如我们某一天因为某些原因,需要删除或格式化该分区,或者做一些会引起该分区序号变化的操作(比如用 pqmagic 在该分区前添加或删除分区),就需要预先重装一次grub,重新指定存放stage2文件的分区,否则将无法引导。这一点上 GNU GRUB 显得不够灵活。 而 GRUB4DOS 采取了新的策略:其MBR引导代码并非固定地指向某个分区,而是搜索所有分区,查找并加载 grldr,只要某分区上存有一份 grldr,就能启动。而且安装时可以将原微软的MBR备份,启动时如果找不到grldr,便自动加载备份的MBR。显然这种方式更加灵活、稳健,所以 GRUB4DOS 虽然保留了 GNU GRUB 的全部命令和功能(包括 setup 命令),但不推荐使用传统的安装方式。 当然 GRUB4DOS 方式也不是十全十美,因为其引导代码还不能识别所有的文件系统,目前支持仅 NTFS/FAT12/FAT16/FAT32/EXT2/EXT3(也不少了 ,以后将支持更多的格式),所以可能需要专门设一个分区来存放 grldr 。不过在我看来这是值得的,一点小麻烦换来了更多的方便和稳定。 4、图片背景支持 可用 640x480@14位色 的图片做背景。图片制作请参考这里: http://bbs.znpc.net/viewthread.php?tid= ... a=page%3D4 5、中文支持 GRUB4DOS 发布有中文版,大大方便了中文用户。 6、光驱支持 该功能使得用户可以再GRUB环境下访问光驱,并可由光驱启动,很实用。Os:GNU/Debian Linux 5.0 Lenny -AMD Athlon(tm) 64 Processor 3000+ -NVIDIA GeForce 7300 GT -512M*2 DDR Memory -ST SATA 250G
本书是根据相关的博客做的PDF格式的电子书,欢迎到原作者的博客去看看。 这个是目录: ·嵌入式Linux之我行——虚拟机中安装Linux ·嵌入式Linux之我行——虚拟机中实现Linux与Windows之间的文件传输 ·嵌入式Linux之我行——开发环境的建立与Eclipse的使用 ·嵌入式Linux之我行——配置内核时出现“ncurses-devel”错误 ·嵌入式Linux之我行——C+CGI+Ajax在S3C2440中的应用 ·嵌入式Linux之我行——嵌入式数据库sqlite在2440上的移植 ·嵌入式Linux之我行——嵌入式数据库sqlite在2440上的应用 ·嵌入式Linux之我行——Linux-2.6.30.4在2440上的移植之内核 ·嵌入式Linux之我行——Linux-2.6.30.4在2440上的移植之文件系统 ·嵌入式Linux之我行——Linux-2.6.30.4在2440上的移植之RTC时钟驱动 ·嵌入式Linux之我行——Linux-2.6.30.4在2440上的移植之DM9000E网卡驱动 ·嵌入式Linux之我行——Linux-2.6.30.4在2440上的移植之USB驱动 ·嵌入式Linux之我行——Linux-2.6.30.4在2440上的移植之MMC/SD卡驱动 ·嵌入式Linux之我行——Linux-2.6.30.4在2440上的移植之LCD驱动 ·嵌入式Linux之我行——Linux-2.6.30.4在2440上的移植之触摸屏驱动 ·嵌入式Linux之我行——Linux-2.6.30.4在2440上的移植之UDA1341声卡驱动 ·嵌入式Linux之我行——u-boot-2009.08在2440上的移植详解(一) ·嵌入式Linux之我行——u-boot-2009.08在2440上的移植详解(二) ·嵌入式Linux之我行——u-boot-2009.08在2440上的移植详解(三) ·嵌入式Linux之我行——u-boot-2009.08在2440上的移植详解(四) ·嵌入式Linux之我行——u-boot-2009.08在2440上的移植详解(五) ·嵌入式Linux之我行——u-boot-2009.08在2440上的移植详解(六) ·嵌入式Linux之我行——s3c2440的IO静态映射的分析 ·嵌入式Linux之我行——内核访问外设I/O资源的方式 ·嵌入式Linux之我行——深入理解DM9000在mini2440上的驱动 ·嵌入式Linux之我行——LCD背光驱动在2440上的实例开发 ·嵌入式Linux之我行——LED驱动在2440上的实例开发 ·Linux内核常用的一些宏的收集 ·嵌入式Linux之我行——按键驱动在2440上的实例开发(带去抖动) ·嵌入式Linux之我行——ARM MMU工作原理剖析 ·嵌入式Linux之我行——设备文件系统剖析与使用 ·嵌入式Linux之我行——PWM在ARM Linux中的原理和蜂鸣器驱动实例开发 ·嵌入式Linux之我行——S3C2440上RTC时钟驱动开发实例讲解 ·嵌入式Linux之我行——S3C2440上看门狗(Watchdog)驱动开发实例讲解 ·嵌入式Linux之我行——S3C2440上ADC驱动实例开发讲解 ·嵌入式Linux之我行——S3C2440上触摸屏驱动实例开发讲解 ·嵌入式Linux之我行——S3C2440上LCD驱动(FrameBuffer)实例开发讲解(一) ·嵌入式Linux之我行——S3C2440上LCD驱动(FrameBuffer)实例开发讲解(二) ·嵌入式Linux之我行——RamDisk块设备驱动实例开发讲解 ·嵌入式Linux之我行——S3C2440上MMC/SD卡驱动实例开发讲解(一) ·嵌入式Linux之我行——S3C2440上MMC/SD卡驱动实例开发讲解(二) ·嵌入式Linux之我行——内核通知链机制的原理及实现(转载) ·嵌入式Linux之我行——S3C2440上Flash驱动实例开发讲解(一)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值