![]() |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
AROS Application Development ManualAvis This document is not finished! It is highly likely that many parts are out-of-date, contain incorrect information or are simply missing altogether. If you want to help rectify this, please contact us. exec.libraryTypesIn exec/types.h the following short-cuts are typedef'd. They are used often in AROS, so you should nearly always include exec/types.h.
IPTRThere is another important typedef, IPTR. It is really important in AROS, as it the only way to declare a field that can contain both: an integer or a pointer. Note AmigaOS does not know this typedef. If you are porting a program from AmigaOS to AROS, you have to search your source for occurences of ULONG that can also contain pointers, and change them into IPTR. If you don't do this, your program will not work on systems which have pointers with more than 32 bits (for example DEC Alphas that have 64bit pointers). BPTRThe so-called BPTR`s were always a problem in AmigaOS and this problem was inherited by AROS. In binary-compatible AROS versions a `BPTR is in fact the fourth of the real pointer. If, for example, a pointer points to address $80000 the BPTR pointing to the same address would contain $20000. On systems without binary-compatibility, a BPTR is equal to an APTR. To convert between a normal pointer and a BPTR use the macros: #include <dos/bptr.h> APTR BADDR( BPTR bptr ); BPTR MKBADDR( APTR ptr ); There also exists something called BSTR which is a special kind of string. We will not discuss this here, though, because it is used only very rarely. HistoryWhen the development of the Amiga started, it was designed as a pure module-based games-console. As such it didn't need any means of filesystem handling. The OS was created without it in mind. But Commodore, who bought the Amiga, wanted a full-fletched home-computer instead of another games-platform. So, a short time before the Amiga's initial presentation, a filesystem was needed. Instead of wasting time in developing a custom one, the filesystem of an operating systm called TRIPOS was ported to the Amiga. Unfortunately TRIPOS was written in BCPL, a programming language with a quite eccentric pointer handling. This pointer handling was inherited by the AmigaDOS and later by AROS (even though later versions of AmigaOS and also AROS are written in C). Exec lists and memory managementExec listsAROS implements a system of linked lists, so-called exec lists. A linked-list consists of a number of nodes that point to each other. Two types of nodes are defined in exec/nodes.h:
Both structures can be embedded into other structures. For example struct Library (defined in exec/libraries.h) contains a struct Node at the beginning. This way all libraries can be contained in a list. The field ln_Name points to the name of the library, ln_Type is set to NT_LIBRARY to show that this node is a library and ln_Pri reflects the importance of a library. Of course, we need a list containers. These are defined in exec/lists.h. Like nodes, we have two different kind of lists:
MinList's take MinNode's as members, while List's use Node's. They are not interchangeable. While it's technically possible to use Node's in MinList's, you loose all their advantages. FIXME: Macros List Manipulating Functionsexec.library and the link-library amiga.lib contain some functions for manipulating exec lists. Before a list can be used, it must be initialized, using the amiga.lib function: #include <proto/alib.h> void NewList( struct List *list ); Nodes can be added to lists with the exec.library functions: #include <proto/exec.h> void AddHead( struct List *list, struct Node *node ); void AddTail( struct List *list, struct Node *node ); void Enqueue( struct List *list, struct Node *node ); void Insert( struct List *list, struct Node *node, struct Node *pred ); With AddHead() and AddTail() node is inserted at the beginning or the end of list respectively. Enqueue() inserts node according to its ln_Pri field. A node can be inserted after another by using Insert(). A pointer to the node that is to predecess node must be provided as pred. Nodes can be removed using the exec.library functions: #include <proto/exec.h> void Remove( struct Node *node ); struct Node *RemHead( sruct List *list ); struct Node *RemTail( struct List *list ); While RemHead() and RemTail() remove the first or last node of a list respectively and return a pointer to it, Remove() removes node from whatever list it is in. Of course, all list functions (except Enqueue()) can process struct MinList and struct MinNode's, too. A list can be searched for a named node, using: #include <proto/exec.h> struct Node *FindName( struct List *list, STRPTR name ); name is a pointer to a string that is to be compared with the ln_Name of the nodes in list. The comparison is case-sensitive! If name matches any ln_Name field, a pointer to the corresponding node is returned. If no field matches, NULL is returned. Note A list used with FindName() must not contain any struct MinList entries. If it does, memory could get corrupted! In the following example, we create a list, add three nodes to it, search a named node and then remove it:
#include <proto/alib.h>
#include <proto/exec.h>
#include <exec/types.h>
#include <exec/lists.h>
#include <exec/nodes.h>
#include <dos/dos.h> /* For RETURN_OK */
struct List list;
/* Our nodes */
struct Node node1 =
{
NULL, NULL, /* No predecessor and successor, yet */
NT_UNKNOWN, 0, /* Unknown type, priority ignored */
"First node" /* Name of the node */
};
struct Node node2 =
{
NULL, NULL,
NT_UNKNOWN, 0,
"Second node"
};
struct Node node3 =
{
NULL, NULL,
NT_UNKNOWN, 0,
"Third node"
};
int main(int argc, char *argv[])
{
struct Node *node;
/* Prepare the list for use. */
NewList(&list);
/* Add the first two nodes at the end of the list. */
AddTail(&list, &node1);
AddTail(&list, &node2);
/* Insert the third node after the first node. */
Insert(&list, &node3, &node1);
/* Find the second node */
node = FindName(&list, "Second node");
/*
If the node was found (which is always the case in this example),
remove it.
*/
if (node)
Remove(&node);
return RETURN_OK;
}
MacrosAROS defines a couple of macros in various header files. All macros cast their parameters to the correct type, so you must provide a valid input but can safe the casts (macros are meant to make life more simple).
Memory HandlingYou need memory for nearly everything in a program. Many things can be done by using the stack. But often you need larger chunks of memory or don't want to use the stack for some reason. In these cases you have to allocate memory by yourself. exec.library provides different methods for allocating memory. The two most important functions are: #include <proto/exec.h> APTR AllocMem( ULONG size, ULONG flags ); APTR AllocVec( ULONG size, ULONG flags ); Both functions return a pointer to a memory area of the requested size provided as argument. If not enough memory was available, NULL is returned, instead. You must check for this condition, before using the memory. If the memory was successfully allocated, you can do with it whatever you want to. You can provide additional flags to get a special kind of memory. The following flags are defined in exec/memory.h:
Memory allocated with these functions must be freed after use with one of the following functions. Note well that you must not use memory that was already freed.: #include <proto/exec.h> void FreeMem( APTR memory, ULONG size ); void FreeVec( APTR memory ); Of course, FreeMem() must be used for memory allocated with AllocMem() and FreeVec() for memory allocated with AllocVec(). The synopsis for these two functions shows the difference between AllocMem() and AllocVec(): AllocVec() remembers the size of the chunk of memory, it allocated. So, if you use AllocVec(), you don't have to store the requested size, while you have to, if you use AllocMem(). Allocating Multiple Regions of Memory at onceSometimes you may want to make multiple memory allocations at once. The usual way to do this is calling AllocVec() with the size of all memory-blocks added and then making pointers relative to the returned pointer. But what do you do, if you need memory of different kinds, it with different MEMF_ flags set? You could make multiple allocations or simply use the function: #include <proto/exec.h> struct MemList *AllocEntry( struct MemList *oldlist ); As you will have noticed, AllocEntry() uses a pointer to a struct MemList as only argument and as result. We find the definition of this structure in exec/memory.h:
struct MemEntry
{
union
{
ULONG meu_Reqs;
APTR meu_Addr;
} me_Un;
ULONG me_Length;
};
struct MemList
{
struct Node ml_Node;
UWORD ml_NumEntries;
struct MemEntry ml_ME[1];
};
The array ml_ME of struct MemList has a variable number of elements. The number of its elements is set in ml_NumEntries. The struct MemEntry describes one memory-entry. Stored are its size (me_Length), its requirements (i.e. the MEMF_, set in me_Un.meu_Reqs) and possibly a pointer to the memory-block (me_Un.meu_Addr). The struct MemList, you pass in as oldlist, must have set the field ml_NumEntries to the actual number of struct MemEntry`s contained in ml_ME. The struct MemEntry`s must have set the fields me_Length and me_Un.meu_Reqs. The other fields are ignored. The function returns a pointer to a copy of the struct MemEntry, passed in as oldlist, with all the relevant fields set (especially me_Un.meu_Addr). An error is indicated by setting the most significant bit of the pointer returned. So you always have to check it, before using the pointer returned. Memory allocated with AllocEntry() must be freed using FreeMem(). Memory PoolsAROS manages different so-called memory-pools. Each memory-pool contains a list of memory-areas. The most important memory-pool is the pool that contains all free memory in the system. But you also can create memory-pools yourself. This has some advantages:
Before a memory-pool can be used, it must be created. This is performed by the function: #include <proto/exec.h> APTR CreatePool( ULONG flags, ULONG puddleSize, ULONG threshSize ); The flags specifies the type of memory you want to get from the AllocPooled() function . All MEMF_ definitions as described above are allowed here. puddleSize is the size of the chunks of memory that are allocated by the pool functions. Usually a size about ten times bigger than the average memory-size, you need to allocate, is a good guess. But on the other hand the puddleSize should not be too large. Normally you should limit it to about 50kb. Note well, though, that these are only suggestions and no real limitations. Finally, the threshSize specifies, how large the memory that is to be allocated is allowed to be so that no new chunk is allocated automatically. If, for example, the threshSize is set to 25kb and you want to allocate a piece of memory with the size of 30kb, the internal lists of chunks of that memory-pool is not searched, but the memory is allocated directly, instead. If the memory to be allocated was only 20kb, the chunk-list would have been searched for a piece of free memory of that size, first. Of course, the threshSize must not be larger than the puddleSize and should not be too small, either. Half the puddleSize is a good guess here. CreatePool() returns a private pointer to a pool-structure that must be saved for further use. NULL is returned, if no memory for the pool-structure was available. You have to check for this condition. After use, all memory-pools must be destroyed by calling: #include <proto/exec.h> void DeletePool( APTR pool ); This function deletes the pool passed in. Additionally all memory that was allocated in this pool is freed. This way, you don't need to remember every single piece of memory, you allocated in a pool. Just call DeletePool() at the end. Note that you should be careful not to use pooled memory after its pool was deleted! If you want to allocate memory out of a pool, you need to call: #include <proto/exec.h> void *AllocPooled( APTR pool, ULONG size ); Besides the pool to allocate memory from, the size of the memory to allocate must be passed in. Returned is a pointer to a block of memory of the requested size or NULL to indicate that not enough memory was available. Memory allocated with AllocPooled() can be freed by either destroying the whole pool with DeletePool() or individually by calling: #include <proto/exec.h> void FreePooled( APTR pool, void *memory, ULONG size ); This function frees exactly one piece of memory that was previously allocated with AllocPooled(). The pointer to the memory pointer, returned by AllocPooled(), its size and the pool, it is in, have to be supplied as arguments. Note You may ask yourself: "If DeletePool() deletes all the memory of a pool, why should I ever use FreePooled()?" The answer is easy: to save memory. Normally it's good style to free memory as soon as you don't need it anymore. But sometimes it is easier just to free a memory-pool after a bunch of allocations. Nevertheless you should not use this feature, if you are not sure, when the memory-pool will be deleted. Imagine a program like this (do not try to compile it; it won't):
#define <exec/types.h>
#define <exec/memory.h>
#define <dos/dos.h>
int main(int argc, char *argv[])
{
APTR pool;
APTR mem;
/* Create our memory pool and test, if it was successful. */
pool = CreatePool(MEMF_ANY, 50*1024, 25*1024);
if (pool)
{
/* Just a dummy function. Image that this function will open a window,
with two buttons "Do Action" and "Quit".
*/
open_our_window();
for(;;)
{
/* Another dummy function that returns one of the definitions
below.
*/
switch(get_action())
{
/* This is returned, if the button "Do Action" was released. */
case DOACTION:
mem = AllocPooled(pool, 10*1024);
if (mem)
{
/* Another dummy function that uses our memory. */
silly_function(mem);
}
break;
/* This is returned, if the button "Quit" was released. */
case QUIT:
return RETURN_OK;
}
}
/* Close the window, we have opened above. */
close_our_window();
/* Delete our pool. */
DeletePool(pool);
}
}
Each time the button Do Action is released, some memory is allocated. This memory is freed at the end of the program, when DeletePool() is called. Of course, the longer the program is used, the more memory will be in use. That is why it would be much better to free the memory after use. This is done by replacing the part between case DOACTION: and case QUIT: by:
mem = AllocPooled(pool, 10*1024);
if (mem)
{
silly_function(mem);
FreePooled(pool, mem, 10*1024);
}
break;
Obsolete Memory Pool FunctionsMemory-pools are managed with struct MemHeader`s. If you have a pointer to such a structure, you may try to allocate some memory of its pool: #include <proto/exec.h> void *Allocate( struct MemHeader *mh, ULONG size ); Apart from the pointer to the struct MemHeader passed in as mh, you have to supply the size of the memory-block you want to allocate. This function returns either a pointer to the first memory-block found or NULL if no matching block was found. You must free every memory-block allocated with Allocate() with: #include <proto/exec.h> void Deallocate( struct MemHeader *mh, APTR mem, ULONG size ); You have to pass the same mh and size to Deallocate() as you have passed to Allocate() and additionally the pointer returned by it. intuition.library provides another way to handle memory pools with the functions AllocRemember() and FreeRemember(). Note, though, that these are obsolete. You should use the normal pool-functions of exec.library, instead. Allocating a specific memory addressUnder very rare circumstances you may have to allocate memory at a specific memory address. This performed by using: #include <proto/exec.h> void *AllocAbs( ULONG size, APTR address ); This function tries to allocate size bytes at address. If this is successful, a pointer to the requested address is returned. If some memory of the requested block is already allocated or is not available in the system, NULL is returned, instead. Avis The beginning of the memory block requested will be used by exec to store its node-data (the exact size is calculated by (2*sizeof (void *)) ). Therefore, you must not write to the beginning of the memory-block! Because of these obstacles you should not use AllocAbs(), except if you really need it. Memory allocated with AllocAbs() must be freed, using FreeMem(). Querying memory size and available memoryTo get the size of available memory, use the function: #include <proto/exec.h> ULONG AvailMem( ULONG type ); The type parameter is some of the following flags (or'ed), as defined in exec/memory.h:
You may as well specify other MEMF_ flags, they will be simply ignored. Note Note well that the queried memory-size does not have to reflect the real size of memory available, as this may always change in a multitasking-system, even while AvailMem() is executed. Program to list memory available in the system:
#include <stdio.h>
#include <exec/memory.h>
int main(int argc, char *argv[])
{
printf("Total free memory: %h, largest block: %h\n",
AvailMem(MEMF_ANY), AvailMem(MEMF_ANY|MEMF_LARGEST));
printf("Free chip memory: %h, largest block: %h\n",
AvailMem(MEMF_CHIP), AvailMem(MEMF_CHIP|MEMF_LARGEST));
printf("Free fast memory: %h, largest block: %h\n",
AvailMem(MEMF_FAST), AvailMem(MEMF_FAST|MEMF_LARGEST));
}
Adding memory to the systemThis chapter is only of concern to you, if you want to write a hardware-driver for a piece of hardware, which adds memory to the system:
#include <proto/exec.h>
void AddMemList
(
ULONG size, ULONG type, LONG priority,
APTR address, STRPTR name
);
adds memory to the list of free memory in the system. You have supply the address and the size of the memory to add. type has to be set to at least one of the MEMF_ flags, which are defined in exec/memory.h:
You can provide a priority, with which your memory will be added to the memory list. The general rule is: The quicker your memory, the higher the priority should be. If you don't know, what to supply here, supply 0. Finally, you can provide a name, with which your memory can be identified by the system and its users. You may provide NULL instead of a name, but giving your memory a name is recommended. Once your memory was added to the list of free memory, it can't be removed anymore. Low memory situationsFIXME: AddMemHandler()/RemMemHandler()
Lists
Balanced Binary Search Trees
Signals
Messages and Ports
Semaphores
Tasks
Devices
Libraries
Resources
Interrups
I/O
Misc.
|
Copyright © 1995-2009, L'équipe de développement AROS. Tous droits réservés. Amiga®, AmigaOS®, Workbench et Intuition sont des marques de Amiga Inc. Les autres marques appartiennent à leur propritaires respectifs. |