NSA - Non Solo Amiga

SOFTWARE => Sistemi Operativi => Altri & "Nerd OS" => Topic aperto da: Z80Fan - 08 Agosto 2011, 01:55:05

Titolo: SuperKernel !
Inserito da: Z80Fan - 08 Agosto 2011, 01:55:05
Mentre contemplo se cominciare a "riscriverlo" in C++, ho pensato che sarebbe stato carino parlarvi del mio SuperKernel!

Ebbene si, sto scrivendo un sistema operativo! :dance:
(come metà della gente qui dentro :lol:)

L'idea è di un paio di anni fa, quando, per dar pace a tutte quelle idee che mi frullavano in testa, decisi di iniziare a scrivere un sistema operativo, inizialmente aiutato da un mio amico, che poi dovette lasciare per un bel po' il progetto, non prima però di avermi tirato fuori una bella libreria per gestire il FAT32. :)

L'idea di partenza era di fare un tipico clone linux, in modo da portare successivamente le varie applicazioni senza riscriverle; proseguendo lo sviluppo è diventato chiaro che io volessi fare le cose a ModoMio©, e quindi l'obiettivo finale ora è quello di fare un sistema operativo modulare, in modo da poterlo configurare incastrandoci dentro i vari moduli per personalizzare la funzionalità. Molto spazio sarà dato alle librerie, in modo da fornire un'interfaccia uniforme per le applicazioni, e possibilmente per i driver.

Quindi, la domanda eterna: monolitico o microkernel?
Beh, sempre per il ModoMio©, non ne ho idea! :D
Ovviamente, poichè devo farlo PiùMeglio™, ho pensato un bel po' su, e son venuto fuori con questa idea: un minikernel in kernel mode, e un monolitico modulare in user mode.
Che roba è??
Il contrario di Windows NT, e cioè: invece di fare un microkernel e portarlo in kernel mode, prendo un monolitico e lo porto in user mode. :ugeek:
Il monolitico in user mode è formato da una parte fissa, e da moduli, in parte simili a delle librerie dinamiche, che vengono caricati e inglobati in questo processo user mode (che può anche avere dei thread). Ovvero: i tipici "server" di un microkernel sono inglobati in un unico spazio virtuale, e possono chiamarsi senza passare dal sistema di messaggi.

L'unico problema sono i driver, almeno nell'architettura x86: come sapete di base supporta l' I/O isolato, però non permette di usare le istruzioni in/out dall'user land senza usare quella stupidata dell'hardware task switching (che tra l'altro non è supportato in AMD64, o x86_64, il quale alla fine deve prendere il posto dell'x86). Se i dispositivi fossero mappati in memoria, sarebbe solo questione di mappare nello spazio virtuale del driver (in user mode), e tutto funzionerebbe.
Poichè non voglio per nessun motivo portare codice non fidato in kernel mode, ho pensato a un compromesso: per le periferiche "legacy" e sempre presenti (tastiera, mouse PS/2, PIC, PPT, PCI etc...), i driver saranno inseriti nel minikernel e fatti funzionare da li; uno potrebbe compilarsi altri driver in kernel mode (perchè l'interfaccia è la stessa dell'user mode), però se si rompe son cavoli suoi. ;)
Per tutti gli altri driver, si potranno generare dei messaggi destinati al minikernel, con indicate delle operazioni da compiere, che siano essi input, output, attendi tot tempo etc. Internamente si può spawnare un nuovo kernel thread che eseguirà queste operazioni, per poi tornare i risultati o gli errori al processo driver.
Ovviamente non tutti i programmi possono generare tali messaggi, solo i driver.


Ok, va bene la teoria, ma la pratica ??
In pratica non c'è nulla di tutto questo! :D
Attualmente il kernel si avvia (caricato da grub), il che è un buon inizio. ;)
Configura il processore, setta le tabelle degli interrupt, la gdt etc., può avviare la paginazione, ha un sistema di allocazione della memoria di blocchi di dimensione variabile (cioè malloc()), ha un sistema per gestire i cosidetti "device" (cioè una prima idea di separazione dei moduli, che non penso sarà usato come modello definitivo, magari forse solo a uso interno), può gestire un framebuffer VESA, e può visualizzare testo sia nella solita modalità caratteri 80x25, oppure disegnando in modalità grafica.
Nei sorgenti ci sono anche la vecchia implementazione della FAT(12), che è stata lasciata da parte, driver per floppy e vga che non hanno mai funzionato (quello vga un pochino), e un accenno di virtual file system, con terminologie e idee prese leggendo informazioni su Linux, ma che non ha attualmente nessuna funzionalità, perchè ho scoperto che forse era meglio fare la gestione della memoria prima del filesystem! :D

Qui il progetto su SourceForge:
http://sourceforge.net/projects/superkernel/ (http://sourceforge.net/projects/superkernel/)

E qui l'iniziale thread su Hardware Upgrade; è una bella lettura per chi ha un paio di orette libere. ;)
http://www.hwupgrade.it/forum/showthread.php?t=2112102 (http://www.hwupgrade.it/forum/showthread.php?t=2112102)

Ora sto sistemando i sorgenti per programmare anche il minikernel in C++, perchè i vantaggi superano il piccolo lavoro supplementare necessario per supportarlo.
Spero di poter fare un nuovo commit prima possibile.

E' tutto per ora, ditemi cosa ne pensate e se volete maggiori informazioni! :D
Titolo: Re: SuperKernel !
Inserito da: TheKaneB - 08 Agosto 2011, 03:55:48
Citazione da: "Z80Fan"
E' tutto per ora, ditemi cosa ne pensate e se volete maggiori informazioni!

Sei sicuro? :-D

In una discussione da bar ti direi "figo, stai facendo un sistema operativo? Micidiale!", ma in una discussione da NSA ti direi:

1- L'architettura del monolitico in Usermode esiste già (cercati L4/Linux e anche Usermode Linux)
2- L'architettura suddetta è una PorcataAbominevole®, si usa soltanto per migrare dei processi legacy, dipendenti da un vecchio kernel monolitico, su una struttura basata su microkernel. In alternativa può essere usata come sistema di virtualizzazione dei poveri, che condivide tutti gli svantaggi della virtualizzazione (lentezza,  problemi di scalabilità su sistemi SMP, cattiva gestione dell'I/O) e nessuno dei suoi vantaggi (un interrupt gestito male in user mode può fare esplodere l'intera macchina host)
3- Il driver model previsto fa pena: non devono essere i drivers a rompere le scatole al kernel, ma essi dovrebbero registrarsi presso un dispatcher che provvederà a diramare le richieste in base alle "classi di servizio" richieste. In caso di interrupt o syscall, il kernel provvederà a riconoscere la classe di servizio (DMA, VFS, Network, Multimedia, ecc...) e ad inoltrare la richiesta direttamente al driver interessato (che si sarà registrato precedentemente). In questo modo ogni driver avrà almeno un thread, tale thread sarà in wait per un messaggio del kernel (un banale semaforo su una coda di messaggi) e il kernel provvederà a svegliarlo pushando un messaggio sulla coda e sbloccando il relativo semaforo (e rischedulando).

Ho in mente un driver model molto particolare, che sto sviluppando nella mia testa e che in teoria dovrebbe risolvere in modo efficiente il problema dei driver in user space. Si basa sul concetto di "attore", cioè un fantoccio in kernel mode che possiede i meccanismi per interagire con l'hardware (un attore per il bus PCI, uno per il bus AGP, uno per la RS232, ecc...) ma non le policies.
Il driver in User Mode si occupa di scrivere il "copione" (il protocollo di comandi necessari per compiere una determinata operazione) e comunica questo copione al suo attore corrispondente.
Un driver può a sua volta registrarsi come attore. In tal caso girerà sempre in User Mode.

Tale architettura si presta chiaramente ad una modellazione ad oggetti, ed è pensata infatti per tale tipo di implementazione. Tuttavia il mio scopo era implementarlo in Objective Pascal (o similare) e non in C++. Comunque visto che mi hai stuzzicato, posterò qualcosa di concreto più in là :-)
Titolo: Re: SuperKernel !
Inserito da: TheKaneB - 08 Agosto 2011, 13:32:43
Il problema di Modula-2 è che gli unici compilatori che ho trovato sono per Amiga e non vengono sviluppati più da 15 anni...

Se trovi qualcosa di moderno che supporta ARM e x86_64 fammi un fischio :-)
Titolo: Re: SuperKernel !
Inserito da: TheKaneB - 08 Agosto 2011, 13:39:48
Citazione da: "dsar"
gnu modula-2 ed opencm3 sono sviluppati e mantenuti

http://www.nongnu.org/gm2/download.html (http://www.nongnu.org/gm2/download.html)

ci sono i source per GCC 4.1.x
mi toccherebbe mantenere una vecchia toolchain basata su gcc 4.1 per poter utilizzare questo frontend... vediamo un po' che si può fare...

OpenCM3 invece sembra un pelo più aggiornato, ma la release 1.5 fatta dopo 9 anni dalla 1.4 e con un changelog scarsetto, mi fanno pensare ad un progetto morto o semi-morto... Ecco perchè mi ero buttato su FreePascalCompiler, più che altro per la sicurezza di avere un compilatore supportato ed aggiornato di continuo...
Titolo: Re: SuperKernel !
Inserito da: Z80Fan - 08 Agosto 2011, 16:59:09
Citazione da: "TheKaneB"
1- L'architettura del monolitico in Usermode esiste già (cercati L4/Linux e anche Usermode Linux)
Non ho mica detto che è una cosa nuova e innovativa. In secondo luogo, per essere più chiaro, l'architettura mia non è
Codice: [Seleziona]
applicazioni
userkernel
--------------
minikernel
ma è
Codice: [Seleziona]
applicazioni - userkernel
--------------
minikernel
Quindi l'userkernel è come un'altra applicazione per il minikernel (con alcuni compromessi, ovvio), e le applicazioni lo vedono come un fornitore di servizi.
I driver poi sono processi separati, non inclusi nell'userkernel.

Citazione
2- L'architettura suddetta è una PorcataAbominevole®, si usa soltanto per migrare dei processi legacy, dipendenti da un vecchio kernel monolitico, su una struttura basata su microkernel.
Tralasciando la virtualizzazione dei poveri, non ho capito gli svantaggi di questa architettura.

Citazione
3- Il driver model previsto fa pena: non devono essere i drivers a rompere le scatole al kernel, ma essi dovrebbero registrarsi presso un dispatcher che provvederà a diramare le richieste in base alle "classi di servizio" richieste. In caso di interrupt o syscall, il kernel provvederà a riconoscere la classe di servizio (DMA, VFS, Network, Multimedia, ecc...) e ad inoltrare la richiesta direttamente al driver interessato (che si sarà registrato precedentemente). In questo modo ogni driver avrà almeno un thread, tale thread sarà in wait per un messaggio del kernel (un banale semaforo su una coda di messaggi) e il kernel provvederà a svegliarlo pushando un messaggio sulla coda e sbloccando il relativo semaforo (e rischedulando).
Che è quello che voglio fare io. Pensavi forse che i drivers stessero in un loop per chiedere lo stato della periferica? :P
Io ti ho descritto come voglio fare per superare il fatto che non posso fare operazioni di I/O dall'user mode, quindi le devo far fare al minikernel.

Citazione
Ho in mente un driver model molto particolare, che sto sviluppando nella mia testa e che in teoria dovrebbe risolvere in modo efficiente il problema dei driver in user space. Si basa sul concetto di "attore", cioè un fantoccio in kernel mode che possiede i meccanismi per interagire con l'hardware (un attore per il bus PCI, uno per il bus AGP, uno per la RS232, ecc...) ma non le policies.
Il driver in User Mode si occupa di scrivere il "copione" (il protocollo di comandi necessari per compiere una determinata operazione) e comunica questo copione al suo attore corrispondente.
Un driver può a sua volta registrarsi come attore. In tal caso girerà sempre in User Mode.
Similmente a come voglio fare io! :D
Nel primo post ho descritto solo l'accesso alle porte di I/O, forse avrei dovuto parlare un po' più in generale. Diciamo che quello che ho descritto è un attore che opera sulle porte di I/O e riceve le richieste ("copioni") dai driver che hanno necessità di fare operazioni specifiche sulla periferica di loro competenza, che non possono fare attraverso altri attori (appunto PCI, AGP etc., che erano le "periferiche legacy e sempre presenti" di cui parlavo).

Citazione
Tale architettura si presta chiaramente ad una modellazione ad oggetti, ed è pensata infatti per tale tipo di implementazione.
Proprio per questo ho voluto cominciare a usare il C++, usare gli oggetti porta troppi vantaggi rispetto a quelle poche cose che è necessario implementare.
Titolo: Re: SuperKernel !
Inserito da: cdimauro - 08 Agosto 2011, 18:33:57
Non farti sentire da Torvalds... :lol:

@dsar: c'è qualche motivo per cui non apprezzi / ti piaccia FreePascal? Fra gli eredi del Pascal dopo quelli di casa Borland è fra i più gettonati e disponibile per parecchie piattaforme (persino per il GameBoy Advance, se non ricordo male).
Titolo: Re: SuperKernel !
Inserito da: Z80Fan - 21 Agosto 2011, 17:06:23
Pian pianino, ho fatto delle modifiche: ora il kernel compila in C++ tranquillamente (ci sono ancora tutti i warning delle conversioni puntatore->intero, ma li sistemerò prima o poi, si tratta di fare qualche cast).
Gli oggetti funzionano tranquillamente, possono essere anche globali e del codice aggiuntivo si preoccupa di chiamare i costruttori e i distruttori.

Ho fatto una classe ostream, e istanziato un oggetto kout. :D

Ora sto lavorando su un blocco chiamato virtual memory manager, che gestisce i vari spazi virtuali; mi serve per poter gestire i moduli caricati all'avvio: avrò uno spazio virtuale per il kernel, dove mapperò (probabilmente partendo dalla fine della memoria) il modulo.
Ovviamente si riutilizza poi per gestire gli spazi virtuali dei processi.
Titolo: Re: SuperKernel !
Inserito da: Z80Fan - 12 Novembre 2011, 20:41:25
Ho un problema da proporvi:
Stiamo migliorando il codice, "riscrivendolo" a oggetti. Fino a adesso abbiamo creato la classe Device, e creato la classe Keyboard che eredita da Device. Mentre scrivevo la classe Keyboard, ho pensato di creare una classe anche per rappresentare una ISR (Interrupt Service Routine), per poter usare gli oggetti nella gestione degli interrupt. Ho quindi creato la classe virtuale GenericISR che dispone di un metodo handle().

Fino a qui, tutto bene. Ora c'è un dettaglio delicato da discutere:
Prendendo sempre Keyboard come esempio: inizialmente, avevo fatto derivare Keyboard sia da Device, sia da GenericISR (con ereditarietà multipla). Però, pensandoci bene, lei è-una Device, e ha-una ISR. Quindi GenericISR sarebbe un membro di Keyboard.

