Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Sauermann J.Realtime operating systems.Concepts and implementation of microkernels for embedded systems.1997.pdf
Скачиваний:
27
Добавлен:
23.08.2013
Размер:
1.32 Mб
Скачать

3. Kernel Implementation

77

 

 

3.9Memory Management

As we will see in Section 6.4, a library libgcc2 has to be provided in order to link the kernel. This library contains in particular the code for the global C++ operators new and delete. The code in libgcc2 basically calls two functions, malloc() (for operator new) and free() (for operator delete).

One way to provide these functions is to compile the GNU malloc package and to link it to the kernel. But this method consumes considerable memory space. It should also be noted that the malloc package contains uninitialized variables and would thus result in a non-empty BSS section. Since we do not use the BSS section, the source code of the malloc package needs to be modified by initializing all uninitialized variables to 0.

As you may have noticed, we never used the new operator in the kernel code, except for creating new tasks and their associated stacks. The main reason for not using this operator is that in an embedded system, there is most likely no way to deal with the situation where new (i.e. malloc()) fails due to lack of memory. The malloc package allocates memory in pages (e.g. 4kByte; the page size can be adjusted) and groups memory requests of similar size (i.e. rounded up to the next power of 2) in the same page. Thus if there are requests for different sizes, a significant number of pages could be allocated. For conventional computers with several megabytes of memory this is a good strategy, since the waste of memory in partly used pages is comparatively small. For embedded systems, however, the total amount of memory is typically much smaller, so that the standard malloc() is not the right choice.

We actually used the standard malloc() in the early kernel versions, but replaced it later on by the following.

1 /* os.cc */

...

17extern int edata;

18char * os::free_RAM = (char *)&edata;

The label edata is computed by the linker and indicates the end of the .DATA section; i.e. past the last initialized variable. The char pointer free_RAM is thus initialized and points to the first unused RAM location.

21extern "C" void * sbrk(unsigned long size)

22{

23void * ret = os::free_RAM;

24

 

 

25

os::free_RAM += size;

 

26

 

 

27

if (os::free_RAM > (char *)RAMend)

// out of memory

28{

29os::free_RAM -= size;

30ret = (void *) -1;

78

3.9 Memory Management

 

 

31 }

32

33return ret;

34}

The function sbrk(unsigned long size) increases the free_RAM pointer by size and returns its previous value. That is, a memory block of size size is allocated and returned by sbrk().

36extern "C" void * malloc(unsigned long size)

37{

38void * ret = sbrk((size+3) & 0xFFFFFFFC);

40

if (ret == (void *)-1) return 0;

41return ret;

42}

Our malloc() implementation rounds the memory request size up to a multiple of four bytes so that the memory is aligned to a long word boundary.

45extern "C" void free(void *)

46{

47}

Finally, our free() function does not free the memory returned. As a consequence, delete must not be used. As long as tasks are not created dynamically and new is not used elsewhere, this scheme is most efficient and adequate. Otherwise, one should use the standard malloc package or write an own version meeting specific requirements. A better solution than the global new operator is to overload the new operator for specific classes. For example, memory for certain classes could be allocated statically and the class specific new operator (which defaults to the global new operator) could be overloaded. This gives more control over the memory allocation.

Finally, it should be noted that embedded systems with hardware memory management need a memory management scheme that is written specifically for the memory management unit used.