virtmem.c

来自「开放源码的编译器open watcom 1.6.0版的源代码」· C语言 代码 · 共 674 行 · 第 1/2 页

C
674
字号
/****************************************************************************
*
*                            Open Watcom Project
*
*    Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
*  ========================================================================
*
*    This file contains Original Code and/or Modifications of Original
*    Code as defined in and that are subject to the Sybase Open Watcom
*    Public License version 1.0 (the 'License'). You may not use this file
*    except in compliance with the License. BY USING THIS FILE YOU AGREE TO
*    ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
*    provided with the Original Code and Modifications, and is also
*    available at www.sybase.com/developer/opensource.
*
*    The Original Code and all software distributed under the License are
*    distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
*    EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
*    ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
*    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
*    NON-INFRINGEMENT. Please see the License for the specific language
*    governing rights and limitations under the License.
*
*  ========================================================================
*
* Description:  Virtual memory support implementation.
*
****************************************************************************/


#include <stdlib.h>
#include <string.h>
#include "drpriv.h"
/*
 * this virtual memory system has been set up in such a way that when
 * running under a 32-bit operating system with effective paging, the
 * vm read routines can be replaced with a simple pointer dereference,
 * and the vm alloc can be replaced with a call to dwralloc
*/

/* the following structures are for virtual memory allocation */

typedef struct {
    char        *mem;
    unsigned_8  sect:4;         // the type of section this page is in
    unsigned_8  refd:1;         // true if page not referenced
    unsigned_8  inmem:1;        // true if page in memory
} page_entry;

/* the page_entrys are referenced by an array of pointers, something like
PageTab --> 0 1 2 3 4 5 6 ...       <-- array elements are pointers
            | | | | | | |
            v v v v v v v
            1 1 1 1 1 1 1    <- array elements are page_entrys
            2 2 2 2 2 2 2   these arrays are referred to as "branches",
            3 3 3 3 3 3 3   and the individual elements are called "leaves".

in other words, it is effectively a dynamically allocated 2 dimensional array.
All virtual memory locations are split into 4K pages.

a virtual memory address is split into three parts:
offset                   leaf id   branch id
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   12 bits             | 4 bits|  16 bits                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 low order word                 high order word.
the leaf id chooses which element of the branch is the correct seg_table.
Note that the branch id can only reach about 16200 due to only being able
to allocate < 64k hunks of memory.
*/

// this structure is used for picking the high order word off a long

#ifdef __BIG_ENDIAN__
typedef struct wordpick {
    unsigned_16 high;
    unsigned_16 low;
} wordpick;
#else
typedef struct wordpick {
    unsigned_16 low;
    unsigned_16 high;
} wordpick;
#endif

// this is used instead of the virt_mem type inside this module, since it is
// desirable to be able to get the high order word without having to do a
// 16-bit shift. MAKE SURE THAT VIRT_MEM IS THE SAME SIZE AS THIS STRUCTURE!
typedef union {
    unsigned_32     l;
    wordpick        w;
} virt_struct;

#define OFFSET_SHIFT      12
#define MAX_NODE_SIZE     (1U << OFFSET_SHIFT)
#define MAX_LEAFS         16            // maximum # of leafs per branch
#define SEG_LIMIT         16200         // maximum # of 4K pages.

/* find the node for MEM_ADDR or FILE_ADDR */
#define NODE( stg )        (&PageTab[ stg.w.high ][ stg.w.low >> OFFSET_SHIFT ])

#define OFFSET_MASK        (MAX_NODE_SIZE - 1)
#define NODE_OFF( stg )    ( stg.w.low & OFFSET_MASK )

static page_entry       **PageTab   = NULL;
static unsigned_16      NumBranches = 15;
// start with branch # 1 so an address of zero can be illegal.
static unsigned_16      CurrBranch = 1;
static unsigned_16      NextLeaf = 0;   // next leaf # to be allocated.
static unsigned_16      SwapBranch = 1; // next branch # to be swapped.
static unsigned_16      SwapLeaf = 0;   // next leaf # to be swapped
static bool             DontSwap = TRUE; // TRUE if we can't swap now.
static unsigned_16      PageCount;

extern void DWRVMInit( void )
/***************************/
// Allocate space for the branch pointers.
{
    PageTab = DWRALLOC( NumBranches * sizeof( page_entry * ) );
    memset( PageTab, 0, NumBranches * sizeof( page_entry * ) );
    PageTab[1] = DWRALLOC( sizeof( page_entry ) * MAX_LEAFS );
    memset( PageTab[1], 0, sizeof( page_entry ) * MAX_LEAFS );
    DontSwap = FALSE;
    PageCount = 0;
}

extern void DWRVMReset( void )
/****************************/
// Reset VM state without having to destroy/init
// NOTE: This will ensure that allocations start from the lowest VM
// address again, without actually freeing the memory that we have
// allocated. Any existing memory will be reused.
{
    CurrBranch = 1;
    NextLeaf = 0;
}

static void GetMoreBranches( void )
/*********************************/
// make a larger array to hold branch pointers in.
{
    page_entry      **branches;
    unsigned        alloc_size;

    alloc_size = NumBranches * sizeof( page_entry * );
    NumBranches = NumBranches * 2;   // double the # of pointers.
    if( NumBranches > SEG_LIMIT ) {
        DWREXCEPT( DREXCEP_OUT_OF_VM );
    }
    branches = DWRALLOC( alloc_size * 2 );
    memcpy( branches, PageTab, alloc_size );
    memset( (char *)branches + alloc_size, 0, alloc_size ); // null pointers
    DWRFREE( PageTab );
    PageTab = branches;
}

