繁体   English   中英

在C程序中调用的Linux重启函数会导致程序在磁盘上创建文件丢失

[英]Linux reboot function called in C program causes file loss created by the program on disk

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

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

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

fclose的手册页说:

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

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

当前的问题是,在重新启动之前不要同步文件。 实际问题是,您直接调用reboot syscall,而不考虑系统上发生了什么。 你所做的与简单地按下硬件复位按钮非常相似; 你只是让内核有机会做一些清理工作,但随后一切都被杀死了。 这是最终破坏文件系统和文件结构的可靠方法。 不要这样做!

相反,您应该要求init系统执行gracefull重新启动。 调用reboot syscall需要特权访问权限。 所以你也可以要求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. */
}

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

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

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

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

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

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

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

LINUX_REBOOT_CMD_POWER_OFF(RB_POWER_OFF,0x4321fedc;自Linux 2.1.30起)。 消息“断电”。 如果可能,打印,系统停止,并从系统中移除所有电源。 如果没有同步(2),则数据将丢失。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM