Domanda

Sto sviluppando un sistema operativo e piuttosto che programmare il kernel, sto progettando il kernel. Questo sistema operativo è mirato all'architettura x86 e il mio obiettivo è per i computer moderni. Il numero stimato di RAM richiesta è pari o superiore a 256 Mb.

Qual è una buona dimensione per far funzionare lo stack per ogni thread sul sistema? Dovrei provare a progettare il sistema in modo tale che lo stack possa essere esteso automaticamente se viene raggiunta la lunghezza massima?

Penso che se ricordo correttamente che una pagina nella RAM è 4k o 4096 byte e che a me non sembra molto. Riesco sicuramente a vedere i tempi, soprattutto quando uso molta ricorsione, che vorrei avere più di 1000 integar in RAM contemporaneamente. Ora, la vera soluzione sarebbe quella di fare in modo che il programma lo facesse usando malloc e gestisse le proprie risorse di memoria, ma davvero vorrei sapere l'opinione dell'utente su questo.

4k è abbastanza grande per uno stack con i moderni programmi per computer? Lo stack dovrebbe essere più grande di quello? Lo stack dovrebbe espandersi automaticamente per adattarsi a qualsiasi tipo di dimensione? Sono interessato a questo sia dal punto di vista dello sviluppatore pratico che dal punto di vista della sicurezza.

Il 4K è troppo grande per uno stack? Considerando la normale esecuzione del programma, specialmente dal punto di vista delle classi in C ++, noto che un buon codice sorgente tende a malloc / new i dati di cui ha bisogno quando vengono create le classi, per minimizzare i dati che vengono lanciati in una chiamata di funzione.

Quello che non ho nemmeno capito è la dimensione della memoria cache del processore. Idealmente, penso che lo stack risiederebbe nella cache per velocizzare le cose e non sono sicuro se devo raggiungere questo obiettivo o se il processore può gestirlo per me. Stavo solo pianificando di utilizzare la normale vecchia noiosa RAM a scopo di test. Non riesco a decidere. Quali sono le opzioni?

È stato utile?

Soluzione

Le dimensioni dello stack dipendono da cosa stanno facendo i tuoi thread. Il mio consiglio:

  • rende la dimensione dello stack un parametro al momento della creazione del thread (thread diversi faranno cose diverse e quindi avranno bisogno di dimensioni dello stack diverse)
  • fornisce un ragionevole valore predefinito per coloro che non vogliono essere disturbati a specificare una dimensione dello stack (4K fa appello al maniaco del controllo in me, poiché causerà il debilitato dello stack, ehm, per ottenere il segnale abbastanza rapidamente)
  • considera come rileverai e gestirai lo overflow dello stack. Il rilevamento può essere complicato. Puoi mettere le pagine di guardia - vuote - alle estremità della pila, e generalmente funzionerà. Ma stai facendo affidamento sul comportamento del Bad Thread per non saltare quel fossato e iniziare a inquinare ciò che si trova oltre. Generalmente ciò non accadrà ... ma poi, questo è ciò che rende i bug davvero difficili. Un meccanismo ermetico comporta l'hacking del compilatore per generare il codice di controllo dello stack. Per quanto riguarda la gestione di un overflow dello stack, avrai bisogno di uno stack dedicato altrove su cui verrà eseguito il thread offensivo (o il suo angelo custode, chiunque tu decida che sia - sei il progettista del sistema operativo, dopo tutto).
  • Consiglio vivamente di contrassegnare le estremità del tuo stack con un motivo distintivo, in modo che quando i tuoi thread passano sopra le estremità (e lo fanno sempre), puoi almeno andare in post-mortem e vedere che qualcosa ha effettivamente fatto scappare dal suo stack. Una pagina di 0xDEADBEEF o qualcosa del genere è utile.

A proposito, le dimensioni delle pagine x86 sono generalmente 4k, ma non devono esserlo. Puoi andare con una dimensione di 64k o anche più grande. La solita ragione per pagine più grandi è di evitare mancati TLB. Ancora una volta, lo renderei una configurazione del kernel o un parametro di runtime.

Altri suggerimenti

Cerca KERNEL_STACK_SIZE nel codice sorgente del kernel linux e scoprirai che dipende molto dall'architettura - PAGE_SIZE, o 2 * PAGE_SIZE ecc. (sotto sono solo alcuni risultati - molti output intermedi vengono eliminati).

./arch/cris/include/asm/processor.h:
#define KERNEL_STACK_SIZE PAGE_SIZE

