7

我开发了一个 C 程序(Linux),该程序创建一个新文件并写入,然后重新启动 PC。

重新启动后,我丢失了程序创建的文件。当我停用重启功能时,我的程序创建的文件仍然存在。

在 Linux 上可以看到这种行为: - VirtualBox(文件系统 ext2)上的 OpenWrt(Backfire 10.03) - 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

4 回答 4

7

fclose的手册页说:

请注意, fclose() 仅刷新 C 库提供的用户空间缓冲区。为确保数据物理存储在磁盘上,内核缓冲区也必须刷新,例如使用 sync(2) 或 fsync(2)。

这意味着您需要在关闭文件描述符之前调用fsync 。

于 2012-05-14T15:10:14.970 回答
7

直接的问题是,您在重新启动之前没有同步文件。实际的问题是,您reboot直接调用系统调用,而不考虑系统上发生的其他事情。您所做的与简单地按下硬件重置按钮非常相似;你只是给内核做一点清理的机会,但随后一切都被艰难地杀死了。这是最终破坏文件系统和文件结构的绝对可靠方法。不要这样做!.

相反,您应该要求 init 系统执行正常重启。调用reboot系统调用需要特权访问。因此,您也可以要求 init 系统重新启动。在大多数系统上,都有一个符号链接/sbin/reboot指向程序,如果通过该符号链接调用该程序,该程序将启动正常重启。因此,我建议您将脏替换为(注意execlp 中reboot(RB_AUTOBOOT)的双重规范——这很重要)。"/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. */
}

这样做,系统可以正常关闭,终止以干净方式运行的所有程序并在重新启动之前卸载所有文件系统,从而保持文件系统和数据的完整性。

请注意,如果您希望您的程序没有 root 访问权限,那么您将不得不跳一些圈子;在带有 systemd 的系统上,您可以通过 D-Bus 发送重启请求。但除非它失败,如果执行该命令的用户没有重新启动权限。

于 2014-08-18T09:56:00.693 回答
1

我认为这里重要的是重启永远不会返回,所以你的程序永远不会真正正常退出。

在正常情况下(即在调用 fclose 后程序退出甚至崩溃),您的 FILE * 下的文件描述符将被关闭,并且它们的内核缓冲区将被刷新。

但是,在这种情况下,由于重新启动永远不会返回,我怀疑内核缓冲区没有以通常的方式被清理,因此没有将内容写入磁盘。

一个 fsync 调用可能会处理它。如果您想多疑,请执行 fsync,然后使用 fileno() 获取文件描述符并使用 sync() 确保刷新缓冲区。那时,进程地址空间中不应该有任何文件,并且您对重新启动的调用不应该导致任何问题。

于 2012-05-14T15:20:22.490 回答
0

另一种解决方案是根据重启手册页调用同步

LINUX_REBOOT_CMD_POWER_OFF(RB_POWER_OFF,0x4321fedc;自 Linux 2.1.30 起)。消息“关机”。打印,系统停止,如果可能的话,系统的所有电源都会被切断。如果前面没有同步(2),数据将丢失。

于 2015-10-14T19:01:45.750 回答