8/29/2011 1:17:53 PM

来源:互联网 发布:淘宝买家退货率怎么算 编辑:程序博客网 时间:2024/04/30 05:36
 

8/29/2011 1:17:53 PM

 

先测试一下 pxa310的媒体问题

现在是H.264不出图像

保存上一次的信息

现在要求对方停止对讲  发送的时候发了一个对端设备名 通道号 和本端的通道号

返回的时候就发了一个errcode

现在还是将环境搭建起来  测试一下 出个版本

前台需要重新做license

编译android 环境

加入媒体采集的功能

找到视频采集的部分

采集的时候看一下日志

直接打开照相的功能

struct drive_opt {
    const char *file;
    char opt[1024];
    int used;
};

就一个文件名,就一个opt

先看一下音频采集

<4>goldfish_new_pdev goldfish_audio at ff004000 irq 14


3.9.2 Using host USB devices on a Linux host


WARNING: this is an experimental feature. QEMU will slow down when using it.

 USB devices requiring real time streaming (ie USB Video Cameras) are not supported yet.

1.If you use an early Linux 2.4 kernel, verify that no Linux driver is actually using the USB device.

 A simple way to do that is simply to disable the corresponding kernel module by renaming it from ' mydriver.o ' to ' mydriver.o.disabled '.
2.Verify that ' /proc/bus/usb ' is working (most Linux distributions should enable it by default). You should see something like that:   ls /proc/bus/usb
001 devices drivers
 

3.Since only root can access to the USB devices directly, you can either launch QEMU as root or change the permissions of the USB devices you want to use. For testing, the following suffices: 

 chown -R myuid /proc/bus/usb

 

4.Launch QEMU and do in the monitor:   info usbhost
  Device 1.2, speed 480 Mb/s
    Class 00: USB device 1234:5678, USB DISK
 