./arch/ia64/include/asm/ptrace.h:
# define KERNEL_STACK_SIZE_ORDER        3
# define KERNEL_STACK_SIZE_ORDER        2
# define KERNEL_STACK_SIZE_ORDER        1
# define KERNEL_STACK_SIZE_ORDER        0
#define IA64_STK_OFFSET         ((1 << KERNEL_STACK_SIZE_ORDER)*PAGE_SIZE)
#define KERNEL_STACK_SIZE       IA64_STK_OFFSET

./arch/ia64/include/asm/mca.h:
    u64 mca_stack[KERNEL_STACK_SIZE/8];
    u64 init_stack[KERNEL_STACK_SIZE/8];

./arch/ia64/include/asm/thread_info.h:
#define THREAD_SIZE         KERNEL_STACK_SIZE

./arch/ia64/include/asm/mca_asm.h:
#define MCA_PT_REGS_OFFSET      ALIGN16(KERNEL_STACK_SIZE-IA64_PT_REGS_SIZE)

./arch/parisc/include/asm/processor.h:
#define KERNEL_STACK_SIZE   (4*PAGE_SIZE)

./arch/xtensa/include/asm/ptrace.h:
#define KERNEL_STACK_SIZE (2 * PAGE_SIZE)

./arch/microblaze/include/asm/processor.h:
# define KERNEL_STACK_SIZE  0x2000

Lancio i miei due centesimi per far rotolare la palla:

  • Non sono sicuro di quale sia un "tipico" la dimensione dello stack sarebbe. Immagino che forse 8 KB per thread, e se un thread supera questo importo, genera un'eccezione. Tuttavia, secondo questo , Windows ha un dimensioni dello stack riservate predefinite di 1 MB per thread, ma non viene eseguito il commit tutto in una volta (le pagine vengono impegnate quando sono necessarie). Inoltre, è possibile richiedere una dimensione dello stack diversa per un determinato EXE in fase di compilazione con una direttiva del compilatore. Non sono sicuro di cosa faccia Linux, ma ho visto riferimenti a stack da 4 KB (anche se penso che questo possa essere cambiato quando compili il kernel e non sono sicuro di quale sia la dimensione dello stack predefinita ...)

  • Questo si collega al primo punto. Probabilmente vuoi un limite fisso su quanto stack può ottenere ogni thread. Pertanto, probabilmente non vorrai allocare automaticamente più spazio di stack ogni volta che un thread supera lo spazio di stack corrente, perché un programma difettoso che si blocca in una ricorsione infinita sta per consumare tutta la memoria disponibile.

Se si utilizza la memoria virtuale, si desidera rendere lo stack espandibile. Forzare l'allocazione statica di dimensioni dello stack, come è comune nel threading a livello di utente come Qthreads e Windows Fibres, è un casino. Difficile da usare, facile da arrestare. Tutti i moderni sistemi operativi fanno crescere lo stack in modo dinamico, penso di solito avendo una pagina di guardia protetta da scrittura o due sotto il puntatore dello stack corrente. Scrive lì quindi dice al sistema operativo che lo stack si è spostato al di sotto del suo spazio allocato, e alloca una nuova pagina di guardia sotto quella e rendi scrivibile la pagina che è stata colpita. Finché nessuna singola funzione alloca più di una pagina di dati, questo funziona bene. Oppure puoi usare due o quattro pagine di guardia per consentire cornici più grandi.

Se vuoi un modo per controllare le dimensioni dello stack e il tuo obiettivo è un ambiente veramente controllato ed efficiente, ma non preoccuparti di programmare nello stesso stile di Linux ecc., scegli un modello di esecuzione a colpo singolo in cui un'attività è avviato ogni volta che viene rilevato un evento rilevante, viene eseguito fino al completamento e quindi memorizza tutti i dati persistenti nella struttura dei dati dell'attività. In questo modo, tutti i thread possono condividere un singolo stack. Utilizzato in molti sistemi operativi slim in tempo reale per il controllo automobilistico e simili.

Perché non rendere le dimensioni dello stack un elemento configurabile, memorizzato con il programma o specificato quando un processo crea un altro processo?

Esistono diversi modi per renderlo configurabile.

C'è una linea guida che afferma "0, 1 o n", il che significa che dovresti consentire zero, uno o qualsiasi numero (limitato da altri vincoli come la memoria) di un oggetto - questo vale anche per le dimensioni degli oggetti.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top