Visualizzazione post con etichetta kernel. Mostra tutti i post
Visualizzazione post con etichetta kernel. Mostra tutti i post

sabato 13 aprile 2013

FreeBSD kernel malloc

The FreeBSD kernel provides a set of malloc/free functions that are similar to the C-library ones for managing the memory inside the kernel space.

All the memory management is done using the malloc_type structure, that is defined as follows:

struct malloc_type {
   struct malloc_type *ks_next; /* Next in global chain. */
   u_long ks_magic; /* Detect programmer error. */
   const char *ks_shortdesc; /* Printable type name. */
   void *ks_handle; /* Priv. data, was lo_class. */
};

The first field is a pointer to another malloc_type structure used to chain the allocated memory descriptor in a list, in particular the kern_malloc.c contains a kmemstatistics struct that always points to the last allocated malloc_type structure (and hence to the head of the chain). The ks_magic field is used to check for wrong malloc'ed areas: if it does not contains the special value M_MAGIC the kernel will panic assuming the memory is corrupted. The ks_shortdesc contains a description of the memory area, as reported in the output of the command "vmstat -m". Finally, the ks_handle field contains another structure, malloc_type_internal that in turns contains private kernel data, such as DTrace probes, the zone from which the memory has been allocated and statistics about the memory area itself.

Therefore in order to handle a memory chunk in the kernel space a malloc_type struct must be hold. There are two main macros to deal with malloc_type, defined in the sys/malloc.h header file:
- MALLOC_DEFINE
- MALLOC_DECLARE

The former, MALLOC_DEFINE, is used to define an area of memory that will be allocated later using the malloc(9) call. The latter, MALLOC_DECLARE, is a shortcut for "importing" a memory definition into another source file.
The MALLOC_DEFINE macro is shown in the following:


#define MALLOC_DEFINE(type, shortdesc, longdesc) \
   struct malloc_type type[1] = { \
         { NULL, M_MAGIC, shortdesc, NULL } \
         }; \
   SYSINIT(type##_init, SI_SUB_KMEM, 
               SI_ORDER_SECOND, malloc_init, \
              type); \
   SYSUNINIT(type##_uninit, SI_SUB_KMEM, 
                    SI_ORDER_ANY, \
                    malloc_uninit, type)


The macro contains three instructions. The first is the declaration and initialization of a malloc_type structure as a single element array (this trick is done to have a pointer to the malloc type within the macro itself - the array name is the pointer to the malloc type). The other two instructions attach the malloc_init and malloc_uninit functions to the specified malloc type. In this way, the system will initialize (and uninitialize) the memory area with the above functions (from kern_malloc.c). The SI_SUB_KMEM subsystem is scanned and executed during the boot, and if the MALLOC_DEFINE happens to be in a module, the initialization and desctruction happens as soon as the module is loaded/unloaded. The "##" used in the macro is for macro token concatenation.

To better understand the above MALLOC_DEFINE macro, suppose the following is called in a module:

MALLOC_DEFINE( M_LUKE, "Luke Memory", 
               "Memory for the Luke subsystem" );

this will expand to:

struct malloc_type M_LUKE[1] = 
      { { NULL, M_MAGIC, "Luke Memory", NULL } };
SYSINIT(M_LUKE_init, 
          SI_SUB_KMEM, SI_ORDER_SECOND, 
          malloc_init, M_LUKE );
SYSUNINIT(M_LUKE_uninit, 
          SI_SUB_KMEM, SI_ORDER_ANY, 
          malloc_uninit, type);


The second macro for memory usage is MALLOC_DECLARE, that is defined in sys/malloc.h as follows:

#define MALLOC_DECLARE(type) \
extern struct malloc_type type[1]

and that is useful simply to inform that a piece of malloc-based memory metadata has been defined somewhere else in the source tree.

In order to conclude this brief bird's eye view on kernel memory allocation, the malloc_init and malloc_uninit functions defined in sys/kern_malloc.c perform calls to the uma_zalloc and uma_zfree_arg to respectively allocate and free a memory chunk. The zone used for the allocation is always the same, called mt_zone and created in the kmeminit function (executed of course early in the boot sequence):

static void
kmeminit(void *dummy)
{
...
mt_zone = uma_zcreate("mt_zone", 
           sizeof(struct malloc_type_internal),
           NULL, NULL, NULL, NULL
           UMA_ALIGN_PTR, UMA_ZONE_MALLOC);
...
}

The UMA related functions are defined in the vm/uma_core.c source file.

mercoledì 24 giugno 2009

Creare un modulo per il kernel di Linux

Questo breve articolo mostra come creare e caricare un semplice modulo per il kernel di Linux, in modo da dare accesso alle funzionalità del kernel.
Un modulo deve essere scritto in linguaggio C, compilato in un modo un po' particolare (ma tutto sommato semplice) e installato e rimosso mediante i comandi insmod, depmod e similari.

Vediamo subito il codice di un modulo che viene caricato in memoria e appena attivato stampa la classica frase Hello World:

/* Declare what kind of code we want from the header files */
#define __KERNEL__ /* We're part of the kernel */
#define MODULE /* Not a permanent part, though. */

#include

/* Initialize the LKM */
int init_module()
{
printk("Modulo in fase di inizializzazione...\n");


printk("\nHELLO WORLD!\n");

/* If we return a non zero value, it means that
* init_module failed and the LKM can't be loaded
*/
return 0;
}

/* Cleanup − undo whatever init_module did */
void cleanup_module(){
printk("Modulo in fase di uscita\n");
printk("\nBYE BYE WORLD");
}

La parte relativa agli header, come pure i prototipi delle funzioni, sono standard. In particolare init_module viene richiamata al momento dell'installazione del modulo, mentre cleanup_module al momento della sua rimozione.
La compilazione del modulo avviene tramite il seguente Makefile:

obj-m := HelloWorld.o

KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

Una volta compilato il modulo, sarà disponibile un file .ko (Kernel Object) che può essere caricato nel sistema. Siccome stiamo parlando di un semplice modulo di prova, anziché installare il modulo direttamente nella directory modules possiamo installare e rimuoverlo manualmente.
Per installarlo basta usare il comando insmod:


 insmod ./HelloWorld.ko

Una volta installato il modulo (è possibile controllarne l'installazione con lsmod) si possono vedere i primi messaggi del kernel. Un dmesg mostra infatti la stampa della funzione di inizializzazione.
Rimuovendo poi il modulo con:

 rmmod HelloWorld
si avrà lo scaricamento dalla memoria del modulo e la scrittura fra i messaggi del kernel che la funzione di chiusura del modulo è stata richiamata.