2010-08-20 17 views
8

Attualmente sto lavorando su un sistema di segnalazione degli errori basato su eccezioni per le app MSVC++ (9.0) di Windows (ad esempio strutture & di tipo ereditarietà, stack di chiamate, segnalazione errori & registrazione e così via).

La mia domanda ora è: come segnalare correttamente & registrare un errore di memoria insufficiente?

Quando si verifica questo errore, ad es. come bad_alloc lanciato dall'opzione new, potrebbero non esserci molte "funzioni" non disponibili, principalmente per quanto riguarda l'ulteriore allocazione di memoria. Normalmente, passerei l'eccezione all'applicazione se è stata lanciata in una lib, quindi utilizzando le finestre di messaggio e i file di log degli errori per segnalare e loggare. Un altro modo (principalmente per i servizi) è utilizzare il registro eventi di Windows.
Il problema principale che ho è quello di assemblare un messaggio di errore. Per fornire alcune informazioni di errore, vorrei definire un messaggio di errore statico (potrebbe essere una stringa letterale, una voce migliore in un file di messaggio, quindi utilizzare FormatMessage) e includere alcune informazioni di runtime come uno stack di chiamate.
Le funzioni/metodi necessari per tale uso siaC++/Windows: come segnalare un'eccezione di memoria esaurita (bad_alloc)?

  • STL (std::string, std::stringstream, std::ofstream)
  • CRT (swprintf_s, fwrite)
  • o Win32 API (StackWalk64, MessageBox, FormatMessage, ReportEvent, WriteFile)

Oltre ad essere documentata sul MSDN, tutti hanno più (Win32) o meno (STL) closed source in Windows, quindi non so come si comportano in caso di problemi di memoria insufficiente.

solo per dimostrare ci potrebbero essere dei problemi, ho scritto una piccola applicazione banale provocando una bad_alloc:

int main() 
{ 
    InitErrorReporter(); 

    try 
    { 
     for(int i = 0; i < 0xFFFFFFFF; i++) 
     { 
      for(int j = 0; j < 0xFFFFFFFF; j++) 
      { 
       char* p = new char; 
      } 
     } 
    }catch(bad_alloc& e_b) 
    { 
     ReportError(e_b); 
    } 

    DeinitErrorReporter(); 

    return 0; 
} 

Ran due istanze w/o debugger (in config di uscita, VS 2008), ma "non è successo niente ", ovvero nessun codice di errore da ReportEvent o WriteFile utilizzato internamente nella segnalazione degli errori. Quindi, ha lanciato un'istanza con e un debugger e gli ha consentito di provare a segnalare gli errori uno dopo l'altro utilizzando un punto di interruzione nella riga ReportError. Questo ha funzionato bene per l'istanza con il debugger allegato (riportato correttamente & ha registrato l'errore, anche utilizzando LocalAlloc senza problemi)! Ma il taskman ha mostrato uno strano comportamento, in cui c'è molta memoria liberata prima che l'app esca, suppongo quando viene lanciata l'eccezione.


Si prega di prendere in considerazione ci possono essere più di un processo [modifica] e più di un thread [/ modifica] consumano molta memoria, così liberando spazio pre-assegnati heap non è una soluzione sicura per evitare un ambiente di memoria insufficiente per il processo che vuole segnalare l'errore.

Grazie in anticipo!

+2

Hai mai pensato di avere un blocco di memoria prenotato in anticipo che diventerà la fonte di posizionamento allocazioni ogni volta che il sistema entra in una situazione di esaurimento della memoria? Ho sempre usato questo metodo solo per uscire con grazia dall'applicazione, ma i sistemi operativi (OpenSolaris, Linux) fanno qualcosa di simile a dare all'applicazione il tempo necessario per liberare o scambiare allocazioni a bassa priorità e recuperare con garbo – Sniggerfardimungus

+0

Attualmente, Utilizzo dello spazio di stack (le variabili membro dichiarate quando si chiama InitErrorReporter) per fornire i buffer alle funzioni CRT/WinSDK. Ma non so cosa facciano internamente - vedi l'anser di Alex Farber. – dyp

+0

http://stackoverflow.com/questions/1308052/policy-with-catching-stdbad-alloc parla di qualcosa di simile – Chubsdad

risposta

2
  • pre-allocare il buffer (s) è necessario
  • collegamento staticamente e utilizzare _beginthreadex invece di CreateThread (in caso contrario, funzioni CRT possono fallire) - OPPURE - attuare la stringa concat/i2a te
  • Usa MessageBox (MB_SYSTEMMODAL | MB_OK) MSDN menziona questo per la segnalazione di condizioni di OOM (e alcuni SM blogger ha descritto questo comportamento come previsto: la finestra di messaggio non allocare la memoria.)

registrazione è più difficile, per lo meno, il il file di registro deve essere già aperto.

