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.

venerdì 12 aprile 2013

C macro: do..while(0) explaination

C macros are a very powerful way to make the code readable without having to define a lot of functions in the code. One important aspect of the macros is that they can have arguments, much like an ordinary function, and therefore using a macro can be totally like a function invocation. For instance, in the following piece of code it is almost impossible to say if DO_SOMETHING is a function or a macro, except the convention that macro names are all uppercase:

if( must_work )
   DO_SOMETHING( "I'm working hard!" );
else
   printf( "I'm not working" );

Now assume that DO_SOMETHING is a macro and has the following definition:

#define DO_SOMETHING( msg ) do{ \
    if( debug_level > 0 ) \
        printf( msg ); \
    } while( 0 )

The above macro simply prints the specified message to standard output if the debug level is higher than zero.
So what are the differences between a macro that accepts parameters and an ordinary function? The first difference should be evident from the above example: the argument "msg" does not have a type, and therefore the macro must be handled with correct arguments.
A second difference is the presence of the do{ } while( 0 ) statement that surrounds the macro itself: first of all note that there is no semicolon at the end of the do..while statement so that the semicolon at the end of the whole macro is used. In other words the macro expands from:

DO_SOMETHING( "BLAH!" );

to

do{
 if( debug_level > 0 )
   printf( msg );
} while( 0 );

But why is the do..while needed at all? Does not suffice to use a couple of brackets {..} to surround the macro code? Well, they do suffice, but there is a side effect in using the brackets: the semicolon will cause the breaking of the statement that contains the macro itself.
To better explain the above, assume the macro is defined without the do..while and therefore appears as follows:

#define DO_SOMETHING( msg ) { \
  if( debug_level > 0 ) \
    printf( msg ); \
}

and place it into the code that calls it, that is then expanded it in the following:

if( must_work )
  /* DO_SOMETHING( "I'm working hard!" ); */
  {
    if( debug_level > 0 )
       printf( msg );
  };
else
  printf( "I'm not working" );

and that does not compile! The reason is that the semicolon before the else statement causes the if to stop (in other words it is a null instruction after the if closes) and the else cannot be "attached" to the if.
Therefore, having the do..while allows the caller to place a semicolon without worrying about the statement that (could) wrap the macro itself. In fact, using the do..while the above macro expands to the following working code:

if( must_work )
  /* DO_SOMETHING( "I'm working hard!" ); */
  do {
   if( debug_level > 0 )
     printf( msg );
  } while( 0 );
else
  printf( "I'm not working" );

that is legal!

Gigetto: orari flessibili?

No, non si tratta di una modifica agli orari dell famoso (o infame?) trasporto ferroviario Modena-Sassuolo. Facevo caso ad una cosa curiosa circa la tabella degli orari del treno, o come la chiamerebbero i produttori dell'oscuro sito Web ad esso relativo, la time table. Ci sono dei treni che nel corso della giornata cambiano leggermente orario. Ad esempio ci sono fermate che per tutta la mattina sono fissate ai '25 (ossia 6:25, 7:25, 8:25 e così via), per poi passare ai '30 (ad es. 13:30) per poi arrivare ai '37 (ad esempio 21:37). La frequenza dei treni è invariata, c'è semplicemente un "rilassamento" della tabella degli orari man mano che ci si avvicina alla fine della giornata.
Perché curarsi di questo dettaglio? Beh, perché come in quasi tutti i trasporti periodici il cliente si basa sulla periodicità propria del mezzo, ovvero sa che il suo trasporto passerà ai 'xx di ogni ora. Avendo questa modifica apparentemente aleatoria e ingiustificata (si tratta di 12 minuti almeno nell'arco di meno di 15 ore) il viaggiatore rischia di rimanere confuso e, cosa piu' importante, impossibilitato a pianificare con precisione i propri spostamenti (se non consultando costantemente la time table).

giovedì 11 aprile 2013

Calendar for fun

Il calendario di Unix/Linux prende i propri file da /usr/share/calendar. Esplorando tale directory si possono trovare alcuni file curiosi, come ad esempio:
- calendar.computer che contiene date storiche circa i computer
- calendar.lotr che contiene le date della saga Lord Of The Rings
- calendar. che contiene le date principali della propria distribuzione o del proprio sistema operativo (es. calendar.debian, calendar.freebsd)
- calendar.music che ovviamente contiene delle date storiche per la musica

Ad esempio, invocando calendar con il file lotr si ottiene:

$ calendar -f /usr/share/calendar/calendar.lotr
Apr 04  Gandalf visits Bilbo


Vale la pena visitare la directory per scoprire altri calendari interessanti!

giovedì 4 aprile 2013

make love, not war

Adoro la cultura Unix!
E adoro la possibilita' di inserire degli "easter egg" nei programmi, ovvero delle piccole sorprese, il piu' delle volte mirate a far sorridere l'utente o il sysadmin.

