È vero che chiamare _exit () anziché exit () non impedirà di chiamare i distruttori statici?

5

Su [email protected], Greg Herlihy ha pubblicato la seguente funzione "C" extern:

extern "C" 
{
    int func()
    {
        wchar_t memoryName[256];
        wchar_t mutexName[256];
        wchar_t eventName[256];
        mbstowcs(memoryName, "MemoryName", 256);
        mbstowcs(mutexName, "MutexName", 256);
        mbstowcs(eventName, "EventName", 256);
        std::wstring memoryString(memoryName);
        std::wstring mutexString(mutexName);
        std::wstring eventString(eventName);
        CDataTransferServer *srv = new CDataTransferServer();
        srv->Initialize(1, CC_SAMPLETYPE_MPEG4,128,256,64);
        printf("Inside entry point tester 1\n");
        srv->AddUser(5, memoryString, mutexString, eventString);
        printf("Inside entry point tester 2\n");
        delete srv;
        printf("Exiting entry point tester 3\n");                                                      
    }
} 

che è un punto di ingresso g ++ diverso da main(int argc, char*argv[]) .

Greg Herlihy quindi ha scritto:

Calling _exit() (which presumably should be "_Exit()") - does not "prevent" C++ global objects from being destroyed. The destruction of a C++ program's global objects is not inevitable. Instead a C++ program is responsible for destroying its own global objects - and can do so in one of two ways: the program returns from main() or it calls exit(). So a C++ program that fails to exit main() and neglects to call exit() before it terminates - will not have destroyed its global objects by the time it ended.

Non credo che ciò derivi da ciò che è scritto nello standard.

Per quanto posso vedere, quando chiamo exit() , la specifica garantisce che:

  • Distruttori per oggetti con durata di archiviazione automatica non sarà chiamato.

  • Distruttori per oggetti con durata di archiviazione statica sarà chiamato, in ordine inverso di costruzione.

The worrying thing is that the standard doesn't say anything about exit() or _exit(), so I'm relying on implementation-dependent behavior.

     

Non così. Lo standard C ++ specifica che chiamare exit () distrugge globale   oggetti [3.6.3 / 1] E _Exit () fa parte dello standard C99 (e lo farà   presumibilmente essere incorporato nel prossimo standard C ++ per riferimento).

A destra, lo standard C ++ dice cosa exit() fa, ma non dice cosa _exit() o _Exit() do. E lo standard C certamente non dice qualsiasi cosa sui distruttori del C ++.

I don't see any implementation-defined behavior here. Calling _Exit() is no more likely to destroy a C++ program's global objects than calling printf() - or calling any other function that is not exit().

_exit() e _Exit() sono specificati per non chiamare alcuna funzione registrata atexit() o on_exit() . Tuttavia, non è utile per C ++ perché Le specifiche di C ++ non dicono in che modo il runtime si prende cura della chiamata distruttori di oggetti con durata di archiviazione statica. Un compliant l'implementazione potrebbe utilizzare un meccanismo diverso da atexit() o on_exit() per richiamare i distruttori statici.

In altre parole, le specifiche C ++ non dicono nulla di tutto ciò comportamento di _exit() o _Exit() . Pertanto, non posso formulare ipotesi sull'opportunità o meno di chiamare una funzione causerà distruttori di oggetti statici da eseguire o meno.

Qualsiasi commento è benvenuto.

    
posta Frank 04.01.2016 - 07:11
fonte

4 risposte

10

Right, the C++ standard says what exit() does, but it doesn't say what _exit() or _Exit() do.

Da n3290 che, se non sbaglio, è lo standard C ++ 11 tranne le piccole modifiche editoriali dell'ultimo minuto:

The function _Exit(int status) has additional behavior in this International Standard:

  • The program is terminated without executing destructors for objects of automatic, thread, or static storage duration and without calling functions passed to atexit()

Non esiste una descrizione di _exit (ma lo standard C non descrive nemmeno _exit).

    
risposta data 04.01.2016 - 11:33
fonte
5

In other words, the C++ spec does not say anything all about the behavior of _exit() or _Exit(). Therefore, I cannot make any assumptions about whether or not calling either function will cause destructors of static objects to run or not.

Se stiamo parlando di comportamenti standard, temo di non saperlo. Potrebbe benissimo essere semplicemente non specificato.

Tuttavia, ogni compilatore / piattaforma che ho incontrato si allinea con quello che Greg ha scritto, ed è un po 'un punto di inciampo se si inserisce la propria libreria di runtime C standard (CRT) per scoprire che gli oggetti statici non sono inizializzati all'avvio né distrutto all'uscita senza fare tutto manualmente. I distruttori per globali / statici tendono ad essere invocati nello stage hook atexit e il punto principale di _exit è di ignorare l'intero processo (e quindi di solito ignorare la distruzione di tali oggetti).