Probabilmente il migliore con FILE_FLAG_NO_BUFFERING e FILE_FLAG_WRITE_THROUGH, per evitare qualsiasi tentativo di buffering. Il primo richiede che le scritture ei buffer di memoria siano allineati al settore (ovvero è necessario interrogare GetDiskFreeSpace, allineare il buffer con quello e scrivere solo su offset di file "multipli di dimensione settore" e in blocchi multipli di dimensioni di settore. Non sono sicuro se sia necessario o utile, ma una OOM a livello di sistema in cui l'allocazione non riesce a effettuare l'allocazione.

+1

grazie mille. Ho fatto alcuni test (ma non so quanto siano significativi) e il MessageBox | MB_SYSTEMMODAL appare sempre, anche dopo aver catturato diversi bad_allocs. Per questo, ho usato del codice basato su quello nella mia risposta, ma ho deciso di costruirlo per x64/Release. I bad_allocs che ho ricevuto con x86 erano per lo più errori out-of-virtual-address-space poiché ho 4 GB di RAM più (ora) 2 GB di dimensione massima del file di pagina. Con la versione x64, il sistema si blocca in modo grave, anche a volte il cursore. Ma ancora, MB funziona e questo trucco con la pagina che ho menzionato, anche! – dyp

3

"Liberare spazio heap pre-allocato ...". Questo era esattamente quello che pensavo leggendo la tua domanda. Ma penso che tu possa provarlo. Ogni processo ha il proprio spazio di memoria virtuale. Con altri processi che consumano molta memoria, questo potrebbe funzionare anche se tutto il computer funziona.

+0

k, ma supponevo che ci fossero due thread in un'applicazione in cui entrambi eseguivano funzioni di librerie diverse. Un processo -> uno spazio di memoria virtuale. Quindi, quando uno lancia un bad_alloc libera lo spazio pre-allocato, l'altro potrebbe allocarlo? Il problema è quando utilizzo le funzioni OS/SDK, non so se facciano affidamento internamente sullo spazio principale, quindi utilizzare lo spazio preassegnato per funzionare non è stata una soluzione. – dyp

+0

devo sospendere tutti gli altri thread prima di poter usare questa tecnica? – dyp

+0

Un altro thread può utilizzare immediatamente la memoria pre-allocata appena rilasciata ... Ogni attività deve essere interrotta per assicurarsi che il report degli errori abbia esito positivo. –

1

Considerare che potrebbe esserci più di un processo che consuma molta memoria, quindi liberare lo spazio di heap preassegnato non è una soluzione sicura per evitare un ambiente di memoria insufficiente per il processo che desidera segnalare l'errore.

In Windows (e in altri sistemi operativi moderni), ogni processo ha il proprio spazio indirizzo (noto anche come memoria) separato da ogni altro processo in esecuzione. E tutto ciò è separato dalla RAM letterale nella macchina. Il sistema operativo ha virtualizzato lo spazio di indirizzamento del processo lontano dalla RAM fisica.

In questo modo Windows è in grado di trasferire la memoria utilizzata dai processi nel file di paging sul disco rigido senza che questi processi abbiano alcuna conoscenza di ciò che è accaduto.

Questo è anche il modo in cui un singolo processo può allocare più memoria rispetto alla RAM fisica della macchina e ancora funzionare. Ad esempio, un programma in esecuzione su una macchina con 512 MB di RAM potrebbe comunque allocare 1 GB di memoria. Windows non riusciva a mantenere tutto nella RAM contemporaneamente e parte del file di paging. Ma il programma non lo saprebbe.

Quindi, di conseguenza, se un processo alloca memoria, non causa un altro processo con meno memoria su cui lavorare. Ogni processo è separato.

Ogni processo deve solo preoccuparsi di se stesso. E quindi l'idea di liberare una porzione di memoria pre-allocata è in realtà molto valida.

+0

Sono d'accordo. Cosa si tratta di due thread? Guarda la risposta di Alex Farber. – dyp

+0

Eh? Questo non è corretto. Mentre ogni processo ha il proprio spazio VA, ci sono un numero limitato di frame fisici e spazio di scambio per soddisfare una richiesta interna. Se la spesa di commit totale supera questo numero, tutte le richieste di allocazione di un processo falliscono. Se ti trovi in ​​questa situazione, liberare una porzione di memoria significa che qualsiasi processo nel sistema è in grado di prenderlo (cioè qualsiasi processo sarà in grado di aumentare nuovamente la carica di commit). –

+0

@Paul: hai un punto. Indipendentemente dal sistema operativo, alla fine ci sarà sempre un limite. Tuttavia, al di fuori delle situazioni in cui i meccanismi di memoria del sistema operativo sono stati espulsi, mi chiedo se non sia ragionevole che i programmi agiscano come se avessero sempre a disposizione il loro spazio di indirizzi virtuali completo con cui lavorare. Ma immagino che possa essere specifico della situazione, a seconda di quanto è probabile che la macchina dell'utente finale aumenti al massimo le sue risorse e quanto utile sarebbe effettivamente l'accesso in risposta a tale situazione. – TheUndeadFish

0

Non è possibile utilizzare le funzioni CRT o MessageBox per gestire OOM poiché potrebbero richiedere memoria, come descritto. L'unica cosa veramente sicura che puoi fare è allocare un blocco di memoria all'avvio in cui puoi scrivere informazioni e aprire un handle in un file o in una pipe, quindi scrivere WriteFile quando esci da OOM.

+0

Quindi, piuttosto che pre-allocare e quindi liberare un blocco, si dovrebbe invece pre-allocare un'area da utilizzare specificamente per essere utilizzata per gestire i dati registrati nello scenario di OOM (una sorta di buffer di scratch per assemblare il stringhe o qualunque cosa si desideri). Supponendo che WriteFile non abbia mai un motivo per fallire in uno scenario di OOM, allora dovrei concordare che questo suona come il modo migliore garantito per registrare le informazioni. – TheUndeadFish

+0

Sì, questo è ciò che fa il sistema operativo se deve garantire che qualcosa funzioni in condizioni di memoria insufficiente (come essere in grado di sollevare un'eccezione SEH, ntdll ne mantiene sempre uno preallocato nella tasca posteriore) –

+0

Sai come la creazione dei diversi tipi di oggetti Win32 si comporta in condizioni OOM? Cioè Oggetti del kernel come file (-> handle di file) e oggetti GUI come MessageBox? Per quanto ne so, almeno gli oggetti del kernel non fanno parte dello spazio virtuale di memoria/indirizzo e quindi il sistema operativo in realtà assegna loro memoria. – dyp

Problemi correlati