2012-05-14 5 views
6

He desarrollado un programa C (Linux), este programa crea un nuevo archivo y escribe, luego reinicia la PC.La función de reinicio de Linux llamada en el programa C causa la pérdida de archivos creada por el programa en el disco

Después de reiniciar, he perdido el archivo creado por mi programa. Cuando desactivo la función de reinicio, el archivo creado por mi programa aún está presente.

Este comportamiento se ve con Linux: - OpenWrt (Detonación 10.03) en VirtualBox (ext2) - Linux (Ubuntu) (sistema de archivos ext4)

usted tiene una explicación para este comportamiento y cómo se puede solucionar I ¿eso?

#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

Enjuague (sincronice) el archivo usted mismo: http: //linux.die.net/man/2/fsync ... – ChristopheD

+0

No estoy en desacuerdo, pero me sorprende que llamar a fclose() no sea suficiente . – larsks

+0

@larsks Vea mis respuestas. La página man explícitamente esto. Aunque es un poco extraño – pmr

Respuesta

7

La página del manual para fclose dice:

Tenga en cuenta que fclose() sólo vacía los buffers del espacio de usuario proporcionados por la biblioteca C. Para garantizar que los datos se almacenen físicamente en el disco, los almacenamientos intermedios del kernel también se deben purgar, por ejemplo, con sync (2) o fsync (2).

Lo que significa que debe llamar al fsync antes de cerrar el descriptor de archivo.

+0

Gracias por la explicación, ahora entiendo de dónde proviene la pérdida de datos de mi programa. – developer

1

Creo que lo importante aquí es que el reinicio nunca retorna, por lo que su programa nunca sale realmente normalmente.

En condiciones normales (es decir, un programa que se cierra o incluso se cuelga después de llamar a fclose), los descriptores de archivos que subyacen a su ARCHIVO * se cerrarán y sus búferes de núcleo se vacían.

En este caso, sin embargo, dado que el reinicio nunca vuelve, sospecho que los almacenamientos intermedios del kernel no se están limpiando de la manera habitual, y por lo tanto no se escriben cosas en el disco debido a ello.

Una llamada fsync probablemente se encargue de ello. Si quieres ser paranoico, haz fsync, luego usa fileno() para obtener un descriptor de archivo y usa sync() para asegurarte de que los búferes estén limpios. En ese momento, no debería quedar nada del archivo en el espacio de direcciones del proceso, y su llamada al reinicio no debería ocasionar más problemas.

+0

Esta es la manera correcta, no hubo sincronización. – developer

6

El problema inmediato es que no sincroniza el archivo antes de reiniciar. El problema real es que llama a la llamada de sistema reboot directamente, sin tener en cuenta qué más está sucediendo en el sistema. Lo que haces es muy similar a simplemente presionar el botón de reinicio HW; simplemente le das al kernel la oportunidad de limpiar un poco, pero luego todo se mata de la manera más difícil. Esta es una forma segura de corromper sistemas de archivos y estructuras de archivos. ¡No hagas esto!.

En su lugar, debe solicitar al sistema init que realice un reinicio completo. Llamar al reboot syscall requiere acceso privilegiado. Entonces puedes pedirle al sistema init que se reinicie también. En la mayoría de los sistemas hay un enlace simbólico /sbin/reboot que apunta al programa que iniciará un reinicio en buen estado si se llama a través de ese enlace simbólico. Por lo tanto, le recomiendo que reemplace su sucio reboot(RB_AUTOBOOT) con (tenga en cuenta la doble especificación de "/sbin/reboot" en execlp - esto es importante).

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. */ 
} 

Hacerlo de esa manera el sistema puede cerrar de forma correcta, cerrar todos los programas que se ejecutan de una manera limpia y desmontar todos los sistemas de archivos antes de realizar el reinicio, por lo tanto keeing sistema de archivos y la integridad de los datos.

Tenga en cuenta que si espera que su programa no tenga acceso de root, entonces tendrá que saltar algunos aros; en sistemas con systemd puede enviar una solicitud de reinicio por D-Bus. Pero excepto que falle, si el usuario que ejecuta el comando no tiene privilegios de reinicio.

0

Una solución alternativa es llamar sincronización como por la página del manual reinicio

LINUX_REBOOT_CMD_POWER_OFF (RB_POWER_OFF, 0x4321fedc; desde Linux 2.1.30). El mensaje "Apagar". se imprime, el sistema se detiene y toda la energía se elimina del sistema, si es posible. Si no está precedido por una sincronización (2), se perderán los datos.

Cuestiones relacionadas