You should see the list of the devices you can use (Never try to use hubs, it won't work).

5.Add the device in QEMU by using:   usb_add host:1234:5678
 
Normally the guest OS should report that a new USB device is plugged. You can use the option ' -usbdevice ' to do the same.

6.Now you can try to use the host USB device in QEMU.
When relaunching QEMU, you may have to unplug and plug again the USB device to make it work again (this is a bug).

怎么进入qemu的monitor界面


emulator是怎么指定硬件配置的?

 

QEMU CHARACTER "DEVICES" MANAGEMENT
I. CharDriverState objects:

字符驱动状态 对象

---------------------------
One of the strangest abstraction in QEMU is the "CharDriverState" (abbreviated here as "CS").

The CS is essentially an object used to model a character stream that can be connected to things like a host serial port ,

cs 可以连接到主机串口

 a host network socket, an emulated device, etc...
 
 主机网路  一个模拟设备 等
 
 也就是说你仅仅是一个字符流
what's really unusual is its interface though, which comes from the fact that QEMU implements a big event loop with no blocking i/o allowed.

这个大得事件循环
 You can see "qemu-char.h" for the full interface, but here we will only describe a few important functions:

  - qemu_chr_write() is used to try to write data into a CS object.
 
  Note that success is not guaranteed: the function returns the number of bytes that were really written (which can be 0) and the caller must deal with it.
  This is very similar to writing to a non-blocking BSD socket on Unix.
 
  也就是说你的字符设备是非阻塞的
 
   int  qemu_chr_read( CharDriverState*  cs, const uint8_t*    data,
                           int               datalen );
    This function may return -1 in case of error, but this depends entirely on the underlying implementation (some of them will just return 0 instead).
   
     In practice, this means it's not possible to reliably differentiate between a "connection reset by peer" and an "operation in progress" :-(
    There is no way to know in advance how many bytes a given CharDriverState can accept, nor to be notified when its underlying implementation is ready to accept data again.
  - qemu_chr_add_handler() is used to add "read" and "event" handlers to a CS object. We will ignore "events" here and focus on the
    "read" part.
    Thing is, you cannot directly read from a CS object. Instead, you provide two functions that will be called whenever the object has something for you:
        - a 'can_read' function that shall return the number of bytes that you are ready to accept from the CharDriverState. Its
          interface is:
             typedef int  IOCanRWHandler (void*  opaque);
        - a 'read' function that will send you bytes from the CharDriverState
             typedef void IOReadHandler  (void*           opaque, const uint8_t*  data,
                                          int             datalen);
          normally, the value of 'datalen' cannot be larger than the result of a previous 'can_read' call.
    For both callbacks, 'opaque' is a value that you pass to the function qemu_chr_add_handler() which signature is:
         void qemu_chr_add_handlers(CharDriverState *s, IOCanRWHandler  *fd_can_read,
                                    IOReadHandler   *fd_read, IOEventHandler  *fd_event,
                                    void            *opaque);
     这里就是一个能读和读                                                                        
  - qemu_chr_open() is used to create a new CharDriverState object from a descriptive string, its interface is:
         CharDriverState*  qemu_chr_open(const char*  filename);
    there are various formats for acceptable 'filenames', and they correspond to the parameters of the '-serial' QEMU option described here:
       http://www.nongnu.org/qemu/qemu-doc.html#SEC10
    For example:
       "/dev/<file>" (Linux and OS X only): connect to a host character device file (e.g. /dev/ttyS0)
       写到一个真实的串口上了
       "file:<filename>": Write output to a given file (write only)
       "stdio": Standard input/output
       "udp:[<remote_host>]:<remote_port>[@[<src_ip>]:<src_port>]": Connect to a UDP socket for both read/write.
       "tcp:[<host>]:<port>[,server][,nowait][,nodelay]" Connect to a TCP socket either as a client or a server.
            The 'nowait' option is used to avoid waiting for a client connection.
            The 'nodelay' is used to disable the TCP Nagle algorithm to improve throughput.
           
            注意cs对象在open的时候传入的参数
           
            
    For Android, a few special names have been added to the internal implementation and redirect to program functions:
       "android-kmsg": A CharDriverState that is used to receive kernel log messages
            from the emulated /dev/ttyS0 serial port.
          
           也就是向这个串口写日志 
          
       "android-qemud": A CharDriverState that is used to exchange messages between the
            emulator program and the "qemud" multiplexing daemon that runs in the emulated system.
            The "qemud" daemon is used to allow one or more clients in the system to connect to various services running in the emulator
            program. This is mainly used to bypass the kernel in order to implement certain features with ease.
            (see docs/ANDROID-QEMUD.TXT for details)
           
            这个android-qemud 是定位到外界socket上了吗? 是和adb server 进行通信的么?
           
           
            /* intialize a QemudSerial object with a charpipe endpoint
 * and a receiver.
 */
static void
qemud_serial_init( QemudSerial*        s,
                   CharDriverState*    cs,
                   QemudSerialReceive  recv_func,
                   void*               recv_opaque )
{
    s->cs           = cs;
    s->recv_func    = recv_func;
    s->recv_opaque  = recv_opaque;
    s->need_header  = 1;
    s->overflow     = 0;

    qemud_sink_reset( s->header, HEADER_SIZE, s->data0 );
    s->in_size      = 0;
    s->in_channel   = -1;

#if SUPPORT_LEGACY_QEMUD
    s->version = QEMUD_VERSION_UNKNOWN;
    qemud_serial_send_legacy_probe(s);
#endif

    qemu_chr_add_handlers( cs,
                           qemud_serial_can_read,
                           qemud_serial_read,
                           NULL,
                           s );
}


qemu_chr_add_handlers 添加处理只是添加读串口处理么


串口和cs操作之间的关系弄明白了么?

封装了客户端和服务器的操作

charBuffer 怎么会用2个cs结构呢?

 

/* this function is used to register a new named qemud-based
 * service. You must provide 'serv_opaque' and 'serv_connect'
 * which will be called whenever a new client tries to connect
 * to the services.
 *
 * 'serv_connect' shall return NULL if the connection is refused,
 * or a handle to a new QemudClient otherwise. The latter can be
 * created through qemud_client_new() defined above.
 *
 * 'max_clients' is the maximum number of clients accepted by
 * the service concurrently. If this value is 0, then any number
 * of clients can connect.
 */
 
 连接回调和连接限制 
 
 typedef struct QemudMultiplexer  QemudMultiplexer;

struct QemudMultiplexer {
    QemudSerial    serial[1];
    QemudClient*   clients;
    QemudService*  services;
};

static QemudMultiplexer  _multiplexer[1];

独此一家,别无分号

是谁连接的?

看看qemud的实现看看

实际上就是模拟器对外提供的一个接口

 


/*
 *  the qemud daemon program is only used within Android as a bridge
 *  between the emulator program and the emulated system. it really works as
 *  a simple stream multiplexer that works as follows:
 
 谁是谁?
 *
 *    - qemud is started by init following instructions in
 *      /system/etc/init.goldfish.rc (i.e. it is never started on real devices)
 *
 *    - qemud communicates with the emulator program through a single serial
 *      port, whose name is passed through a kernel boot parameter
 *      (e.g. android.qemud=ttyS1)
 
  模拟程序和模拟系统之间使ttyS1进行通信
 
 *    - qemud binds one unix local stream socket (/dev/socket/qemud, created
 *      by init through /system/etc/init.goldfish.rc).
 
 *      emulator <==serial==> qemud <---> /dev/socket/qemud <-+--> client1
 *                                                            |
 *                                                            +--> client2
 *
 *   - the special channel index 0 is used by the emulator and qemud only.
 *     other channel numbers correspond to clients. More specifically,
 *     connection are created like this:
 *
 *     * the client connects to /dev/socket/qemud
 *
 *     * the client sends the service name through the socket, as
 *            <service-name>
 *
 *     * qemud creates a "Client" object internally, assigns it an
 *       internal unique channel number > 0, then sends a connection
 *       initiation request to the emulator (i.e. through channel 0):
 *
 *           connect:<id>:<name>
 *
 *       where <name> is the service name, and <id> is a 2-hexchar
 *       number corresponding to the channel number.
 *
 *     * in case of success, the emulator responds through channel 0
 *       with:
 *
 *           ok:connect:<id>
 *
 *       after this, all messages between the client and the emulator
 *       are passed in pass-through mode.
 *
 *     * if the emulator refuses the service connection, it will
 *       send the following through channel 0:
 *
 *           ko:connect:<id>:reason-for-failure
 *
 *     * If the client closes the connection, qemud sends the following
 *       to the emulator:
 *
 *           disconnect:<id>
 *
 *       The same message is the opposite direction if the emulator
 *       chooses to close the connection.
 *
 *     * any command sent through channel 0 to the emulator that is
 *       not properly recognized will be answered by:
 *
 *           ko:unknown command
 *
 *
 *  Internally, the daemon maintains a "Client" object for each client
 *  connection (i.e. accepting socket connection).
 */

/* name of the single control socket used by the daemon */

从启动参数中获取串口

怎么模拟的这个tty?

goldfish_tty_add 怎么添加的?


goldfish_device 是怎么组织起来的?

除了一个串口cs数组就没有其他的数据结构了?

怎么进行地址转换 和 中断通知的?

cpu_physical_memory_rw

读写物理地址

        page = addr & TARGET_PAGE_MASK;
        l = (page + TARGET_PAGE_SIZE) - addr;

还差多少写满一页?

        if (l > len)
            l = len;
           
需要判断这个长度的读写会不会越过一页?

static PhysPageDesc *phys_page_find_alloc(target_phys_addr_t index, int alloc)

通过索引来查找物理页的描述

也就是说L1 居然有12位


/* XXX: for system emulation, it could just be an array */
static PageDesc *l1_map[L1_SIZE];
static PhysPageDesc **l1_phys_map;

1级页表描述  也就是页目录表
2 级页表描述 也就是页表            
           
       "android-gsm": A CharDriverState that is used to connect the emulated system to
            a host modem device with the -radio <device> option. Otherwise, the system uses qemud to connect to the emulator's internal modem emulation.
        "android-gps": A CharDriverState that is used to connect the emulated system to a
            host GPS device with the -gps <device> option. Otherwise the system uses qemud to connect to the emulator's internal GPS
            emulation.
II. CharDriverState users:
--------------------------
As described above, a CharDriverState "user" is a piece of code that can write to a CharDriverState (by calling qemu_chr_write() explicitely) and can also read from it after registering can_read/read handlers for it through qemu_chr_add_handlers().
Typical examples are the following:
  - The hardware serial port emulation (e.g. hw/goldfish_tty.c) will read data from the kernel then send it to a CS. It also uses a small buffer that is used to read data from the CS and send it back to the kernel.
  - The Android emulated modem also uses a CS to talk with its client, which will in most cases be an emulated serial port.
III. CharBuffer objects:
------------------------
The Android emulator provides an object called a CharBuffer which acts as a CharDriverState object that implements a *write* buffer to send data to a given CS object, called the endpoint. You can create one with:
    #include "charpipe.h"
    CharDriverState*  qemu_chr_open_buffer( CharDriverState*  endpoint );
This function returns a new CS object that will buffer in the heap any data that is sent to it, but cannot be sent to the endpoint yet. On each event loop iteration, the CharBuffer will try to send data to the endpoint until it doesn't have any data left.
This can be useful to simplify certain CS users who don't want to maintain their own emit buffer. Note that writing to a CharBuffer always succeeds.
Note also that calling qemu_chr_add_handler() on the CharBuffer will do the same on the endpoint. Any endpoint-initiated calls to can_read()/read() callbacks are passed directly to your handler functions.
IV. CharPipe objects:
---------------------
The Android emulator also provides a convenient abstraction called a "charpipe" used to connect two CharDriverState users together. For example, this is used to connect a serial port emulation (in hw/goldfish_tty.c) to the internal GSM modem emulation (see telephony/modem_driver.c).
Essentially, a "charpipe" is a bi-directionnal communication pipe whose two endpoints are both CS objects. You call "qemu_chr_open_pipe()" to create the pipe, and this function will return the two endpoints to you:
    #include "charpipe.h"
    int  qemu_chr_open_pipe(CharDriverState* *pfirst, CharDriverState* *psecond);
When you write to one end of the pipe (with qemu_chr_write()), the charpipe will try to write as much data as possible to the other end. Any remaining data is stored in a heap-allocated buffer.
The charpipe will try to re-send the buffered data on the next event loop iteration by calling the can_read/read functions of the corresponding user, if there is one.
Note that there is no limit on the amount of data buffered in a charpipe, and writing to it is never blocking. This simplifies CharDriverState users who don't need to worry about buffering issues.

phys_offset

在模拟器中的偏移?

触摸屏的模拟


在PC机中,由于早期版本的系统资源限制,其物理内存被分为多个不同的区域,
并一直延续至今,那么QEMU是如何对这种静态内存布局进行模拟的呢?

内存


1.1    整体内存分配
虽然PC机的物理内存被人为地分为多个不同的区域,但是在物理结构上它们仍然是连续的,因此qemu直接从宿主机中分配了一块内存:

int main(int argc, char **argv, char **envp)

{
     /* init the memory */

     phys_ram_size = machine->ram_require & ~RAMSIZE_FIXED;

 

     if (machine->ram_require & RAMSIZE_FIXED) {

         if (ram_size > 0) {

              if (ram_size < phys_ram_size) {

                   fprintf(stderr, "Machine `%s' requires %llu bytes of memory/n",

                       machine->name, (unsigned long long) phys_ram_size);

                   exit(-1);

              }
              phys_ram_size = ram_size;

         } else

              ram_size = phys_ram_size;

     } else {

         if (ram_size == 0)

              ram_size = DEFAULT_RAM_SIZE * 1024 * 1024;

 

         phys_ram_size += ram_size;

     }

 

     phys_ram_base = qemu_vmalloc(phys_ram_size);

     if (!phys_ram_base) {

         fprintf(stderr, "Could not allocate physical memory/n");

         exit(1);

     }

………………………….

    return 0;

}

在这一段代码里面,ram_size变量的值可以通过“-m megs”参数指定,如果没指定则取默认值DEFAULT_RAM_SIZE,即:

#define DEFAULT_RAM_SIZE 128

但总共分配的内存并不只这些,还要加上machine->ram_require的大小,这个值来自于预定义的常量,对于pc模拟而言就是:

QEMUMachine pc_machine = {

    /*.name =*/ "pc",

    /*.desc =*/ "Standard PC",

    /*.init =*/ pc_init_pci,

    /*.ram_require =*/ VGA_RAM_SIZE + PC_MAX_BIOS_SIZE,

    /*.nodisk_ok =*/ 0,

    /*.use_scsi =*/ 0,

    /*.max_cpus =*/ 255,

    /*.next =*/ NULL

};

也就是说,总共分配的内存还要加上VGA_RAM_SIZE 和 PC_MAX_BIOS_SIZE:


#define VGA_RAM_SIZE (8192 * 1024)

#define PC_MAX_BIOS_SIZE (4 * 1024 * 1024)

总共12M。

VGA的Bios的内存也需要

在分配了内存后,将其指针保存在phys_ram_base这一全局变量中,猜测以后虚拟机访问SDRAM的操作都将访问此内存块。

1.2    内存块的再分配
如果要从前面分配的大内存块中取一小块,则必须使用qemu_ram_alloc函数:

 

/* XXX: better than nothing */

ram_addr_t qemu_ram_alloc(ram_addr_t size)

{

    ram_addr_t addr;

    if ((phys_ram_alloc_offset + size) > phys_ram_size) {

        fprintf(stderr, "Not enough memory (requested_size = %" PRIu64 ", max memory = %" PRIu64 ")/n",

                (uint64_t)size, (uint64_t)phys_ram_size);

        abort();

    }

    addr = phys_ram_alloc_offset;

    phys_ram_alloc_offset = TARGET_PAGE_ALIGN(phys_ram_alloc_offset + size);

 

    if (kvm_enabled())

        kvm_setup_guest_memory(phys_ram_base + addr, size);

 

    return addr;

}

从这个函数可以看出,它使用了按顺序从低到高分配这种很简单的手段,用phys_ram_alloc_offset这一个全局变量记录当前已经分配了多少内存。

需要注意的是,这个函数最后返回的也是一个偏移量,而不是宿主机上的实际内存地址。

1.3    内存块管理
对于使用qemu_ram_alloc分配出来的内存块,通常还需要调用cpu_register_physical_memory进行注册:

static inline void cpu_register_physical_memory(target_phys_addr_t start_addr,

                                                ram_addr_t size,

                                                ram_addr_t phys_offset)

{

    cpu_register_physical_memory_offset(start_addr, size, phys_offset, 0);

}

 

/* register physical memory. 'size' must be a multiple of the target

   page size. If (phys_offset & ~TARGET_PAGE_MASK) != 0, then it is an

   io memory page.  The address used when calling the IO function is

   the offset from the start of the region, plus region_offset.  Both

   start_region and regon_offset are rounded down to a page boundary

   before calculating this offset.  This should not be a problem unless

   the low bits of start_addr and region_offset differ.  */

void cpu_register_physical_memory_offset(target_phys_addr_t start_addr,

                                         ram_addr_t size,

                                         ram_addr_t phys_offset,

                                         ram_addr_t region_offset)

{

……………..

    region_offset &= TARGET_PAGE_MASK;

    size = (size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK;

    end_addr = start_addr + (target_phys_addr_t)size;

    for(addr = start_addr; addr != end_addr; addr += TARGET_PAGE_SIZE) {

        p = phys_page_find(addr >> TARGET_PAGE_BITS);

        if (p && p->phys_offset != IO_MEM_UNASSIGNED) {

………………
        } else {

            p = phys_page_find_alloc(addr >> TARGET_PAGE_BITS, 1);

            p->phys_offset = phys_offset;

            p->region_offset = region_offset;

            if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM ||

                (phys_offset & IO_MEM_ROMD)) {

                phys_offset += TARGET_PAGE_SIZE;

            } else {

………..

            }

        }

        region_offset += TARGET_PAGE_SIZE;

    }

…………….

}

从这段代码可以猜测到,QEMU对每一个注册进来的内存块都进行了分页,每一个页面大小为4K,且用一个结构体对这些页进行描述:

typedef struct PhysPageDesc {

    /* offset in host memory of the page + io_index in the low bits */

    ram_addr_t phys_offset;

    ram_addr_t region_offset;

} PhysPageDesc;

物理地址注册

然后采用某种机制对此结构体的变量进行管理。在这个结构体里的phys_offset指出这个页面的实际内容存放的位置,


通过这个偏移量和phys_ram_base可以访问到这个页面的实际内容,也是通过这个手段实现了对bios内容的映射。

而region_offset则指出这个内存页在其所属的内存块中的偏移量,其数值为4K的整数倍。

区域偏移和物理偏移


1.4    对PC静态内存布局的模拟
在QEMU启动对X86结构的模拟时,会调用一个叫pc_init1的函数:

 

/* PC hardware initialisation */

static void pc_init1(ram_addr_t ram_size, int vga_ram_size,

                     const char *boot_device,

                     const char *kernel_filename, const char *kernel_cmdline,

                     const char *initrd_filename,

                     int pci_enabled, const char *cpu_model)

{

…………………..

 

    /* allocate RAM */

    ram_addr = qemu_ram_alloc(0xa0000);

    cpu_register_physical_memory(0, 0xa0000, ram_addr);

 

    /* Allocate, even though we won't register, so we don't break the

     * phys_ram_base + PA assumption. This range includes vga (0xa0000 - 0xc0000),

     * and some bios areas, which will be registered later

     */

    ram_addr = qemu_ram_alloc(0x100000 - 0xa0000);

    ram_addr = qemu_ram_alloc(below_4g_mem_size - 0x100000);

    cpu_register_physical_memory(0x100000,

                 below_4g_mem_size - 0x100000,

                 ram_addr);

………………….

 

    /* allocate VGA RAM */

    vga_ram_addr = qemu_ram_alloc(vga_ram_size);

 

    /* BIOS load */

    if (bios_name == NULL)

        bios_name = BIOS_FILENAME;

    snprintf(buf, sizeof(buf), "%s/%s", bios_dir, bios_name);

    bios_size = get_image_size(buf);

    if (bios_size <= 0 ||

        (bios_size % 65536) != 0) {

        goto bios_error;

    }

    bios_offset = qemu_ram_alloc(bios_size);

    ret = load_image(buf, phys_ram_base + bios_offset);

    if (ret != bios_size) {

    bios_error:

        fprintf(stderr, "qemu: could not load PC BIOS '%s'/n", buf);

        exit(1);

    }

 

    if (cirrus_vga_enabled || std_vga_enabled || vmsvga_enabled) {

        /* VGA BIOS load */

        if (cirrus_vga_enabled) {

            snprintf(buf, sizeof(buf), "%s/%s", bios_dir, VGABIOS_CIRRUS_FILENAME);

        } else {

            snprintf(buf, sizeof(buf), "%s/%s", bios_dir, VGABIOS_FILENAME);

        }

        vga_bios_size = get_image_size(buf);

        if (vga_bios_size <= 0 || vga_bios_size > 65536)

            goto vga_bios_error;

        vga_bios_offset = qemu_ram_alloc(65536);

 

        ret = load_image(buf, phys_ram_base + vga_bios_offset);

        if (ret != vga_bios_size) {

vga_bios_error:

            fprintf(stderr, "qemu: could not load VGA BIOS '%s'/n", buf);

            exit(1);

        }

 

        /* setup basic memory access */

        cpu_register_physical_memory(0xc0000, 0x10000,

                                     vga_bios_offset | IO_MEM_ROM);

    }

 

    /* map the last 128KB of the BIOS in ISA space */

    isa_bios_size = bios_size;

    if (isa_bios_size > (128 * 1024))

        isa_bios_size = 128 * 1024;

    cpu_register_physical_memory(0x100000 - isa_bios_size,

                                 isa_bios_size,

                                 (bios_offset + bios_size - isa_bios_size) | IO_MEM_ROM);

………………………..

 

    /* map all the bios at the top of memory */

    cpu_register_physical_memory((uint32_t)(-bios_size),

                                 bios_size, bios_offset | IO_MEM_ROM);

………………………
}

这段代码按从低到高的顺序依次注册了几个内存块:

l         常规内存(Conventional Memory):系统内存的第一个640 KB就是著名的常规内存。它是标准DOS程序、DOS驱动程序、常驻内存程序等可用的区域,它们统统都被放置在00000h~9FFFFh之间。

l         上位内存区(Upper Memory Area):系统内存的第一个1M内存顶端的384 KB(1024 KB - 640 KB)就是UMA,它紧随在常规内存之后。也就是说,第一个1M内存被分成640KB常规内存和384KB的UMA。这个区域是系统保留区域,用户程序不能使用它。它一部分被系统设备(CGA、VGA等)使用,另外一部分被用做ROM shadowing和Drivers。UMA使用内存区域A0000h~FFFFFh。

l         扩展内存(Extended Memory):从0x100000到系统物理内存的最大值之间的区域都属于扩展内存。当一个OS运行在Protected Mode时,它可以被访问,而在Real Mode下,则无法被访问(除非通过某些Hacker方法)。

内存布局


本来扩展内存的第一个64K可以独立出来称之为HMA,但是从上面的代码可以看到,QEMU并没有将之单独列出来。

紧接着要模拟的物理内存之后,QEMU分配了8M的显存。

在显存之后,分配了一块空间给bios,而这段空间的内容则直接来自于bios.bin这一文件,QEMU提供的bios.bin大小为128K。

在bios之后,分配了64K的空间给vga bios,而这段的内容则来自于vgabios-cirrus.bin文件。

原创粉丝点击