Using Shared Memory

Shared memory blocks can be used for inter-client communication or simply to hold tables or subroutine libraries that are needed by more than one client. Explicit use of shared blocks is necessary because each VM has its own linear address space, and thus cannot inspect or modify the memory owned by a client in another virtual machine. The basic strategy for use of a shared memory block is as follows: Shared memory blocks are allocated with Int 31H Function 0D00H. The client passes the address of a data structure that specifies the ASCIIZ name and requested size of the shared block to the host; the host returns the block's handle, linear base address, and actual size in the same structure. The block's true size is determined by the first client to allocate the block, and the block will have the same linear address for all clients which allocate it.

After the shared block is allocated, the client must allocate one or more descriptors that will be used to address the block with Int 31H Function 0000H. Once descriptor(s) have been allocated and initialized to point to a shared memory block through separate LDT management calls, the client has the physical capability to read, write, or execute addresses within the block as allowed by the access rights/type byte. The client should synchronize with any other clients which might have addressability to the same block, to avoid race conditions or corruption of data. This synchronization is accomplished with Int 31H Function 0D02H (Serialize on Shared Memory) and Int 31H Function 0D03H (Free Serialization on Shared Memory). Serialization can be thought of as representing ownership or right of access to a shared memory block.

In essence, Int 31H Functions 0D02H and 0D03H treat the handle of a shared memory block as a semaphore. The client can request exclusive (read/write) or shared (read-only) serialization with Int 31H Function 0D02H, and the host will grant the serialization if no other client has already obtained a conflicting serialization on the same memory block. The client can then go ahead and manipulate the shared memory block, releasing the serialization with Int 31H Function 0D03H when it is finished using the block. If the Int 31H Function 0D02H serialization request fails, the client will either be suspended until the serialization is available, or the function will return with an error code, depending on the parameters supplied by the client.

The first paragraph (16 bytes) of the shared memory block (or the entire shared block, if smaller than 16 bytes) will always be initialized to zero on the first allocation and can be used by clients as an "area initialized" indicator. For example, a shared memory block might be used by a suite of cooperating client programs to hold a table of static data or a subroutine library. The first client to allocate the shared memory block can obtain exclusive ownership of the block with Int 31H Function 0D02H, load the necessary data or code into the block from disk, set the first 16 bytes of the block to a nonzero value, and finally release its ownership of the block with Int 31H Function 0D03H. Other clients that allocate the shared memory block can check the "area initialized" indicator and know that the desired code or data is already present in memory.

When a client has finished using the shared memory block, it should deallocate the shared block with Int 31H Function 0D01H. After the block is deallocated, the linear addresses within the block are no longer valid for the current client, and may cause an exception if accessed. However, the block is not actually destroyed until all clients which have allocated the block have also deallocated it.

Note that a client can make multiple (nested) allocation requests for the same shared memory block, and should assume that each allocation request will return a distinct handle. The shared block will remain physically accessible to the client until each of its handles to the block have been deallocated. Similarly, a client can make multiple serialization requests for the same block, and will retain "ownership" of the block until a corresponding number of deserialization requests have been issued. Lastly, allocation of zero-length shared memory blocks is explicitly allowed, so that clients can use the handles resulting from such allocations as pure semaphores.

Example: The following code illustrates how shared memory can be used to load code and data that can be used by more than one DPMI client. Note that no serialization calls are required if the memory is already initialized.

memreqstruc struc
length	dd	?			; number of bytes requested
actual	dd	?			; number of bytes allocated
handle	dd	?			; handle for shared memory block
base	dd	?			; linear address of shared block
nameptr	dp	?			; pointer to shared memory name
	dw	0			; reserved, must be zero
	dd	0			; reserved, must be zero
memreqstruc ends

memname	db	'MYBLOCK',0
memreq	memreqstruc <>			; allocate request block

	mov	word ptr memreq.length,2000h	; set reqeusted length
	mov	word ptr memreq.length+2,0	; of shared block to 8 KB
					; initialize nameptr
	mov	dword ptr memreq.nameptr, offset memname
	mov	word ptr memreq.nameptr+4, ds

	mov	di,ds			; ES:DI = address of shared
	mov	es,di			; memory request structure
	mov	di,offset memreq
	mov	ax,0d00h		; DPMI fxn 0D00H = allocate
	int	31h			; shared memory block
	jc	error			; jump if allocation failed

	mov	cx,1			; allocate one LDT descriptor
	mov	ax,0			; using DPMI Function 0000h
	int	31h
	jc	error			; jump, no descriptor available

	mov	bx,ax			; let BX = new selector
	mov	dx,word ptr memreq.base		; let CX:DX = linear base
	mov	cx,word ptr memreq.base+2	; address of shared block
	mov	ax,0007h		; set descriptor base address
	int	31h			; using DPMI Function 0007H
	jc	error			; jump, function failed

	mov	dx,word ptr memreq.actual	; set CX:DX = length-1
	mov	cx,word ptr memreq.actual+2	; of shared memory block
	sub	dx,0
	sbb	cx,0			; (BX still = selector)
	mov	ax,8			; set descriptor limit using
	int	31h			; DPMI Function 0008H
	jc	error			; jump, function failed

	mov	es,bx			; ES = selector for shared block
	mov	ax,es:[0]		; is block already initialized
	or	ax,ax
	jnz	@@1			; jump if it's initialized

					; not initialized, get ownership
					; of the shared memory block
	mov	di,word ptr memreq.handle	; SI:DI = handle for
	mov	si,word ptr memreq.handle+2	; shared memory block
	mov	dx,0			; exclusive + wait for ownership
	mov	ax,0d02h		; DPMI Fxn 0D02H = serialize
	int	31h
	jc	error			; jump if serialization failed

	mov	ax,es:	[0]		; check again if someone else
	or	ax,ax			; already initialized block
	jnz	@@2			; jump if it's initialized
	.				; load code into the shared
	.				; memory block here...
@@2:					; now release ownership of
					; the shared memory block
	mov	di,word ptr memreq.handle	; SI:DI = handle for
	mov	si,word ptr memreq.hanlde+2	; shared memory block
	mov	dx,0			; serialization type = exclusive
	mov	ax,0d03h		; DPMI Fxn 0D03H = release
	int	31h
	jc	error			;  jump if serialization failed

@@1:					; finished initializing the
					; shared memory block

  webmaster     delorie software   privacy  
  Copyright © 1999     Updated Feb 1999