Ma non posso usare direttamente GenericISR, perchè è virtuale pura. Dovrei creare una classe tipo KeyboardISR, e usare quella come membro di Keyboard.
Il programmatore del driver dovrebbe fare una "doppia" fatica (perchè deve creare due classi), ma vabbè, lo deve fare una volta sola...

Problema ultimo: la ISR, per essere un qualche modo utile, dovrebbe poter accedere ai dati di Keyboard. Ora però l'handler è in una classe diversa!
Bisognerebbe passare a KeyboardISR il puntatore this a Keyboard, e poi rendere KeyboardISR una friend di Keyboard, o creare metodi per accedere ai dati, che però potrebbero potenzialmente essere usati dall'esterno, cosa che non si desidera...

Pensate che stia facendo un gran casino? Io penso di si! :D
Avete qualche idea di come impostare la faccenda? Questa parte è abbastanza essenziale, quindi bisogna pensarla bene prima di continuare.
Titolo: Re: SuperKernel !
Inserito da: Z80Fan - 13 Novembre 2011, 18:48:11
Citazione da: "dsar"
Data la moltitudine di device che ci saranno ti consiglio di creare una classe Device e una classe DeviceFrames che conterrà la lista dei device type presenti nel kernel (non so se conosi la tecnica).
Probabilmente ho già una cosa simile: ho i vari "Device" e poi un DeviceManager che contiene la lista dei device che vengono registrati.

Citazione
Le classi IS le devi organizzare come single-inheritance (gerarchico), l'object composition HAS è safe usarlo con il multiple inheritance bloccato ad un livello.
Si, alla fine "Keyboard" (o qualsiasi altro device) non sarà più virtuale, quindi non c'è problema nell'usare ereditarietà multipla.

Citazione
In ogni caso l'object composition è una tecnica alternativa di encapsulation all'inheritance, quindi ciò che dici tu non si può fare (il composed object non può vedere in alcun modo gli state di chi lo possiede).
Infatti. A dire il vero, il fatto che la classe "interna" debba poter vedere l'"esterna" mi è venuto in mente mentre scrivevo il post, quindi lo ho scritto lo stesso, anche se non mi piaceva per niente.

Citazione
E' meglio organizzarlo come independent component del kernel (un servizio) in cui tu registri i Device (ecco che qui la classe DeviceFrames ti viene in aiuto). Mettere l'handler come componente della classe device la vedo problematica come soluzione, anche perché per gli usi futuri che farai degli ISR ti servirebbe osservare gli state anche di altri device

