2012-05-14 14 views
6

我開發了一個C程序(Linux),該程序創建一個新文件並寫入,之後重啓PC。在C程序中調用的Linux重啓函數導致磁盤上的程序創建文件丟失

重新啓動後,我丟失了由我的程序創建的文件。當我停用重啓功能時,我的程序創建的文件仍然存在。

這種行爲被認爲是對Linux: - OpenWrt的(逆火10.03)VirtualBox上(文件系統EXT2) - 的Linux(Ubuntu的)(文件系統EXT4)

你有這種行爲的解釋,我該如何解決它?

#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

自己沖洗(同步)文件:http://linux.die.net/man/2/fsync ... – ChristopheD

+0

我並不反對,但我很驚訝,調用fclose()是不夠的。 – larsks

+0

@larsks查看我的答案。手冊頁明確指出了這一點。雖然它有點奇怪。 – pmr

回答

7

的手冊頁fclose說:

注意FCLOSE()僅僅刷新由 C庫提供的用戶空間緩衝區。爲確保數據在物理上存儲在磁盤上,必須刷新 內核緩衝區,例如,同步(2)或 fsync(2)。

這意味着您在關閉文件描述符之前需要調用fsync

+0

謝謝你的解釋,現在我明白我的程序中的數據丟失。 – developer

1

我認爲這裏重要的是重新啓動永不返回,所以你的程序永遠不會正常退出。

在正常情況下(即在調用fclose之後退出甚至崩潰的程序),FILE *下的文件描述符將關閉並刷新其內核緩衝區。然而,在這種情況下,由於重新啓動永不返回,我懷疑內核緩衝區沒有按照通常的方式清理乾淨,因此因爲它而沒有寫入磁盤。

fsync調用可能會處理它。如果你想成爲偏執狂,做fsync,然後使用fileno()來獲取文件描述符並使用sync()來確保緩衝區被刷新。此時,進程地址空間中不應該存在任何文件,並且您的重新啓動應該不會導致更多問題。

+0

這是正確的方式,沒有同步。 – developer

6

直接的問題是,在重新啓動之前,您沒有同步文件。 實際的問題是,你直接調用reboot系統調用,而不考慮系統上發生了什麼。你所做的與簡單按下硬件重置按鈕非常相似;你只是讓內核有機會做一些清理工作,但是所有的事情都會以困難的方式被殺死。這是最終損壞文件系統和文件結構的絕對方法。 不要這樣做!

而應該讓init系統執行優雅重啓。調用reboot系統調用需要特權訪問。所以你可以讓init系統重啓。在大多數系統中,有一個符號鏈接/sbin/reboot指向程序,如果通過該符號鏈接進行調用,該程序將啓動一個合理的重新啓動。因此,我建議你用髒碼reboot(RB_AUTOBOOT)替換(注意execlp中的"/sbin/reboot"的雙重規格 - 這很重要)。

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

做這種方式的系統能夠正常關閉,終止在一個乾淨的方式運行的所有程序,並做了重新啓動,從而keeing文件系統和數據的完整性之前卸載所有的文件系統。

請注意,如果您希望您的程序不具有root訪問權限,那麼您將不得不跳過一些環節;在systemd系統上,您可以通過D-Bus發送重新啓動請求。但除了失敗之外,如果執行該命令的用戶沒有重新啓動權限。

0

一種替代的解決方案是調用同步按重啓手冊頁

LINUX_REBOOT_CMD_POWER_OFF (RB_POWER_OFF,0x4321fedc;因爲Linux 2.1.30)。消息 「關機」。如果可能,系統將被停止,並且所有電源 都將從系統中移除。如果前面沒有同步(2) ,則數據將丟失。

相關問題