Ecco allora che il super-comando make, se invocato con il target speciale "love", risponde con il classico slogan. La versione GNU dei sistemi Linux e' invece piu' seria e rigorosa, e risponde con un messaggio di errore poiche' il target "love" non esiste...




Se si controlla il sorgente del make Unix, in particolare in job.c, si trova il seguente pezzo di codice che, ovviamente, spiega il funzionamento:

#ifndef WITHOUT_OLD_JOKE
                    if (strcmp(gn->name,"love") == 0)
                               (*abortProc)("Not war.");
                     else
#endif

Sysadmin panics: cheap hardware

Uno degli effetti principali dei movimenti Free/Open e' stato il supporto ad architetture dismesse e/o obsolete. In breve, non dovendo piu' forzare gli utenti a comprare nuovo hardware per soddisfare una qualche alleanza con i vendor (o per mascherare i propri bug ed inefficienze con un sistema piu' potente), l'utente ha la liberta' di eseguire il sistema operativo e le applicazioni sull'hardware che ha a disposizione.
Effetto collaterale: molti sysadmin si fanno vanto di riuscire a fare deploy di interi stack full-feature su macchine obsolete e quindi a basso costo.
Ed e' possibile, sia chiaro! Io stesso ho usato piu' volte vecchi server "di riciclo" per fare upgrade o repliche. Per non parlare del mio parco computer personale: vecchi PC e Mac ai quali ho restituito dignita' grazie a sistemi Open Source non avari di risorse.
Quindi qual'e' il punto?
Essere coscienti dei limiti del proprio hardware.

L'hardware si usura.
L'hardware si rompe.
L'hardware puo' avere dei bug (o meglio il suo firmware puo' averli).

Usare quindi hardware di riciclo invece che un sistema opportunamente dimensionato pone il sistema che si implementando ad una serie di rischi di sicurezza, durata, stabilita' che devono essere tenuti in considerazione.

Usare un netbook come server di rete e' possibile, ma si perde, ad esempio, la possibilita' di fare un RAID dei dati. Ora, se questi sono sottoposti a regolari backup, e la continuita' operativa non e' stringente, la cosa e' possibile. Spacciare invece una simile soluzione come una "genialata" che fa risparmiare soldi al cliente e' invece un azzardo.

Comprare vecchi server per pochi euro non significa aver risparmiato: significa aver comprato una macchina dismessa e prossima alla morte fisica dell'hardware. Nulla a cui affiderei i miei dati piu' importanti.

La soluzione e' quindi comprare sempre hardware nuovo fiammante? No!
E' semplicemente prendere coscenza di quello che si ha per quello che si spende. Un po' come quando si compra una macchina usata e ci si interroga su come il precedente proprietario l'abbia trattata.

E si presti attenzione: il fatto che il proprio OS preferito giri su tale pezzo di metallo non significa che i risultati siano eccellenti.

martedì 2 aprile 2013

Org Mode cambia logo!

Inizialmente pensavo ad uno scherzo (visto che il messaggio è del 1 Aprile), ma a quanto pare Org Mode ha realmente cambiato logo, passando dall'unicorno

allo struzzo!

L'e-mail ufficiale con le ragioni per la modifica al logo è qui.

In realtà, a parte il pesce d'Aprile, il logo del progetto è cambiato leggermente nella forma e nei colori:



Passare da [OpenSource Desktop] a OSX è giusto?

Miguel De Icaza ha pubblicato giorni fa un post piuttosto sconvolgente circa il suo passaggio definitivo ad OSX, il sistema operativo di casa Apple.

Prima di addentrarsi nei dettagli dell'articolo occorre ricordare chi Miguel De Icaza sia. Miguel è uno sviluppatore eccezionale, autore prima di Midnigth Commander, e poi fondatore e leader del progetto Gnome, uno dei desktop piu' famosi per ambienti Unix e Linux. E' anche fondatore di Mono, il "port" della tecnologia .NET di casa Microsoft su piattaforma Unix, ed è stato, fra le altre cose, fondatore di Ximian e ora di Xamarin. Miguel è una mente brillante, ha dato prova nel tempo di essere un hacker eccellente e di saper vedere nel sistema Open Source una ragione di essere e un modo di fare business. E' ovviamente un forte sostenitore del Free Software, e Mono inizialmente tanto criticato si è rivelata una delle sue ennesime scelte azzeccate: rendere i clienti Microsoft interessati anche al mondo Unix e Linux (free).

Solitamente gli hacker "impegnati" del calibro di Miguel non si lasciano "corrompere" dal mondo Apple. Parliamo di gente che è in grado di aggiustare i driver del proprio computer se non funzionano, e quindi di persone che non devono aspettare un qualche rilascio su mainstream o una patch solo per avere funzionante il tasto del volume del loro nuovo portatile.
Ecco quindi perche' il post di Miguel è sembrato sconvolgente.

E il cambiamento di Miguel verso il mondo Apple era gia' stato annunciato da un precedente post in cui spiegava come Linux avesse perso la corsa al desktop. http://tirania.org/blog/archive/2012/Aug-29.html

Ultimamente la discussione sui desktop alternativi a OSX e a Microsoft Windows diviene sempre piu' serrata, e molti utenti sui forum continuano a chiedere a gran voce supporto per i loro computer.
Il post di Miguel sostanzialmente puo' essere riassunto in "uso OSX perche' l'hardware funziona e ho accesso ad un sistema Unix", pensiero condiviso da sempre piu' hacker. Perché OSX funziona? Beh tutto sommato è semplice: la Apple fornisce un sistema completo, ovvero hardware e software. Ne consegue che la Apple ha le specifiche hardware prima ancora di installare il software, e quindi puo' creare il connubio perfetto cambiando il software ad-hoc o ricercando hardware differente e meglio compatibile. E tipicamente un utente Apple non farà modifiche all'hardware se non per sostituire un componente rotto con uno rigorosamente Apple (e rigorosamente bianco con una mela morsicata sopra).
Questa cosa mi ricorda molto le workstation Sun: funzionava tutto alla perfezione, e vi erano perfino comandi appositi per controllare le schede grafiche 3D, e tutto questo era possibile perché chi forniva il software era, prima di tutto, un fornitore hardware.

Nello scenario del commodity hardware la situazione é invece differente: Microsoft stringe da sempre alleanze con i produttori di hardware per avere la possibilità di fornire driver di (mediocre) qualità. Di contro, i sistemi liberi si trovano nella difficoltà di non aver accesso a tutto l'hardware esistente, come pure di non avere sufficiente forza lavoro per implementare nuovi driver e stare al passo con il mercato. Certo, fino a che i driver si possono appoggiare a degli standard il gioco regge. O meglio puo' reggere: l'hardware puo' infatti essere affetto da bug, e non sempre questi sono di facile soluzione. In tutto questo chi produce anche l'hardware si trova nell'indiscussa posizione di vantaggio che gli consente di fornire ai clienti un prodotto finito solo quando tutto sia ben testato: i bug vengono corretti o i problemi hardware vengono risolti sostituendo addirittura i componenti fallati.

In altre parole, il vero nocciolo della questione e' il supporto hardware e i relativi driver.
Gia', i driver, sono questi quelli che gli utenti chiedono a gran voce, e sono questi quei componenti che i sistemi open tanto faticano a fornire.
E si badi, non è un problema di compatibilità binaria, come descritto da Miguel: sistemi operativi come FreeBSD (che fondamentalmente importano "tutto") non sono messi bene nel ramo desktop, anzi sono ancora indietro rispetto a Linux.

E' quindi forse un problema legato all'API di [Open/Next]Step e alla scelta del linguaggio, Objective-C, rispetto all'uso ad esempio del C++ di KDE o del C (ad oggetti) di Gnome/GTK?
Io non penso, visto che il progetto GNUStep, fondamentalmente una implementazione libera della suddetta API compatibile con l'implementazione OSX, non ha portato piu' avanti il desktop Linux. Infatti, nonostante WindowMaker sia un ottimo window manager, e uno dei miei preferiti quando ero un'apprendista unixiano, non mi pare sia uno dei piu' apprezzati, anzi credo di poter affermare che ricopre un ruolo di nicchia. Si lo so, WindowMaker e GNUStep sono progetti tutto sommato separati e GNUStep non necessita nemmeno di un window manager, ma mi si passi il ragionamento.

La soluzione quindi e' forse condividere la ricerca sui driver open in modo che tutti i vari sistemi possano ispirarsi a risultati gia' ottenuti per incrementare il loro supporto? Non mi pare nemmeno questa la strada, visto le forti discrepanze fra i vari OS.

Creare allora livelli di astrazioni che mascherino l'hardware (ad es. Solid di KDE)? Si, sulla carta, ma alla fine quello che capita e' che ogni sistema prima o poi reinventa un pezzo di ruota. Ad esempio Gnome e' ormai fortemente ispirato a Linux, e quindi di difficile adattamento a BSD; PCBSD ha scelto deliberatamente di riscrivere quasi tutti i tool gia' inclusi in KDE piuttosto che "adattarli", e cosi' via.

Allora la soluzione e' prendere un sistema OSX che funziona out-of-the-box?
Beh, saro' forse un purista, ma non penso sia nemmeno questa la soluzione. Anzi, penso sia la morte del sistema Open Source. Eh si, perche' se e' vero che tutti noi abbiamo un lavoro da fare e un sistema che non si blocca e' meglio di uno che ha dei problemi, comprare un sistema proprietario, per quanto buono sia, significa ammettere esplicitamente il fallimento del movimento Open Source e negare il nostro supporto ad esso.

Vale la pena rifletterci.