Quindi, se ho capito bene, dici di: mettere un metodo di handling nel Device (o inserendolo come funzione virtuale in Device e gestire un'ereditarietà singola, o come virtuale pura in GenericISR e eredità multipla Device+GenericISR); il device che ha bisogno di gestire un interrupt si registra; Il DeviceFrames (o Manager, quel che sia), al momento dell'interrupt, chiama il device che si è registrato per quel IRQ?

Se intendi questo, è proprio quello che volevo fare. :D
Titolo: Re: SuperKernel !
Inserito da: Z80Fan - 19 Novembre 2011, 00:54:10
Eccomi, scusa ma nei giorni precedenti avevo da fare.

Citazione da: "dsar"
No, non ti conviene mettere in alcun modo quegli oggetti in relazione, perché poi trovi difficoltà a gestire casi come per esempio più device nello stesso interrupt,
Non ho sentito casi di periferiche diverse sullo stesso IRQ number; ma anche in questa evenienza, quando una periferica si registra per un certo IRQ, la segno in un vettore insieme a tutte le periferiche che si son registrate in precedenza (per quel IRQ number); quando avviene un interrupt, attivo le diverse routine in sequenza finchè non mi trovo una routine che mi dice che l'interrupt era di una sua periferica e lo ha gestito.
Dovrò si scorrere le routine in sequenza, ma non mi aspetto di avere più di 2-3 device contemporaneamente mappati a uno stesso IRQ.

In un ipotetico sistema dove tutte le periferiche condividono un bus e hanno una sola linea di interrupt (potenzialmente PCI o USB? non lo so), conviene molto di più creare un Device "bus master", e gli altri Device dedicati a ogni periferica si registrano a lui (in qualche modo).


Citazione
Il problema è che l'interrupt handler dovrebbe avere certi gradi di libertà sui device, non sottostare ai device.
Ma scusa, ogni handler è specifico per ogni periferica; l'handler del controller SATA è specifico solo per il Device "sata_controller", come l'handler per la tastiera è specifico solo per il Device "keyboard".
Se per "interrupt handler" intendi la primissima routine che viene chiamata al momento di un interrupt: quella è già indipendente, ed è quella che controlla quale IRQ è scattato e richiama gli handler nei Device che si sono registrati per quel determinato IRQ durante la loro inizializzazione.

Citazione
Come ho detto prima conviene che gestisci l'interrupt handler come un componente indipendente del kernel in cui registri i device. Ogni interrupt handler all'interno del componente ha un DeviceFrame con tutti i device che dipendono da quell'interrupt.
Le comunicazioni tra device ed interrupt handler avvengono con un metodo Signal o Notify (scegli tu) del device, in cui (come parametro di output) comunica quello che deve comunicare all'interrupt handler.

A proposito di polling dato che con gli interrupt li userai tantissimo conviene che gestisci il tuo design OOP sugli interrupt in maniera diversa. Creare una classe KernelEvent (ti servirà parecchio in futuro) da cui derivare poi InterruptEvent. I device la comunicazione a segnali la gestiranno tramite InterruptEvent dichiarandone una propria, per esempio InterruptEventKeyboard (per la tastiera), che in più avrà i flagset o quel che sia che serviranno all'interrupt handler della tastiera.

Il tuo componente per la gestione degli interrupt handler dovrà solo interpretare gli InterruptEvent dei vari device registrati.
Quello che mi sfugge (ed è il motivo per il quale non riesco a comprendere pienamente la tua tecnica) è: dov'è che viene messo il codice dell'handler? Banalmente: dov'è che leggo la porta della tastiera per leggere il tasto premuto?
Nell' InterruptEventKeyboard? Quindi lo scrittore deve predisporre 2 classi, ok. Però l'InterruptEventKeyboard poi come fa ad accedere ai dati di Keyboard (per registrare nel buffer il tasto premuto ad esempio) ?
Titolo: Re: SuperKernel !
Inserito da: Z80Fan - 25 Novembre 2011, 22:51:37
Citazione da: "dsar"
Non c'eri ai tempi di Windows 95 che non sapeva gestire gli IRQ condivisi? :P
Si, ma avevo 4 anni :P.

Citazione da: "dsar"
Qui sono stato poco chiaro io :P intendo che non è il device che deve stabilire la comunicazione, ma qualcun altro.
Che l'handler sia specifico per ogni periferica è chiaro, io intendevo che chi gestisce l'interrupt deve essere uno solo.
Ho già una singola unità che mi gestisce gli interrupt; il mio problema non è di architettura, ma semplicemente come organizzare i metodi all'interno degli oggetti.

Citazione
Su FreeBSD la componente del kernel che gestisce gli interrupt è un framework basato sui bus ed i resource, che sono molto generici. Questo permette di separare i dettagli dell'architettura dalla periferica e dal tipo di comunicazione (pci, usb, etc).
Questo è una buona cosa, vedrò anche io di fare una cosa simile.

Citazione
Quando scrivi un driver su freebsd, l'interrupt handler lo scrivi tu ma poi viene gestito dal framework del bus:
http://www.freebsd.org/cgi/man.cgi?query=bus_setup_intr (http://www.freebsd.org/cgi/man.cgi?query=bus_setup_intr)

int BUS_SETUP_INTR(device_t dev, device_t child, struct resource *irq, int flags, driver_filter_t *filter, driver_intr_t *ithread, void *arg, void **cookiep);

con BUS_ALLOC_RESOURCE crei e riservi una risorsa interrupt e dopo gli assegni l'handler con il parametro driver_intr_t *ithread, la comunicazione verrà gestita dal bus manager.
Ecco, con quella funzione colleghi un device, un handler e un IRQ assieme, e se il mio kernel fosse in C, sarebbe come avrei fatto: un puntatore al device, uno alla funzione che fa da handler, e un'indicazione per l'IRQ che desidero. Non c'è nessun problema, perchè quando la funzione ha il puntatore al device, può fare quello che vuole.
Ma io sto scrivendo in C++, e voglio avere un sistema a oggetti. Se facessi in quel modo (avendo un oggetto device, un oggetto intHandler e il numero dell'irq), dovrei mettere i dati interni del device come pubblici, in modo che l'oggetto intHandler possa accedervi; ciò però non permette al device di proteggere i suoi dati da codice esterno che con lui non ha nulla a che fare. Non posso neanche mettere delle funzioni get/set perchè non devono poter essere usate da tutti, ma solo da intHandler. Se gli oggetti fossero separati, dovrei almeno dichiarare intHandler come friend di device, ma ciò non mi entusiasma molto. La seconda opzione era quella di creare un singolo oggetto che eredita sia da device sia da intHandler, così i dati interni di device si possono accedere da intHandler (o meglio, dal metodo handle() che viene richiamato dal gestore centrale degli interrupt quando si è verificato l'interrupt richiesto).

Citazione
Io sono newbie sul fronte sistema operativi, ho letto qualche libro ma non ho mai implementato nulla di serio.
Non ti preoccupare, sei stato lo stesso di grande aiuto. :)

Citazione
Secondo me dovresti basarti su qualche driver model esistente,
Questa discussione mi ha spinto a cercare più informazioni, possibilmente degli schemi. Di FreeBSD ho trovato poco, però ho trovato dei documenti che spiegano l'I/O kit di MacOS X (http://http://developer.apple.com/library/mac/#documentation/DeviceDrivers/Conceptual/IOKitFundamentals/ArchitectOverview/ArchitectOverview.html) e su Windows (http://http://technet.microsoft.com/en-us/library/cc776371%28WS.10%29.aspx). Ho dato un'occhiata anche a WDF, che da un esempio di driver base non sembrava male, ma non mi piace il fatto che hanno progettato un sistema object-oriented e poi gli hanno dato un'interfaccia in C. D'accordo che volevano prendere la maggioranza dei programmatori, ma non penso sarebbe stato un trauma per i programmatori C imparare quelle 4 cose di C++ necessarie per scrivere il driver.

Citazione
magari quello che ti piace di più ed ha il più basso rapporto pregi/difetti
Forse volevi dire "il più alto rapporto pregi/difetti" :P.
Titolo: Re: SuperKernel !
Inserito da: cdimauro - 26 Novembre 2011, 07:26:45
Citazione da: "Z80Fan"
Ho dato un'occhiata anche a WDF, che da un esempio di driver base non sembrava male, ma non mi piace il fatto che hanno progettato un sistema object-oriented e poi gli hanno dato un'interfaccia in C. D'accordo che volevano prendere la maggioranza dei programmatori, ma non penso sarebbe stato un trauma per i programmatori C imparare quelle 4 cose di C++ necessarie per scrivere il driver.
Perché non c'è solo il C++. Un'interfaccia "C" è utilizzata come "lingua franca" per esporre API "consumabili" da tutti gli altri linguaggi.

Se, invece, definisci un'interfaccia C++ per esporre le tue API, queste saranno "consumabili" da ben pochi linguaggi.
Titolo: Re: SuperKernel !
Inserito da: Z80Fan - 26 Novembre 2011, 15:35:20
Citazione da: "cdimauro"
Perché non c'è solo il C++. Un'interfaccia "C" è utilizzata come "lingua franca" per esporre API "consumabili" da tutti gli altri linguaggi.

Se, invece, definisci un'interfaccia C++ per esporre le tue API, queste saranno "consumabili" da ben pochi linguaggi.

Ora non voglio iniziare un'altro discorso su quale linguaggio è il più figo, ma in cosa li vorresti scrivere dei Device Driver?
Titolo: Re: SuperKernel !
Inserito da: cdimauro - 26 Novembre 2011, 15:39:08
Delphi o FreePascal.
Titolo: Re: SuperKernel !
Inserito da: TheKaneB - 26 Novembre 2011, 15:41:02
Citazione da: "cdimauro"
Delphi o FreePascal.
:clap:  :banana-linedance:
Titolo: Re: SuperKernel !
Inserito da: Z80Fan - 26 Novembre 2011, 19:15:37
Citazione da: "cdimauro"
Delphi o FreePascal.
E sono dei linguaggi talmente usati nello sviluppo di driver da obbligare a complicare l'interfaccia per poterli supportare?
Oppure solo una minoranza di driver è programmata così, e si potrebbe usare un wrapper, qualcosa come questo (http://http://rvelthuis.de/articles/articles-cppobjs.html) ?

Poi è da vedere dove il driver deve essere eseguito. Se il modulo sta in user mode, uno può usare il linguaggio che vuole perchè ci saranno le apposite API, che penso saranno quasi sicuramente in stile C.

Attualmente sto implementando dei "driver" che stanno in kernel mode, quindi è plausibile che siano scritti nello stesso linguaggio del kernel.
Titolo: Re: SuperKernel !
Inserito da: cdimauro - 26 Novembre 2011, 19:32:11
Citazione da: "Z80Fan"
Citazione da: "cdimauro"
Delphi o FreePascal.
E sono dei linguaggi talmente usati nello sviluppo di driver da obbligare a complicare l'interfaccia per poterli supportare?
Oppure solo una minoranza di driver è programmata così, e si potrebbe usare un wrapper, qualcosa come questo (http://http://rvelthuis.de/articles/articles-cppobjs.html) ?
Non vedo perché ci si dovrebbe abbassare a usare dei wrapper: è una gran rottura di scatole!

Il punto è un altro: perché un s.o. dovrebbe essere C++ centrico?

Un s.o. dovrebbe essere agnostico dal punto di vista dell'ABI utilizzata per i vari linguaggi, ed è per questo che si fornisce un "minimo comune denominatore" che è rappresentato da un'interfaccia C o Pascal (ebbene sì: si usa anche questa; buona parte delle API di Windows usano questo modello, e la usavano pure le API del vecchio MacOS).
Citazione
Poi è da vedere dove il driver deve essere eseguito. Se il modulo sta in user mode, uno può usare il linguaggio che vuole perchè ci saranno le apposite API, che penso saranno quasi sicuramente in stile C.

Attualmente sto implementando dei "driver" che stanno in kernel mode, quindi è plausibile che siano scritti nello stesso linguaggio del kernel.
Chiaro, questa è una scelta che hai fatto tu. Comunque se ti limitassi all'ereditarietà singola, probabilmente potresti abbracciare buona parte dei linguaggi che supportano il paradigma OOP.
Titolo: Re: SuperKernel !
Inserito da: Z80Fan - 21 Gennaio 2012, 19:11:07
Ho aggiornato il codice nel repository principale; la nuova versione 0.06 (che nel repo git corrisponde al branch cpp_conv) si può scaricare da Sourceforge al link:
https://sourceforge.net/projects/superkernel/ (https://sourceforge.net/projects/superkernel/)

Principalmente questa versione porta il nuovo sistema OOP per le periferiche (classe Device) e gli interrupt (classe GenericISR), più qualche "pulitura" di diversi file.
Non è detto che l'implementazione corrente di questi due sistemi sia definitiva.

Prima di poter eseguire il merge tra cpp_conv e il branch principale, devo pulire ancora diversi file, ad esempio mettendo i namespace giusti, commenti etc...

Manca ancora da fare la gestione della memoria in OOP (principalmente la classe "vspace" per gestire ogni singolo spazio virtuale che andrà a ospitare un singolo processo), e successivamente (o anche contemporaneamente) fare una gestione dei processi, anche una cosa semplice come il round-robin giusto per iniziare.
Titolo: Re: SuperKernel !
Inserito da: cdimauro - 21 Gennaio 2012, 19:42:40
Cerca di dare una sistemata anche ai commenti appena puoi: un misto di italiano e inglese non è proprio il massimo. :oops:
Titolo: Re: SuperKernel !
Inserito da: Z80Fan - 21 Gennaio 2012, 20:14:03
Citazione da: "cdimauro"
Cerca di dare una sistemata anche ai commenti appena puoi: un misto di italiano e inglese non è proprio il massimo. :oops:
Infatti; la sistemazione comprende anche quello...
Titolo: Re: SuperKernel !
Inserito da: Allanon - 30 Gennaio 2012, 00:00:45
Citazione da: "cdimauro"
Cerca di dare una sistemata anche ai commenti appena puoi: un misto di italiano e inglese non è proprio il massimo. :oops:

...questa cosa mi perseguita inesorabilmente... quando programmo spesso in una sessione metto commenti in italiano e la volta dopo li metto in inglese... :roll:
Titolo: Re: SuperKernel !
Inserito da: cdimauro - 30 Gennaio 2012, 09:10:00
Per questo è meglio partire direttamente coi commenti in inglese :D

Comunque non seguo fedelmente questo schema. Ad esempio se sviluppo un'applicazione per il mercato locale (e sono anche sicuro che rimarrà tale), utilizzo commenti e identificatori in italiano per tutta la parte che è strettamente di business-logic (ad esempio Clienti, Fornitori, CodiceFiscale, ecc.) mentre per tutto quello che potrebbe essere riciclato in un'altra applicazione e/o finire poi in una libreria uso solo l'inglese.

Ormai dopo tanti anni sono abituato a lavorare così, ma in ogni caso l'inglese domina sicuramente.
Titolo: Re:SuperKernel !
Inserito da: Z80Fan - 06 ſettembre 2013, 03:22:41
Beh, tutti i messaggi più nuovi si son persi, vabbè...

Ho pensato di riprendere lo sviluppo (che magari lo uso anche come tesi di laurea, se non hanno niente in contrario), però devo farvi un RFC:

Ricapitolando la situazione attuale, ho lasciato perdere la "conversione" C++ che stavo facendo dai vecchi sorgenti, e ho ricominciato da capo. Ho ben diviso i compiti di kernel/architettura/piattaforma in modo che sia facilmente portabile, e in generale ho fatto le cose più "object oriented".
Ho riportato tutte le cose essenziali per avviare il sistema, praticamente quello che manca per tornare alle "feature" del vecchio kernel sono i driver delle periferiche (tastiera, video etc).

In questa versione però, mi serve avere un sistema prima di implementare i driver, ed è un sistema di message passing.
Praticamente ho deciso che il message passing sarà una funzione fondamentale in questo kernel, su cui fare praticamente un po' tutto, incluse la maggioranza di API user->kernel (o syscall come vengono chiamate). Su questo poi si costruisce un sistema di RPC etc.

Ovviamente la prima cosa che viene in mente, sono le prestazioni; certamente dover scendere a messaggi per quasi tutto sarà più pesante di non fare delle syscall con interrupt direttamente, però ne viene di flessibilità (si possono aggiungere funzioni successivamente senza infastidire le applicazioni esistenti), e una generale ortogonalità (un programma chiama servizi del kernel allo stesso modo con cui chiama servizi di altri processi o driver).
Una cosa bella è che, in stile Wayland, un protocollo si può definire in XML, e da questo avere un tool che genera l'interfaccia adatta per un particolare linguaggio, così ogni linguaggio può avere l'API fatta in suo stile, ma sempre compatibile col sistema (perchè tanto poi passa tutto per i messaggi) (questo risolve anche quel dibattito che ho avuto con cdimauro sul fatto che uno vorrebbe poter scrivere i driver nel linguaggio che preferisce).
In più, OS ben testati come QNX (un microkernel hard realtime), usano abbondantemente il message passing per implementare RPC, e con uno scheduling intelligente ottengono anche buone prestazioni e latenze.

Ma quindi cosa dovrebbe fare sto sistema? Come ho detto, viene usato sia per user->kernel, sia per kernel->kernel (nel senso di kernel<-> driver in kernel mode), sia user->user. Quando un processo vuole mandare un messaggio a un altro processo, lo costruisce e informa il kernel (con una syscall tipica, probabilmente una delle poche che serviranno ancora, oppure tramite una libreria standard che poi può segnalare il kernel nel modo che preferiamo); quest'ultimo ha l'infrastruttura per direzionare il messaggio e portarlo nella memoria del destinatario, il quale viene avvisato dell'arrivo (in modo indiretto, tramite una variabile nel suo spazio che il processo può testare in un ciclo di eventi, oppure deviando il flusso del processo su una sua funzione di gestione che il processo può registrare).

Ci sono 2 modalità di passaggio che bisogna implementare: il trasferimento semplice, dove un messaggio da un processo viene copiato nel mailbox di un altro processo, e il processo mittente è libero di continuare (messaggi unidirezionali o RPC asincrono), e il trasferimento con attesa, dove il processo mittente viene bloccato finchè il destinatario non risponde con un altro messaggio o finchè non scade un timeout.

Poi, c'è da pensare a come rappresentare i dati nel messaggio: il modo più semplice è considerarlo solo un'area di dati binari con un header, e lasciare che le applicazioni decidano cosa metterci dentro; il modo più avanzato è "formattare" i dati che ci vanno dentro in triplette nome-tipo-valore, ed eseguire marshalling/unmarshalling nel mittente/destinatario.
Il primo ha il vantaggio di essere molto semplice e veloce, mentre il secondo è più complesso però permette di fare cose carine tipo poter mandare i messaggi, attraverso lo stesso sistema, a un'altra macchina (magari collegata in rete), per fare simpatici sistemi cluster.
Sinceramente, poichè il secondo sistema si può costruire sopra il primo, conviene tenere le cose semplici ed avere solo un header con i campi fondamentali per il routing del messaggio (inclusa la sua dimensione), e lasciare la zona dati come area binaria arbitraria.

Infine, bisogna fare un modo per far "scoprire" alle applicazioni i servizi del sistema, ma quello non è un problemone perchè si può sempre avere un processo server sempre attivo, con un'API definita, a cui si può parlare sempre usando i messaggi, e da lui le app possono trovare i servizi disponibili nel sistema.


Date queste specifiche, non è difficile implementare un tale sistema, però c'è da fare un'attenta progettazione per non cadere in dei pitfall; ad esempio, bisogna pensare come vengono rappresentati e gestiti gli indirizzi dei destinatari.

Un modo banale è usare il PID del processo come suo identificatore nel sistema di messaggi, magari con il valore 0 assegnato al kernel; però è facile trovare subito dei problemi: già di progettazione, si limita una singola mailbox (i.e. un posto dove i messaggi possono essere bufferizzati) per processo, quando un sistema complesso potrebbe volerne diverse per diversi thread, senza aver bisogno di un ciclo principale che raccoglie messaggi dalla mailbox centrale e li ricopia in buffer privati di ogni thread. Poi, se un server crasha e viene riavviato con un altro PID, non possiamo avere applicazioni che hanno ancora il vecchio PID e mandano messaggi con informazioni potenzialmente sensibili a processi casuali (neanche il fatto di non riciclare i PID subito è una soluzione).

Allora ogni thread che volesse fornire un punto di ingresso per i messaggi dovrebbe richiedere un ID, che viene usato come mittente/destinatario, però: il problema dei PID sopra si ripete, perchè se un server cade e si riavvia, potenzialmente richiederà un nuovo ID, a meno che non ci sia un modo di riaverlo...
Si potrebbe fare in questo modo: un server che vuole registrarsi come potenziale server richiede al kernel un ID, passandogli una stringa identificativa che vuole lui; la stringa (documentata) la useranno anche i mittenti per recuperare l'ID del server (una sorta di DNS); nel caso il server cada (per crash o per volontario riavvio), quando riparte e chiede un ID con la stessa stringa, il kernel fornirà lo stesso ID di prima (si tiene la lista delle associazioni per un po' anche quando il processo termina). Qua però nasce un problema di sicurezza, ovvero un programma maligno potrebbe far crashare il server (o attendere che termini per i fatti suoi) e poi immediatamente richiedere la stringa del server, e farsi dirottare le richieste (stile man-in-the-middle).
Una possibile soluzione è richiedere che richieste consecutive derivino dallo stesso identico eseguibile della richiesta precedente; ciò rompe quando uno vuole fare un aggiornamento a un altro server equivalente, ma non vuol dire che dobbiamo tenere l'associazione per sempre, solo per un certo time-out dove il precedente eseguibile ha ancora l'esclusiva dell'ID (come avviene già per le porte di rete, dove sono assegnate per un certo periodo anche se il server cade, proprio in modo da evitare che altri dispositivi continuino a inviare dati a un server diverso avviato dopo).

Probabilmente quest'ultima soluzione è la migliore in generale. Sopra questo ora però bisogna pensare a come due processi che vogliono comunicare in modo totalmente privato possano gestire questa opzione; ad esempio si potrebbe voler che un driver in kernel mode possa parlare solo con il relativo server in user-mode, senza che nessun altro si possa connettere, però a questo livello penso sia da lasciare alle singole applicazioni gestirsi, anche perchè, come fa uno a sapere che il client che ti si connette è effettivamente quello che vuoi tu o qualcun altro? Se tutta sta segretezza è richiesta, sarà un problema delle applicazioni validarsi e criptare le comunicazioni... (soluzioni a questo problema sono però ben accette).



Ci saranno sicuramente altre cose a cui ho pensato ma che in questo momento non mi tornano in mente, perciò mi fermo qui. Ho scritto tanto ma ho buttato giù tutto a quello che avevo già pensato, quindi è una specie di riassunto del mio pensiero. È anche abbastanza raw come output, quindi è probabile che ci siano errori o punti non chiari. ;D

Suggerimenti/domande/insulti son tutti ben accetti, perchè essendo questo forse il componente più importante di questo OS deve essere fatto bene già dell'inizio, altrimenti rischiamo di fare tanto lavoro per poi doverlo buttare via e rifarlo.
Titolo: Re:SuperKernel !
Inserito da: cdimauro - 06 ſettembre 2013, 21:38:06
Per il momento il problema degli ID delle mailbox rimandalo. Assumi che a ogni eseguibile / applicazione sia associato un ID univoco, e sei a posto per ora.

Riguardo al message passing, non lo userei sempre. Francamente mi affiderei ai registri della CPU, riservandone un po' alla scopo quando c'è da effettuare una syscall. Messaggi più complessi useranno il classico paradigma ID messaggio + lunghezza + dati. In pratica l'ID del messaggio ti indica se è semplice o complesso; ad esempio usando il bit più significativo.
Titolo: Re:SuperKernel !
Inserito da: Z80Fan - 07 ſettembre 2013, 00:51:37
Per il momento il problema degli ID delle mailbox rimandalo. Assumi che a ogni eseguibile / applicazione sia associato un ID univoco, e sei a posto per ora.
Si, come hack per continuare lo sviluppo si può fare, ma volevo avere un'immagine completa del sistema prima di partire.

Riguardo al message passing, non lo userei sempre. Francamente mi affiderei ai registri della CPU, riservandone un po' alla scopo quando c'è da effettuare una syscall. Messaggi più complessi useranno il classico paradigma ID messaggio + lunghezza + dati. In pratica l'ID del messaggio ti indica se è semplice o complesso; ad esempio usando il bit più significativo.
Non sono d'accordo nel "non usare sempre il message passing", anche se qui potremmo scontrarci sulla definizione di "message passing", con cui non intendo per forza riempire un'area di memoria e copiarla nello spazio di un altro processo; una syscall lo considero un messaggio, anche se poi decidiamo di farlo passare per i registri della cpu.

Le applicazioni (e i driver) parleranno sempre con una libreria (che risiede sempre nel loro spazio di indirizzamento), libreria che l'OS stesso fornisce, e serve per disaccoppiare l'implementazione sottostante con l'API standard del sistema.
In questa libreria possiamo attuare tutte le ottimizzazioni che vogliamo: un esempio a cui ho già pensato è un buffer di messaggi da inviare, se questi messaggi non richiedono una risposta sincrona (i.e. chiamano servizi asincroni). Quando il processo finisce il suo timeslice, rilascia volontariamente la cpu (magari in attesa di un semaforo), o invia un messaggio che richiede una risposta sincrona, il kernel viene richiamato e, già che deve fare un context switch, sposta i messaggi dal buffer ai relativi destinatari. Ciò ci risparmia di dover andare avanti e indietro dal kernel per ogni messaggio la cui consegna magari non è immediatamente importante.

In secondo luogo, non so quanto più veloce sia il passaggio di messaggi via registri, per il seguente motivo: quando l'esecuzione passa al kernel, quest'ultimo deve lo stesso salvare i valori dei registri in una qualche struttura di stato del thread interrotto, perchè anche se fosse lui il destinatario dell'azione, è probabile che debba eseguire molto codice per soddisfare la richiesta, e anche un sottoinsieme di registri lasciato libero per gli usi del kernel non è sufficiente oltre ai primissimi istanti di entrata in kernel mode.
Diverso invece è se ammettiamo che il controllo possa tornare al thread utente con i registri dati sporcati (non necessariamente tutti, anche solo un sottoinsieme): in questo caso il kernel può usare i valori direttamente e lavorarci sopra, senza necessità di salvare e ripristinare i registri.

Con il fatto che il programma utente lavora sempre attraverso la libreria base, è però possibilissimo implementare "dietro le quinte" il sistema doppio registri-memoria come dici te, dove la libreria può scegliere se passare il messaggio via registri oppure via memoria, non solo in base alla dimensione ma in generale la miglior soluzione per quel momento (es. se il messaggio è asincrono, anche se è corto lo vorremo mettere nel buffer di messaggi, sulla base che un accesso in memoria è più veloce che entrare nel kernel e fare il trasferimento subito con i dati nei registri). In un'architettura con pochi registri (come x86), magari il trasferimento a registri non verrà nemmeno preso in considerazione.
Quindi possiamo fare in questo modo: la libreria fornisce un singolo modo di trasferimento di messaggi all'utente (nel senso, un solo formato, poi ovviamente ci sono tutti i modi sincrono/asincrono etc.), e poi sceglierà internamente il modo migliore di inviarlo.

La libreria, oltre alle funzioni di trasferimento di messaggi, espone i servizi del kernel attraverso funzioni facili da usare (es. open(), read() etc.); qui possiamo imbrogliare spudoratamente e creare delle syscall "scorciatoia" che bypassa il sistema di messaggi e attiva direttamente la funzionalità (chiamate sincrone) oppure inserisce un job per i thread kernel di background (chiamate asincrone) direttamente nelle strutture dati del kernel invece che fare il doppio salto parsing messaggio->inserimento nella queue del thread background.

questa soluzione e' usata con successo anche dal vecchio VxWorks v5 che ho sulla 2x68060 board di Eltec.

Il passaggio tramite registri dici?
Titolo: Re:SuperKernel !
Inserito da: cdimauro - 07 ſettembre 2013, 07:26:00
   E' chiaro che per i servizi asincroni serve un buffer di memoria dove depositare il messaggio.
La mia soluzione riguarda soltanto quelli sincroni (non so se legacy intendesse anche questo; suppongo di sì), dove è fondamentale ridurre al minimo la latenza. Il kernel alla fine può decidere di farci quello che vuole coi registri, anche parcheggiarli in uno spazio temporaneo se gliene serve qualcuno (non è che deve per forza memorizzarli tutti; anche qui, si ottimizza al massimo: si conservano solo quelli che servono).

Per quelli asincroni ho pensato a una soluzione a doppio buffer per i risultati. Il processo / thread utente si smazza un buffer (coda) tutto in user-space, senza interferenze da parte del kernel; si tratta di una lista di messaggi che ha già caricato il kernel tempo fa, e che il processo può smazzarsi direttamente senza usare lock, semafori, ecc. Nel frattempo il kernel riempie il secondo buffer con le nuove richieste, in maniera del tutto indipendente. Quando il processo finisce tutti i messaggi del primo buffer, invoca una syscall; a questo punto il kernel scambia i due buffer (scambia i puntatori alle due aree) e restituisce al processo il puntatore al nuovo buffer che deve processare. E così via.

Qualcosa di simile si potrebbe realizzare anche per le chiamate asincrone, dove il processo le deposita in un buffer, e quando questo si riempie invoca una syscall per dare in pasto al kernel l'intero bundle da processare. La syscall provvede, al solito, a scambiare i due buffer e restituire al processo il puntatore al nuovo buffer da riempire.

Si può pensare a diversi buffer allocati in base alla diverse priorità / esigenze. E magari ad API che devono essere processate con urgenza, e non passano da nessun meccanismo di buffering.
Titolo: Re:SuperKernel !
Inserito da: Z80Fan - 07 ſettembre 2013, 15:33:24
Per quelli asincroni ho pensato a una soluzione a doppio buffer per i risultati. Il processo / thread utente si smazza un buffer (coda) tutto in user-space, senza interferenze da parte del kernel; si tratta di una lista di messaggi che ha già caricato il kernel tempo fa, e che il processo può smazzarsi direttamente senza usare lock, semafori, ecc. Nel frattempo il kernel riempie il secondo buffer con le nuove richieste, in maniera del tutto indipendente. Quando il processo finisce tutti i messaggi del primo buffer, invoca una syscall; a questo punto il kernel scambia i due buffer (scambia i puntatori alle due aree) e restituisce al processo il puntatore al nuovo buffer che deve processare. E così via.

È senz'altro un'ottima soluzione, semplice ed elegante; la mia iniziale idea era un semplice buffer circolare pre-allocato (o almeno, pre-riservato anche senza memoria fisica assegnata) e di dimensione fissa; una tale struttura dati è molto semplice da fare e molto veloce in applicazioni di tipo produttore/consumatore, dove un thread (il kernel) inserisce e basta e un thread (il processo) legge e basta.
Tipicamente, se il produttore non può inserire il messaggio si blocca, ma il kernel potrebbe scartare il messaggio e inviare al mittente un messaggio di "casella piena". Anche gli aggiornamenti dei puntatori sono molto veloci, sfruttando le operazioni atomiche.

Il buffer multiplo però sembra molto più simpatico, dato che non si è limitati a una dimensione fissa (anche se una dimensione massima permessa c'è sempre), e c'è una più flessibilità nell'allocazione, quindi probabilmente andrò con questa strada.

Citazione
Qualcosa di simile si potrebbe realizzare anche per le chiamate asincrone, dove il processo le deposita in un buffer, e quando questo si riempie invoca una syscall per dare in pasto al kernel l'intero bundle da processare. La syscall provvede, al solito, a scambiare i due buffer e restituire al processo il puntatore al nuovo buffer da riempire.

Si, quello che avevo pensato io era di svuotare la coda messaggi ogni volta che tocca fare un context-switch, già che ci siamo, ma si può tranquillamente fare lo svuotamento a richiesta (anzi, qualcosa di simile era necessario anche nell'altro caso, per quando un processo si mette in attesa di un messaggio sincrono).

Citazione
Si può pensare a diversi buffer allocati in base alla diverse priorità / esigenze. E magari ad API che devono essere processate con urgenza, e non passano da nessun meccanismo di buffering.

E questo favorisce sicuramente l'approccio multi-buffer. Io organizzerei la priorità mettendo per prime le chiamate sincrone, visto che sono urgenti per il proseguimento del processo (e vengono anche favorite dallo scheduler, come fa QNX, avviando immediatamente il processo destinatario), e successivamente vari buffer di priorità decrescente.

Bisogna però decidere a chi si riferiscono le priorità: se le priorità sono solo del processo mittente, allora lui può mettere un messaggio di priorità alta in un buffer e uno di priorità bassa in un altro, ma non è detto che la priorità maggiore venga soddisfatta prima, perchè il server del messaggio prioritario potrebbe essere occupato mentre il server del messaggio di priorità inferiore è libero.
Far decidere al mittente la priorità del messaggio al lato destinatario non è una soluzione facile, perchè se fatta semplicemente incorre in una serie di possibili attacchi DoS da processi malevoli e starvation dei messaggi "genuini", se fatta bene diventa complessa quanto la gestione dello scheduling (si possono trovare paragoni tra i due sistemi).

La miglior soluzione a cui ho pensato è: diciamo che si hanno 5 livelli di priorità di messaggi; un server può scegliere di avere da 1 a 5 buffer diversi, ognuno che contiene messaggi di uno o più livelli di priorità. Il mittente può scegliere la priorità del messaggio, ma sarà il server poi che attua una sua politica di gestione; un esempio potrebbe essere un server che usa 5 thread diversi (magari a priorità di CPU diverse), ognuno che legge messaggi da una coda diversa, oppure un singolo thread che prende in sequenza da ciascuna coda, in modo da ridurre il tempo medio di attesa di ciscun messaggio nella coda.

In più, si possono implementare policy a livello di OS, dove certi livelli di priorità (magari quella massima) possono essere scelti solo da determinati processi scelti e fidati (come processi di sistema, driver etc), in modo da rispondere con la massima tempestività alle richieste genuinamente importanti (perchè non possiamo fidarci che qualsiasi programma sceglierà sempre la priorità corretta, per il discorso malware). Il kernel, quando smista i messaggi, si preoccuperà di abbassare il livello di un messaggio se questo viene da un processo non privilegiato, anche senza dare errore, in modo che un processo possa comunque usare le priorità per gestire il suo invio di messaggi (il kernel garantisce di gestire/recapitare i messaggi che il mittente ritiene più prioritari), anche se poi vengono abbassati al livello massimo consentito (es. si potrebbe voler far girare un processo al minimo di priorità, quindi impostando opportunamente una policy, i suoi messaggi a livello "normale" vengono automaticamente e trasparentemente convertiti a "basso").
Titolo: Re:SuperKernel !
Inserito da: cdimauro - 07 ſettembre 2013, 16:12:07
@Z80Fan: va bene.

Ricorda, comunque, che col doppio buffer non devi azzerare alcunché: basta spostare il puntatore indietro, all'inizio del buffer, per ricominciare a scrivere messaggi man mano.

L'unico dubbio è su ciò che bisogna fare se un buffer si riempie. Ma c'è tempo per pensarci.
Titolo: Re:SuperKernel !
Inserito da: cdimauro - 07 ſettembre 2013, 16:37:20
Lasciamoglielo scrivere prima. :P
Titolo: Re:SuperKernel !
Inserito da: Z80Fan - 07 ſettembre 2013, 16:55:44
Ricorda, comunque, che col doppio buffer non devi azzerare alcunché: basta spostare il puntatore indietro, all'inizio del buffer, per ricominciare a scrivere messaggi man mano.
Uhm, non mi pare di averlo pensato a azzeramenti, ma ho scritto quello che mi veniva in mente quindi è possibile che ci siano delle incongruenze. :D

Questo kernel possiamo anche anche provarlo sulla 68060 board con 8Mbyte di ram, o sulla Atlas MIPSR32-r2 con 128Mbyte di ram,
Non mi ricordo, alla fine avevi trovato il compilatore C++ per 68k? Quella patch di LLVM non son riuscito per niente e inserirla, neanche sulla versione 2.9 che dovrebbe essere quella su cui era basata; mi fa credere che quella patch non abbia mai funzionato...  :-\

Citazione
oppure con a una versione mignon (in puro C) sulla 68HC11, uno schedino che volendo potrei produrre con almeno 40Kbyte di ram.
Niente C, come avevo già detto punto tutto su C++, e probabilmente in generale avrebbe prestazioni scadenti su piccoli micro a 8 bit.
Non dico che in C++ non si può fare un sistema prestante per sistemi embedded molto piccoli, è solo che o non si hanno i compilatori o sono veramente poco ottimizzati.

Per quel tipo di applicazioni c'è già Contiki (http://www.contiki-os.org/), che è progettato appositamente per quello scopo.
Titolo: Re:SuperKernel !
Inserito da: Z80Fan - 07 ſettembre 2013, 18:46:15
Ci dovremmo accontantare di x86, ARM, PPC e MIPS allora, che comunque non sono poche per dimostrare la validità del concetto! :D
Sopratutto sono abbastanza diverse tra di loro (anche un po' più di abbastanza), quindi aiuta a testare la portabilità del kernel.
Titolo: Re:SuperKernel !
Inserito da: Z80Fan - 07 ſettembre 2013, 22:07:24
non ti preoccupare del C
al porting in C per il bsp della 68060 ci penso io

Ma come fai? "Portarlo" vorrebbe dire riscriverlo praticamente da capo... (a meno che non hai un qualche convertitore C++ -> C intermedio...)
Titolo: Re:SuperKernel !
Inserito da: Z80Fan - 07 ſettembre 2013, 22:47:30
questa constraint rilassala o annichiliscila del tutto: non e' un Kernel general purpose tipo linux, e' una roba molto + mirata, no ?

Eh no, l'idea del progetto è proprio quella di fare un kernel general purpose, ma che sia molto flessibile nella sua struttura, potendo scegliere quali moduli includere e ottenere alla fine o un sistema operativo desktop, o qualcosa di anche molto compatto.

Quindi nello sviluppo cerco di prendere in considerazione tutti i lati, sia il desktop che l'embedded, però alle volte bisogna decidere tra due scelte mutualmente esclusive; una di queste è proprio stata la scelta di svilupparlo in C++, sacrificando la portabilità su sistemi più piccoli ma guadagnandoci in sanità mentale io e maggior ristrettezza nella sintassi e tipizzazione.

Intanto mi ha incuriosito la conversione C++ -> C: so che LLVM ha un backend per generare codice C (poco leggibile, ma ci interessa solo come linguaggio intermedio), quindi lo devo provare.
L'unico compilatore commerciale che può generare codice C è Comeau C++, che addirittura fa solo quello perchè si appoggia a un altro compilatore per completare il binario.

In più ho trovato i sorgenti di cfront 3.0.3; come interessante esperimento, sto cercando di compilarlo su un moderno Linux e vedere quanto arriva a fare sui sorgenti del kernel. Certamente non sarà proprio una soluzione ottimale (non voglio mica dovermi limitare a C++ '89!), ma lo faccio giusto per sport. :D
Titolo: Re:SuperKernel !
Inserito da: Z80Fan - 07 ſettembre 2013, 23:03:30
Beh, l'esperimento è stato veloce... cfront è troppo strambo per essere compilato in un moderno sistema, e hanno rimosso il backend C da LLVM nella versione 3.1 perchè non funzionava bene.
Titolo: Re:SuperKernel !
Inserito da: Z80Fan - 08 ſettembre 2013, 18:47:47
MMU o TLB le consideri o si puo' forzare compilazione a salti relativi ?

Ci stavo pensando; praticamente dipende dal riuscire a fare funzionare il kernel sia con memoria virtuale, sia senza.
La *protezione* della memoria non è un gran problema (praticamente quando il kernel dice "imposta a sola lettura per il livello 3 questa area", il driver della cpu ignora la richiesta o riporta errore), mentre per la paginazione è un po' più complesso.

Cioè, non è che sia un gran problema fare un kernel che lavora in un singolo grande spazio di indirizzamento, ma è farlo in modo che sia più trasparente possibile.
Il kernel è diviso in 3 categorie: arch/platform/kernel. In kernel ci va tutto quello che è indipendente dalla piattaforma (scheduler, allocazione memoria, filesystem, ipc, network etc), in arch ci va tutto quello che è dipendente dall'architettura (i386, x86_64, ARM, PPC etc), e in platform ci va tutto quello che è dipendente dalla piattaforma (IBM PC, la tua board di fiducia... praticamente dove puoi fare più assunzioni possibili sull'hardware).

Il trick qua è poter spostare il più possibile della gestione dell'MMU nel driver CPU in arch/ in modo che il codice in kernel/ possa essere più generico possibile. Forse possiamo aggirare il problema fornendo in kernel/ 2 tipi di gestore di memoria (quello da usare può essere scelto durante la compilazione): uno che lavora con MMU, e l'altro che lavora senza; dato che tutti i bisogni di allocazione e gestione passano per questo modulo, la restante parte di kernel/ non deve pensarci su.

La famosa scheda UDOO che ho ordinato (che dovrebbe arrivare a settembre, ma vista la quantità di ordini arriverà probabilmente a inizio ottobre) monta sia un iMX.6 di architettura Cortex-A9, sia un SAM3X di architettura embedded Cortex-M3, perciò avrò l'hardware per testare anche contemporaneamente le due strategie.


Giusto per avere un'idea, che caratteristiche di memoria hanno le schede su cui vorresti eseguirlo?
Titolo: Re:SuperKernel !
Inserito da: Z80Fan - 09 ſettembre 2013, 00:09:00
Credo si riesca a fare. :D