[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [home] [volume] [parte]


Capitolo 613.   Preparazione

In questa parte viene descritto il procedimento per realizzare un sistema, estremamente banalizzato, per elaboratori x86-32. Questo sistema è privo di pianificazione dei processi (scheduler), ma soprattutto non è in grado di avviare programmi e nemmeno di accedere a qualche file system. Tuttavia, dispone di una specie di shell che può essere modificata per inserire proprie funzionalità; ha una gestione della memoria con cui è possibile usare le funzioni ...alloc() e free(); inoltre esiste la possibilità di definire delle chiamate di sistema.

Giusto per dare un nome a questa cosa, si usa la sigla «05», ovvero le cifre numeriche che più si avvicinano a «os».

Prima di cominciare conviene preparare tutto quello che serve, come viene descritto in questo capitolo. Naturalmente ci si avvale degli strumenti di un sistema GNU/Linux per lo sviluppo di questo giocattolo.

Tutti i file descritti in questi capitoli dovrebbero essere disponibili a partire da <allegati/a2/05/>.

613.1   File-immagine

Per prima cosa serve un file-immagine di un dischetto da 1,44 Mibyte, predisposto con GRUB 1, in modo tale da avviare il file kernel. In pratica si predispone inizialmente un dischetto reale, con un file system Dos-FAT, si crea la directory grub/ e al suo interno si mettono i file stage1 e stage2 di GRUB 1, assieme al file menu.lst che può avere semplicemente il contenuto seguente:

title kernel
kernel (fd0)/kernel

Si mette temporaneamente un file fittizio, denominato kernel, nella directory principale del dischetto e si procede all'installazione del settore di avvio di GRUB 1 stesso:

grub[Invio]

grub> root (fd0)[Invio]

 Filesystem type is fat, using whole disk.

grub> setup (fd0)[Invio]

 Checking if "/boot/grub/stage1" exists... no
 Checking if "/grub/stage1" exists... yes
 Checking if "/grub/stage2" exists... yes
 Checking if "/grub/fat_stage1_5" exists... no
 Running "install /grub/stage1 (fd0) /grub/stage2 p /grub/menu.lst "...
   succeeded
 Done.

grub> quit[Invio]

A questo punto, avendo terminato il lavoro di installazione di GRUB 1 nel dischetto, si può produrre il file-immagine:

cp /dev/fd0 floppy.img[Invio]

613.2   Directory di lavoro

Prima di iniziare gli esperimenti, si predispone una directory di lavoro, da utilizzare in qualità di utente comune. Nella directory di copia il file floppy.img e si mettono alcuni script molto semplici:

Listato 613.4. ./mount

#!/bin/sh
chmod a+rw floppy.img
su root -c "mount -o loop,uid=1001 -t vfat floppy.img /mnt/fd0"

Listato 613.5. ./umount

#!/bin/sh
su root -c "umount /mnt/fd0"

Listato 613.6. ./bochs

#!/bin/sh
bochs -q 'boot:a' 'floppya: 1_44=floppy.img, status=inserted' 'megs:32'

Il senso di questi script è evidente e il loro scopo è solo quello di ridurre al minimo l'impegno di digitazione. In questa directory viene poi predisposto anche lo script compile, ma viene descritto nella sezione successiva.

613.3   Directory «05/»

A partire dalla directory di lavoro si crea la sottodirectory 05/, nella quale viene poi messo il codice del sistema che si va a creare. Ma per evitare di fare confusione con i file-make, si predispone uno script per la compilazione che li crea al volo, in base ai contenuti effettivi delle sottodirectory.

Listato 613.7. ./05/makeit

#!/bin/sh
#
# makeit...
#
OPTION="$1"
#
edition () {
    local EDITION="include/kernel/build.h"
    echo -n                              > $EDITION
    echo -n "#define BUILD_DATE \""     >> $EDITION
    echo -n `date "+%Y%m%d%H%M%S"`      >> $EDITION
    echo  "\""  >> $EDITION
}
#
#
#
makefile () {
    #
    local MAKEFILE="Makefile"
    local TAB=" "
    #
    local SOURCE_C=""
    local C=""
    local SOURCE_S=""
    local S=""
    #
    local c
    local s
    #
    # Trova i file in C.
    #
    for c in *.c
    do
        if [ -f $c ]
        then
        C=`basename $c .c`
            SOURCE_C="$SOURCE_C $C"
        fi
    done
    #
    # Trova i file in ASM.
    #
    for s in *.s
    do
        if [ -f $s ]
        then
        S=`basename $s .s`
            SOURCE_S="$SOURCE_S $S"
        fi
    done
    #
    # Prepara il file make.
    #
    echo -n                                                     > $MAKEFILE
    echo "# Questo file è stato prodotto automaticamente"      >> $MAKEFILE
    echo "# dallo script \"makeit\", sulla base dei"            >> $MAKEFILE
    echo "# contenuti della directory."                         >> $MAKEFILE
    echo "#"                                                    >> $MAKEFILE
    echo "c = $SOURCE_C"                                        >> $MAKEFILE
    echo "#"                                                    >> $MAKEFILE
    echo "s = $SOURCE_S"                                        >> $MAKEFILE
    echo "#"                                                    >> $MAKEFILE
    echo "all: \$(s) \$(c)"                                     >> $MAKEFILE
    echo "#"                                                    >> $MAKEFILE
    echo "clean:"                                               >> $MAKEFILE
    echo "${TAB}@rm *.o 2> /dev/null ; pwd"                     >> $MAKEFILE
    echo "#"                                                    >> $MAKEFILE
    echo "\$(s):"                                               >> $MAKEFILE
    echo "${TAB}@echo \$@.s"                                    >> $MAKEFILE
    echo "${TAB}@as -o \$@.o \$@.s"                             >> $MAKEFILE
    echo "#"                                                    >> $MAKEFILE
    echo "\$(c):"                                               >> $MAKEFILE
    echo "${TAB}@echo \$@.c"                                    >> $MAKEFILE
    echo "${TAB}@gcc -Wall -Werror -o \$@.o -c \$@.c" \
         "-nostdinc -nostdlib -nostartfiles -nodefaultlibs" \
        "-I../include -I../../include -I../../../include"       >> $MAKEFILE
    #
}
#
#
#
main () {
    #
    local CURDIR=`pwd`
    local OBJECTS
    local d
    local c
    local s
    local o
    #
    edition
    #
    for d in `find .`
    do
        if [ -d "$d" ]
        then
            #
            # Ci sono sorgenti in C o in ASM?
            #
            c=`echo $d/*.c | sed "s/ .*//"`
            s=`echo $d/*.s | sed "s/ .*//"`
            #
            if [ -f "$c" ] || [ -f "$s" ]
            then
                CURDIR=`pwd`
                cd $d
                makefile
                #
                if [ "$OPTION" = "clean" ]
                then
                    make clean
                else
                    if ! make
                    then
                        cd "$CURDIR"
                        exit
                    fi
                fi
                cd "$CURDIR"
            fi
        fi
    done
    #
    cd "$CURDIR"
    #
    #
    #
    if [ "$OPTION" = "clean" ]
    then
        true
    else
        OBJECTS=""
        #
        for o in `find . -name \*.o -print`
        do
            if    [ "$o" = "./kernel/kernel_boot.o" ] \
               || [ "$o" = "./kernel/kernel_main.o" ] \
               || [ ! -e "$o" ]
            then
                true
            else
                OBJECTS="$OBJECTS $o"
            fi
        done
        #
        echo "Link"
        #
        ld --script=linker.ld -o kernel_image \
                kernel/kernel_boot.o \
                $OBJECTS \
                kernel/kernel_main.o
        #
        cp -f kernel_image /mnt/fd0/kernel
        sync
    fi
}
#
# Start.
#
if [ -d include ] && [ -d kernel ] && [ -d lib ]
then
    main
else
    echo "Mi trovo in una posizione sbagliata e non posso svolgere" \
         "il mio compito"
fi

Va osservato che la variabile TAB deve contenere esattamente una tabulazione orizzontale (di norma il codice 0916. Pertanto, se si riproduce il file o se lo si scarica, occorre verificare che il contenuto sia effettivamente una tabulazione, altrimenti va corretto. Se la variabile TAB contiene solo spazi, i file-make che si ottengono non sono validi.

    local TAB=" "

In pratica, attraverso questo script, i file-make che si generano hanno un aspetto simile a quello del listato seguente:

c =  elenco_file_c_senza_estensione
#
s =  elenco_file_asm_senza_estensione
#
all: $(s) $(c)
#
clean:
        @rm *.o 2> /dev/null ; pwd
#
$(s):
        @echo $@.s
        @as -o $@.o $@.s
#
$(c):
        @echo $@.c
        @gcc -Wall -Werror -o $@.o -c $@.c \
  \ -nostdinc -nostdlib -nostartfiles -nodefaultlibs \
  \ -I../include -I../../include -I../../../include

Il «collegamento» (link) dei file avviene attraverso un comando contenuto nello script makeit, dove si fa in modo di mettere all'inizio il file-oggetto che è responsabile dell'avvio, dal momento che contiene l'impronta di riconoscimento per il sistema di avvio aderente alle specifiche multiboot.

Nella directory di lavoro descritta nella sezione precedente, conviene mettere uno script che richiami a sua volta makeit e che provveda a copiare il file del kernel nel file-immagine del dischetto:

Listato 613.9. ./compile

#!/bin/sh
cd 05
./makeit clean
./makeit
cd ..

613.4   Script di collegamento

Sempre all'interno della directory 05/ va predisposto lo script usato da GNU ld per eseguire correttamente il collegamento dei file oggetto in un file eseguibile unico. Dal momento che nel progetto che si intraprende si intende usare la memoria linearmente, si intende che il blocco minimo sia della dimensione di un registro, ovvero pari a 4 byte:

Listato 613.10. ./05/linker.ld

/***********************************************************************
 * La memoria viene usata in modo lineare, senza controlli dei
 * privilegi, così non si usano nemmeno gli allineamenti tradizionali
 * di 4096 byte, ma solo di 4 byte, ovvero di un registro.
 ***********************************************************************/

ENTRY (kernel_boot)
SECTIONS {
    . = 0x00100000;
    k_mem_total_s = .;
    .text : {
        k_mem_text_s = .;
        *(.text)
        . = ALIGN (0x4);
        k_mem_text_e = .;
    }
    .rodata : {
        k_mem_rodata_s = .;
        *(.rodata)
        . = ALIGN (0x4);
        k_mem_rodata_e = .;
    }
    .data : {
        k_mem_data_s = .;
        *(.data)
        . = ALIGN (0x4);
        k_mem_data_e = .;
    }
    .bss : {
        k_mem_bss_s = .;
        *(.bss)
        *(COMMON)
        . = ALIGN (0x4);
        k_mem_bss_e = .;
    }
    k_mem_total_e = .;
}

Il codice contenuto nel file del kernel che si va a produrre, deve iniziare a partire da 0010000016, ovvero da 1 Mibyte, come prescrive il sistema di avvio multiboot, il quale va a collocarlo in memoria, a partire da quella posizione. Inoltre, per consentire di individuare i blocchi di memoria utilizzati, vengono inserite delle etichette; per esempio, k_mem_total_s individua l'inizio del kernel, mentre k_mem_total_e ne individua la fine.

Si dà per scontato che GNU AS predisponga un file eseguibile in formato ELF.

613.5   Altre directory

All'interno di 05/ si creano ancora: lib/, per la libreria standard e altre librerie specifiche del sistema; include/, per i file di intestazione della libreria; kernel/ con i file iniziali usati dal kernel; app/ per le applicazioni (ovvero le funzioni avviate dal kernel quando tutto è pronto).


Appunti di informatica libera 2008 --- Copyright © 2000-2008 Daniele Giacomini -- <appunti2 (ad) gmail·com>


Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome preparazione_1.htm

[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [home]

Valid ISO-HTML!

CSS validator!

Gjlg Metamotore e Web Directory