Wiley.Symbian.OS.Internals.Real.time.Kernel.Programming.Dec.2005.eBook-DDU (779891), страница 65
Текст из файла (страница 65)
Here is the relevant part of this class API:class User : public UserHeap{public:static TInt AllocLen(const TAny*);static TAny* Alloc(TInt);static TAny* AllocL(TInt);static TAny* AllocLC(TInt);static TAny* AllocZ(TInt);static TAny* AllocZL(TInt);static TInt AllocSize(TInt&);static TInt Available(TInt&);PROGRAMMER APIsstaticstaticstaticstaticstaticstaticstaticstatic};305TInt CountAllocCells();TInt CountAllocCells(TInt&);void Free(TAny*);void FreeZ(TAny*&);TAny* ReAlloc(TAny*, TInt, TInt);TAny* ReAllocL(TAny*, TInt, TInt);RAllocator& Allocator();RAllocator* SwitchAllocator(RAllocator*);We can see the almost one-to-one correspondence of this API with theAPI provided by RAllocator.
The User class implements all of thesefunctions in the same way: get the current allocator object and invokethe corresponding member function.It is possible to replace the current allocator with an alternative oneusing the User::SwitchAllocator() function, which returns theprevious thread allocator object. There are several reasons that this maybe desirable, for example:• Replacing the default allocator provided by the OS with one that usesa different allocation strategy better suited to the application• Adding an adaptor to the allocator to provide additional instrumentation or debugging facilities. In this case, the new allocator willcontinue to use the previous allocator for the actual memory allocation but can intercept the actual allocation and de-allocation requeststo do additional processing.7.5.2.4 RHeap – the default allocatorSymbian OS provides a single allocator implementation, RHeap, providing a low memory overhead and generally good performance.
Thesame approach is used for both the user-mode free store, and the kernelfree store. One can describe this allocator as a ‘‘first fit, address ordered,free list allocator’’. It is a simple data structure, and the allocation andde-allocation algorithms are fairly straightforward.RHeap supports different usage models:• Using preallocated memory to provide a fixed size heap, or using achunk to provide a dynamically sized heap• Single-threaded or multi-threaded with light-weight locks• Selectable cell alignment.A dynamic RHeap uses a normal chunk, and so has a single region ofcommitted memory. Within that region, there will be both allocated andfree blocks. Each block is preceded by a 32-bit word which describes thelength of the block.
The allocator does not need to track the allocated306MEMORY MODELSblocks, as it is the program’s responsibility to do this and later free them.The allocator does need to keep track of all the free blocks: it does thisby linking them into a list – the ‘‘free list’’. The allocator uses the spacewithin the free block (the first word) to maintain this list.Free blocks that are neighbors in memory are coalesced into a singlefree block, so at any time the heap consists of a repeated pattern of oneor more allocated blocks followed by a single free block.
The free listis a singly linked queue maintained in address order – this enables thede-allocation algorithm to easily identify if the block being released is adirect neighbor of a block that is already free.The allocation algorithm searches the free list from the start until ablock is found that is large enough to satisfy the request.
If the allocatorfinds such a block, the allocator splits the free block into the requestedallocated block and any remaining free space, which is kept on the freelist. Sometimes the block is only just large enough for the request (or theremaining space is too small to keep on the free list) in which case thewhole block is returned by the request. If there is no free block largeenough, the allocator tries to extend the chunk to create a larger freeblock at the end of the heap to satisfy the request.The de-allocation algorithm searches the free list to find the last freeblock before the block being released and first one after it. If the blockbeing released is a neighbor of either or both of these free blocks theyare combined, otherwise the released block is just added into the listbetween these two free ones.These algorithms are simple and so in general performance is fast.However, because both algorithms require the searching of an arbitrarylength list, the performance is cannot be described as ‘‘real-time’’.
However, this is no worse than the memory model allocation for adjustingchunks, which also does not have real-time behavior.One drawback with this data structure is that large free blocks thatlie inside the heap memory are not released back to the OS – theallocator can only release free memory that lies at the very end ofthe heap.
However, the data structure has a very low memory overheadin general – approximately 4 bytes per allocated cell – though alignmentrequirements for modern compilers increase this to approximately 8 bytes.So RHeap, despite its limitations, provides an excellent general purposeallocator for almost all applications within the OS. When better executionor memory performance is required, you can create custom allocators forindividual applications.7.5.3 Shared memoryIn many cases, when an application must pass data across some memorycontext boundary, such as between two processes or between user andkernel contexts, it is most convenient to copy the data. This can be done inPROGRAMMER APIs307a controlled manner that ensures the data being transferred belongs to thesending memory context – and errors are reported correctly rather thancausing the wrong program to terminate. However, when the amount ofdata to be transferred is large, or lower delays in transfer are required,it is more useful to be able to transfer the memory itself rather thancopy the data.
Some examples of such use cases would be streamingmultiple channels of high bit-rate audio data to a software ‘‘mixer’’ ordownloading large files over USB.For any situation in which we need to share memory between two usermode processes, we could use one or more global chunks to achieve this.Chunks can also be accessed by kernel-mode software or even directlyby DMA. There is a problem with this approach, however.The chunks that I have described so far have the property that memoryis dynamically committed and released from the chunk at the requestof user-mode software.
For example, the kernel grows the heap chunkto satisfy a large allocation request, or releases some stack pages inresponse to thread termination. So you can see that it is possible thata page currently being accessed by a kernel thread or DMA might beunmapped by another thread – probably resulting in a system crash.The case of unmapping memory during DMA is particularly difficult todiagnose because DMA works with the physical address and will continueaccessing the physical memory: the defect may only be discovered afterthe memory model reassigns the memory to another process and thensuffers from ‘‘random’’ memory corruption.To support the sharing of memory between hardware, kernel threadsand user programs, we need different types of memory object.7.5.3.1 Shared I/O buffersThe simplest of these objects is the shared I/O buffer.
Kernel software,such as a device driver, can allocate a shared IO buffer to a fixed size, andmay subsequently map and unmap the buffer from user process addressspace.The major limitation with these buffers is that they cannot be mappedinto more than one user-mode process at the same time. These aresupported in EKA1, but have been superseded in EKA2 by the morepowerful shared chunk. As a result of this, we deprecate use of sharedI/O buffers with EKA2.7.5.3.2 Shared chunksA shared chunk is a more complex, though more capable, shared memoryobject and can be used in almost all memory sharing scenarios.
Itis very much like the global, disconnected chunk that I described inSection 7.3.1, but with one distinct difference: memory can only becommitted and released by kernel code and not by user code.308MEMORY MODELSA shared chunk is likely to be the answer if you are solving a problemwith some of the following demands:• The memory must be created and controlled by kernel-mode software• The memory must be safe for use by ISRs and DMA• The memory can be mapped into multiple user processes at the sametime• The memory can be mapped into multiple user processes in sequence• The memory object can be transferred by user-side code betweenprocesses or to another device driver.A device driver can map a shared chunk into multiple user processes,either in sequence or simultaneously.
In addition, the driver can providea user program with an RChunk handle to the chunk. This allows theuser program to transfer the chunk to other processes and even hand it toother device drivers without the support of the device driver that createdit originally.See Chapter 13, Peripheral Support, for a fuller description of howshared chunks can be used by device drivers.7.5.3.3 Global and anonymous chunksAs I have already mentioned, global chunks provide the most flexibleway of sharing memory between user-mode processes. An anonymouschunk is a global chunk with no name; this restricts the discovery andaccess of the chunk from other processes. However, the limited value ofanonymous chunks for sharing between kernel and user software has alsobeen highlighted.A global chunk is likely to be the solution if you are solving a problemwith some of the following demands:• The memory must be created and controlled by user-mode software• The memory is not accessed directly from DMA/ISR• The memory can be mapped into one or more user processes at thesame time.I will again point out that opening a shared chunk in two processes atthe same time does not always guarantee that they will share the sameaddress for the data.
In fact, closing a chunk and re-opening it at a laterpoint within a single program may result in a different base address beingreturned!MEMORY ALLOCATION3097.5.3.4 Publish and subscribeThere are, of course, other reasons for wanting to share memory, suchas having some data that is global to the whole OS. In this case it is theuniversal access to the data and not the reduction in copying overheadthat drives the desire for sharing.Once again, a global chunk might serve this purpose on someoccasions.