Ha un senso pratico se non è specificato per evitare di invocare i distruttori in questo contesto, dal momento che fare ciò contraddirebbe il punto intero di _exit (che è quello di evitare quel tipo di fase di pulizia atexit ). Anche se fossero disponibili meccanismi alternativi all'interno del linguaggio per richiamare questi distruttori, probabilmente tali meccanismi di spegnimento verrebbero parimenti bypassati da _exit .

Quindi è un po 'come le assunzioni secondo cui std::vector è contigua prima che lo standard la specificasse. Finì per essere un presupposto abbastanza ragionevole nella pratica, poiché si trattava dell'unico modo pratico per concludere un'implementazione conforme ai suoi requisiti.

Forse un avvocato linguistico potrebbe entrare qui e chiarire le cose, ma sospetto che sia un'ipotesi molto pratica che _exit eviti di invocare distruttori per globali / statici.

    
risposta data 04.01.2016 - 08:24
fonte
3

Su Linux, _exit (2) è un chiamata di sistema (spesso exit_group (2) è in realtà chiamato). Quindi se chiami quel raw syscall non succede nient'altro, tranne che il processo attuale sta finendo e _exit

terminates the calling process "immediately".

Una chiamata di sistema è, dal punto di vista della modalità utente, un'istruzione elementare della macchina ( SYSENTER ) che passa alla modalità kernel. E il kernel non ritornerà nel tuo programma (il cui processo cessa di esistere) dopo un _exit o exit_group syscall. E questo è indipendente dal linguaggio di programmazione e dal runtime. Quindi non succede nient'altro, non viene eseguito codice (o prima) _exit , quindi non viene eseguito alcun distruttore (o qualsiasi altra cosa).

Ovviamente exit (3) è una funzione di libreria C standard:

All functions registered with atexit(3) and on_exit(3) are called, in the reverse order of their registration.

All open stdio(3) streams are flushed and closed. Files created by tmpfile(3) are removed.

E vengono anche chiamati distruttori globali. In realtà, suppongo che qualcosa chiamandoli sia registrato da un equivalente di at_exit nella routine _start di crt0 .

Poiché la libreria standard C è generalmente software gratuito su Linux, potresti studiare il codice sorgente di exit ; per musl-libc suo exit/exit.c Il file sorgente è abbastanza leggibile.

Leggi anche la POSIX specifica dell'uscita .

    
risposta data 05.01.2016 - 07:05
fonte
0

Ho scoperto oggi facendo ricerche e testando che i distruttori di oggetti statici non sono il problema qui.   Ho avuto il seguente programma C ++ per lavorare.

#include  <stdio.h>
#include  <iostream>
#include  <dlfcn.h>
#include "CameraControlDefs.h"
#include "DataServer.h"

using namespace std;


int main(int argc,char** argv)
{

   bool (*sayHello)(CDataTransferServer* _this, int nCameraID, CC_SAMPLETYPE nDataType,
                                unsigned int nImageWidth, unsigned int nImageHeight,
                                unsigned int nMaxFrames);


 // API api;
  unsigned int tmp;

  //...

  void* handle = dlopen("libDataServer.so", RTLD_LAZY);
  if (!handle)
  {
    std::cerr << dlerror() << std::endl;
    return 1;
  }

  // load the symbols
    create_t* create_triangle = (create_t*) dlsym(handle, "create");
    const char* dlsym_error = dlerror();
    if (dlsym_error) {
        cerr << "Cannot load symbol create: " << dlsym_error << '\n';
        return 1;
    }

    destroy_t* destroy_triangle = (destroy_t*) dlsym(handle, "destroy");
    dlsym_error = dlerror();
    if (dlsym_error) {
        cerr << "Cannot load symbol destroy: " << dlsym_error << '\n';
        return 1;
    }

    // create an instance of the class
    CDataTransferServer* poly = create_triangle();


  *(bool **)(&sayHello) = (bool *)dlsym(handle,"_ZN19CDataTransferServer10InitializeEi13CC_SAMPLETYPEjjj");
  if (dlerror())
  {
    std::cerr << dlerror() << std::endl;
    return 2;
  }


  printf("ywc = %x\n",poly);


// destroy the class

   (*sayHello)(poly, 5, CC_SAMPLETYPE_MPEG4,128,256,64);
    destroy_triangle(poly);

    // unload the triangle library
  dlclose(handle);

    //...

  return 0;

}
    
risposta data 06.01.2016 - 02:26
fonte

Leggi altre domande sui tag