2012-05-14 7 views
6

Ich habe ein C-Programm (Linux) entwickelt, dieses Programm eine neue Datei erstellen und schreiben, danach startet den PC neu.Linux Reboot-Funktion in C-Programm aufgerufen verursacht Dateiverlust durch das Programm auf der Festplatte erstellt

Nach dem Neustart habe ich die Datei von meinem Programm erstellt verloren. Wenn ich die Neustartfunktion deaktiviere, ist die von meinem Programm erstellte Datei immer noch vorhanden.

Dieses Verhalten wird mit Linux gesehen: - OpenWrt (10.03 Backfire) auf VirtualBox (Dateisystem ext2) - Linux (Ubuntu) (Dateisystem ext4)

Haben Sie eine Erklärung für dieses Verhalten und wie kann ich das beheben es?

#include <stdio.h> 
#include <sys/reboot.h> 

int main() 
{ 
    FILE *pFile; 
    char mybuffer[80]; 

    pFile = fopen ("/home/user/Desktop/example.txt","w"); 
    if (pFile == NULL) perror ("Error opening file"); 
    else 
    { 
     fputs ("test",pFile); 
     fclose (pFile); 
    } 
    rename("/home/user/Desktop/example.txt","/home/user/Desktop/example123.txt"); 
    reboot(RB_AUTOBOOT); 
    return 0; 
} 
+4

Flush (sync) die Datei selbst: http: //linux.die.net/man/2/fsync ... – ChristopheD

+0

Ich stimme nicht zu, aber ich bin überrascht, dass Aufruf von fclose() ist nicht ausreichend . – larsks

+0

@larks Siehe meine Antworten. Die man-Seite gibt dies explizit an. Es ist allerdings ein bisschen komisch. – pmr

Antwort

7

Die Manpage fclose sagt:

Beachten Sie, dass nur fclose(), um die User-Space-Puffer spült durch die C-Bibliothek zur Verfügung gestellt. Um sicherzustellen, dass die Daten physisch auf der Festplatte gespeichert werden, müssen die Kernel-Puffer ebenfalls geleert werden, zum Beispiel mit sync (2) oder fsync (2).

Das bedeutet, dass Sie vor dem Schließen des Dateideskriptors fsync aufrufen müssen.

+0

Vielen Dank für die Erklärung, jetzt verstehe ich, wo der Verlust von Daten aus meinem Programm ist. – developer

1

Ich denke, die wichtige Sache hier ist, dass der Neustart nie zurückkehrt, so dass Ihr Programm nie wirklich normal beendet.

Unter normalen Bedingungen (d. H. Ein Programm, das nach dem Aufruf von fclose beendet wird oder sogar abstürzt), werden die Dateideskriptoren, die Ihrer FILE * zugrunde liegen, geschlossen und ihre Kernel-Puffer geleert.

In diesem Fall jedoch, da Neustart nie zurückkehrt, vermute ich, dass die Kernel-Puffer nicht auf die übliche Weise bereinigt werden, und daher wird nicht auf die Festplatte geschrieben werden, weil es.

Ein fsync-Aufruf wird sich wahrscheinlich darum kümmern. Wenn Sie paranoid sein wollen, tun Sie fsync, dann verwenden Sie fileno(), um einen Dateideskriptor zu erhalten und sync() zu verwenden, um sicherzustellen, dass die Puffer geleert werden. Zu diesem Zeitpunkt sollte nichts von der Datei im Prozessadressraum übrig sein und Ihr Aufruf zum Neustart sollte keine weiteren Probleme verursachen.

+0

Dies ist der richtige Weg, es gab keine Synchronisation. – developer

6

Das unmittelbare Problem ist, dass Sie die Datei vor dem Neustart nicht synchronisieren. Das aktuelle Problem ist, dass Sie den reboot Syscall direkt aufrufen, ohne Rücksicht darauf, was sonst noch auf dem System passiert. Was Sie tun, ist dem Drücken der HW-Reset-Taste sehr ähnlich. Sie geben dem Kernel nur die Chance, ein wenig aufzuräumen, aber dann wird alles auf die harte Tour abgetötet. Dies ist eine todsichere Möglichkeit, Dateisysteme und Dateistrukturen zu beschädigen. Tun Sie das nicht!.

Stattdessen sollten Sie das Init-System bitten, einen ordnungsgemäßen Neustart durchzuführen. Der Aufruf des Systemaufrufs reboot erfordert einen privilegierten Zugriff. Sie können also das init-System einfach neu starten. Auf den meisten Systemen gibt es einen Symlink /sbin/reboot, der auf das Programm verweist, das einen vernünftigen Neustart initiiert, wenn es über diesen Symlink aufgerufen wird. Daher empfehle ich Ihnen, Ihre schmutzige reboot(RB_AUTOBOOT) durch zu ersetzen (beachten Sie die doppelte Spezifikation von "/sbin/reboot" in execlp - das ist wichtig).

pid_t reboot_pid; 
if(0 == (reboot_pid = fork())) { 
    execlp("/sbin/reboot", "/sbin/reboot", NULL); 
    exit(1); /* never reached if execlp succeeds. */ 
} 
if(-1 == reboot_pid) { 
    /* fork error... deal with it somehow */ 
} 
int reboot_status; 
waitpid(reboot_pid, &reboot_status, 0); 
if(!WIFEXITED(reboot_status)) { 
    /* reboot process did not exit sanely... deal with it somehow */ 
} 
if(0 != WIFEXITSTATUS(reboot_status)) { 
    /* reboot process exited with error; 
    * most likely the user lacks the required privileges */ 
} 
else { 
    fputs("reboot call sucessfull -- system is about to shutdown."); 
    /* The init system is now shutting down the system. It will signals all 
    * programs to terminate by sending SIGTERM, followed by SIGKILL to 
    * programs that didn't terminate gracefully. */ 
} 

es auf diese Weise das System tun kann ordnungsgemäß heruntergefahren, beenden Sie alle Programme in einem sauberen Art und Weise ausgeführt wird und alle Dateisysteme aushängen, bevor der Neustart tun, damit keeing Dateisystem und Datenintegrität.

Beachten Sie, dass wenn Sie erwarten, dass Ihr Programm keinen Root-Zugriff hat, dann müssen Sie einige Reifen springen; Auf Systemen mit Systemd können Sie eine Neustartanforderung per D-Bus senden. Aber außer dass es fehlschlägt, wenn der Benutzer, der den Befehl ausführt, keine Neustart-Rechte hat.

0

Eine alternative Lösung ist, synchron gemäß dem reboot Manpage

LINUX_REBOOT_CMD_POWER_OFF zu nennen (RB_POWER_OFF, 0x4321fedc, da Linux 2.1.30). Die Nachricht "Herunterfahren." wird gedruckt, das System wird gestoppt und die gesamte Stromversorgung wird nach Möglichkeit aus dem System entfernt. Wenn keine Synchronisierung (2) vorangestellt ist, gehen Daten verloren.