static virt_struct GetPage( dr_section sect )
/*******************************************/
{
    page_entry      *entry;
    unsigned        alloc_size;
    virt_struct     vmem;

    if( NextLeaf >= MAX_LEAFS ) {
        DontSwap = TRUE;
        NextLeaf = 0;
        CurrBranch++;
        if( CurrBranch >= NumBranches ) {
            GetMoreBranches();
        }
        alloc_size = sizeof( page_entry ) * MAX_LEAFS;
        entry = DWRALLOC( alloc_size );
        PageTab[ CurrBranch ] = entry;
        memset( entry, 0, alloc_size ); //set all flags FALSE.
        DontSwap = FALSE;
    } else {
        entry = &PageTab[ CurrBranch ][ NextLeaf ];
    }
    entry->sect = sect;
    vmem.w.high = CurrBranch;
    vmem.w.low = NextLeaf << OFFSET_SHIFT;
    NextLeaf++;
    return( vmem );
}

extern dr_handle DWRVMAlloc( unsigned long size, int sect )
/*********************************************************/
{
    virt_struct ret;

    ret.l = 0;          // assume 0 not a valid vm address
    if( size > 0 ) {
        ret = GetPage( sect );
        while( size > MAX_NODE_SIZE ) {
            size -= MAX_NODE_SIZE;
            GetPage( sect );
        }
    }
    return( ret.l );
}

extern void DWRVMFree( dr_handle hdl )
/************************************/
{
    hdl = hdl;
}

extern int DRSwap( void )
/***********************/
// this uses the second-chance cyclic algorithm for page replacement.
// NOTE: this tends to degenerate into FIFO under very tight memory
// requirements, which is rather bad for the current usage.  Any better ideas?
{
    unsigned_16         startbranch;
    unsigned_16         startleaf;
    page_entry          *entry;
    bool                passtwo;

    if( DontSwap ) return( FALSE );
    passtwo = FALSE;
    startbranch = SwapBranch;
    startleaf = SwapLeaf;
    for( ;; ) {
        SwapLeaf++;
        if( SwapLeaf >= MAX_LEAFS ) {
            SwapLeaf = 0;
            SwapBranch++;
            if( SwapBranch > CurrBranch ) {
                SwapBranch = 1;
            }
        }
        entry = &PageTab[SwapBranch][SwapLeaf];
        if( entry != NULL ) {
            if( entry->refd ) {
                entry->refd = 0;
            } else if( entry->inmem ) {
                DWRFREE( entry->mem );
                PageCount--;
                entry->inmem = 0;
                return( TRUE );
            }
        }
        if( SwapLeaf == startleaf && SwapBranch == startbranch ) {
            if( passtwo ) break;        // nothing to swap;
            passtwo = TRUE;
        }
    }
    return( FALSE );
}

extern void DWRVMDestroy( void )
/******************************/
/* this frees all virtual memory */
{
    unsigned        branch;
    unsigned        leaf;
    page_entry      *entry;

    DontSwap = TRUE;
    if( PageTab == NULL ) return;
    for( branch = 1; branch < NumBranches; branch++ ) {
        entry = PageTab[ branch ];
        if( entry != NULL ) {
            for( leaf = 0; leaf < MAX_LEAFS; leaf++ ) {
                if( entry->inmem ) {
                    DWRFREE( entry->mem );
                }
                entry++;
            }
            DWRFREE( PageTab[branch] );
        }
    }
    DWRFREE( PageTab );
}

static void ReadPage( page_entry * node, virt_struct vm )
/*******************************************************/
/* read a page in from the dwarf file */
{
    unsigned long size;
    unsigned long base;
    unsigned long offset;
    dr_section    sect;

    sect = node->sect;
    size = DWRCurrNode->sections[sect].size;
    base = DWRCurrNode->sections[sect].base;
    offset = (vm.l - base) & ~((unsigned long)OFFSET_MASK);
    size -= offset;
    if( size > MAX_NODE_SIZE ) {
        size = MAX_NODE_SIZE;
    }
    node->mem = DWRALLOC( size );
    node->inmem = TRUE;
    ++PageCount;
    DWRSEEK( DWRCurrNode->file, sect, offset );
    DWRREAD( DWRCurrNode->file, sect, node->mem, size );
}

extern void DWRVMSwap( dr_handle base, unsigned_32 size, int *ret )
/*****************************************************************/
// Swap out base for length size
// If memory was freed set *ret
{
    volatile virt_struct         vm; // cg bug workaround
    page_entry          *entry;
    int                 ret_val;

    vm.l = base;
    ret_val = FALSE;
    if( size > 0 ) {
        for( ;; ) {
            entry = NODE( vm );
            entry->refd = 0;
            if( entry->inmem ) {
                --PageCount;
                DWRFREE( entry->mem );
                entry->inmem = 0;
                ret_val = TRUE;
            }
            if( size <= MAX_NODE_SIZE ) break;
            size -= MAX_NODE_SIZE;
            vm.l += MAX_NODE_SIZE;
        }
    }
    if( ret_val == TRUE ) {
        *ret = ret_val;
    }
}

extern int DWRVMSectDone( dr_handle base, unsigned_32 size )
/**********************************************************/
{
    int ret;

    DWRVMSwap( base, size, &ret );

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?