• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • Windows 95/98下直接訪問物理內存

    發表于:2007-07-14來源:作者:點擊數: 標簽:
    在很多情況下,我們都有直接訪問物理內存的要求,如在實時高速數據采集系統中, 對I/O板上配置的存儲器的訪問。但是,為了保證系統的 安全 性和穩定性,操作系統 并不提倡應用程序直接訪問硬件資源, 因此,隨著操作系統的進步,導致了目前存在 的這樣一個不幸的事實
    在很多情況下,我們都有直接訪問物理內存的要求,如在實時高速數據采集系統中,
    對I/O板上配置的存儲器的訪問。但是,為了保證系統的安全性和穩定性,操作系統
    并不提倡應用程序直接訪問硬件資源, 因此,隨著操作系統的進步,導致了目前存在
    的這樣一個不幸的事實: 以前在DOS下很容易實現的特定物理內存的讀寫操作,在Windows
    下卻變得相當困難。
    本文主要討論如何在Windows 95/98下實現物理內存的直接讀寫操作。為了論述清
    楚這個問題,有必要敘述保護模式的尋址方式以及W indows 95/98的內存管理方式。
    Windows 95/98內存管理方式
    Windows 95/98工作在32位保護模式下,保護模式與實模式的根本區別在于CPU尋址方
    式上的不同:盡管兩者對應的內存地址均為"段地址:偏移量"形式,但在保護模式下,
    "段地址"代表的值已不再是實模式中段的起始基準地址了;對于CS、DS、ES、SS寄存
    器,在實模式下,這些寄存器的值左移4位,再加上偏移量,即得到物理地址,而在保護
    模式下,這些寄存器的值為"段選擇符",它實際上是一個查全局描述符表(G DT)或局
    部描述符表(LDT)的索引,據此在GDT或LDT找到對應的段描述符,從而獲得段的基址及
    類型等信息,再根據偏移量,才能得到線性地址。如果操作系統沒有采用分頁機制,
    那么得到的線性地址即為物理地址,否則,線性地址需要進一步經過分頁機制才能得
    到物理地址。這就是保護模式下的"段頁式尋址機制"。
    Windows 95/98使用4GB的虛擬內存地址空間,應用程序訪問內存使用虛擬地址,從虛
    擬地址到物理地址的轉換過程如圖1所示: 圖1 虛擬地址到物理地址的轉化過程
    對于圖1中的分頁機制,Windows 95/98采用兩級頁表結構,如圖2 所示。圖2 采用的
    分頁機制的兩級頁表結構
    從圖2可知,線性地址被分割成頁目錄條目(PDE)、頁表條目(PTE) 、頁偏移地址(Off set)
    三個部分。當建立一個新的WIN 32進程時,Wi ndows 95/98會為它分配一塊內存,并
    建立它自己的頁目錄、頁表,頁目錄的地址也同時放入進程的現場信息中。當計算一
    個地址時,系統首先從控制寄存器CR3中讀出頁目錄所在的地址(該地址為物理地址,
    并且是頁對齊的),然后根據PDE得到頁表所在的地址,再根據PTE得到包含了實際Code
    或Data的頁幀, 最后根據Offset訪問頁幀中的特定單元。
    常用內存段的段選擇符
    從上述所介紹的Windows 95/98采用的分段、分頁機制可看出,要想在Windows 95/98
    下直接訪問物理內存,關鍵是得到欲訪問物理內存所在的內存區域對應的段選擇符。
    一般說來,要求直接訪問的物理內存都與實模式下能夠尋址的內存有關(即DOS能直
    接訪問的1M物理內存)。在Windows 3.X中,Microso ft給出了DOS常用段的段選擇符,
    如_000 0H(未公開),_B800H,_F000H( 已公開),等等,均可以在KERNEL中找到,應用
    程序可以直接使用這些段選擇符,實現物理內存的直接訪問。而在Windows 95/98中,
    Microsoft 卻不在任何文檔中提供這些段的預定義,在KERNEL中也不提供相應的段選
    擇符。但是,Windows 95 /98確實給DOS下的這些常用內存段定義了相應的段描述符。
    通過SoftIce 3.02 for Win dows 95/98,我們得到了關于LDT的如下信息:
    ...... :ldt
    LDTbase=80003000 Limit=3FFF
    ……
    1007 Data16 00000C90 0000FFFF 3 P RW
    100F Data16 00000000 0000FFFF 3 P RW
    1017 Data16 00000400 0000FFFF 3 P RW
    101F Data16 000F0000 0000FFFF 3 P RW
    1027 Data16 000A0000 0000FFFF 3 P RW
    102F Data16 000B0000 0000FFFF 3 P RW
    1037 Data16 000B8000 0000FFFF 3 P RW
    103F Data16 000C0000 0000FFFF 3 P RW
    1047 Data16 000D0000 0000FFFF 3 P RW
    104F Data16 000E0000 0000FFFF 3 P RW
    ……
    其中,每一行對應一個段描述符,第一欄為其段選擇符,第二欄為段描述符的類型,
    第三欄為段的基地址(線性地址),第四欄為段的限長 ,第五欄為段描述符的特權級,
    第六欄標志對應段是否存在于內存中, 第七欄表示段的訪問權限。
    可以看出,這些段的基地址與DOS下的常用內存段完全吻合,并且均為16位的數據段,
    限長為64K(0XFFFF),供應用程序訪問,都存在于內存中,可讀寫。實踐證明,這些段
    就是D OS的常用內存段,也就是說, 這里的線性地址即為物理地址。因此,可以用這
    些段選擇符對相應的物理內存進行訪問。
    從程序運行的健壯性考慮,不應該直接應用上述段選擇符,而應該用GetThreadSelec
    torEntry()函數得到欲訪問物理內存對應的段選擇符,該API函數的原型定義為


        BOOL GetThreadSelectorEntry (
        HANDLE hThread,
        // handle of thread that contains selector
        DWORD dwSelector,
        // number of selector value to look up
        LPLDT_ENTRY lpSelectorEntry
        // address of selector entry structure
        );
        其中,LDT_ENTRY的結構定義如下
        typedef struct _LDT_ENTRY { // ldte
        WORD LimitLow;
        WORD BaseLow;
        union {
        struct {
        BYTE BaseMid;
        BYTE Flags1;
        BYTE Flags2;
        BYTE BaseHi;
        } Bytes;
        struct {
        DWORD BaseMid : 8;
        DWORD Type : 5;
        DWORD Dpl : 2;
        DWORD Pres : 1;
        DWORD LimitHi : 4;
        DWORD Sys : 1;
        DWORD Reserved_0 : 1;
        DWORD Default_Big : 1;
        DWORD Granularity : 1;
        DWORD BaseHi : 8;
        } Bits;
        } HighWord;
        } LDT_ENTRY, *PLDT_ENTRY;
        用下面的代碼可以得到基地址為BASE_DESIRED,限長為0XFFFF的
    內存段對應的段選擇符:
        ......
        extern CLDTApp theApp;
        WORD wSelector; // 內存段對應的段選擇符
        LDT_ENTRY ldtEntry;
        DWORD base, baseMid, baseHigh;
        DWORD limit, limitHigh;
        for ( WORD sel = 7; sel <= 0xffff; sel +=8 ) {
        if (::GetThreadSelectorEntry ( theApp.m_hThread,
        DWORD ( sel ), &ldtEntry ) ) {
        baseMid = ldtEntry . HighWord . Bytes . BaseMid;
        baseMid <<= 16;
        baseHigh = ldtEntry . HighWord . Bytes . BaseHi;
        baseHigh <<= 24;
        base = ldtEntry . BaseLow + baseMid +
         baseHigh;
        limitHigh = m_ldtEntry . HighWord . Bits . LimitHi;
        limitHigh <<= 24;
        limit = limitHigh + m_ldtEntry . LimitLow;
        if ( 0xFFFF == limit )
        if ( BASE_DESIRED == base ) {
        // BASE_DESIRED為內存段對應的基地址
        wSelector = sel;
        break; }}}
    直接訪問物理內存的實現
    得到了段選擇符之后,即可把該段選擇符置于相應的段寄存器中( 不能用CS,DS),
    用該寄存器進行數據訪問。需注意的是,任何非法段選擇符寫入段寄存器將會導
    致通用保護錯誤(General Protection Faul t)。
    下面的代碼實現物理內存的讀/寫操作(段選擇符用上述方法得到):



    void WriteMemory(WORD sel, DWORD dwOffset, const char * str, UINT length)
    {
        char cWrite;
        for ( UINT i = 0; i < length; i ++ )
        {
         cWrite = str [i];
         _asm {     push es     mov ax, sel     mov es, ax
        mov ebx, dwOffset     mov al, cWrite     mov byte ptr es:[ebx], al
        inc dwOffset     pop es
              }
        }
    }


    void ReadMemory ( WORD sel, DWORD dwOffset,char * str, UINT length )
    {
        char cRead;
        for ( UINT i = 0; i < length; i ++ ) {
        _asm {
            push es
            mov ax, sel
            mov es, ax
            mov ebx, dwOffset
            mov al, byte ptres:[ebx]
            mov cRead, al
            inc dwOffset
            pop es
        }
          str [i] = cRead;
        }
    }
        本文所用操作系統為中文Windows 95 OSR 2.0以及中文Windows
    98,編程環境為Vis ual C++ 5.0。

    原文轉自:http://www.kjueaiud.com

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>