2012-05-14 26 views
6

Tôi đã phát triển một chương trình C (Linux), chương trình này tạo một tệp mới và ghi vào, sau đó khởi động lại PC.Chức năng khởi động lại Linux được gọi trong chương trình C gây mất tập tin được tạo bởi chương trình trên đĩa

Sau khi khởi động lại, tôi đã mất tệp do chương trình của tôi tạo. Khi tôi tắt chức năng khởi động lại, tập tin được tạo bởi chương trình của tôi vẫn còn hiện diện.

Hành vi này được xem với Linux: - openWRT (Backfire 10,03) trên VirtualBox (ext2) - Linux (Ubuntu) (hệ thống tập tin ext4)

Có bạn một sự giải thích cho hành vi này và làm thế nào tôi có thể sửa chữa nó?

#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

Tự xả (đồng bộ) tệp: http: //linux.die.net/man/2/fsync ... – ChristopheD

+0

Tôi không đồng ý, nhưng tôi ngạc nhiên khi gọi fclose() là không đủ . – larsks

+0

@larsks Xem câu trả lời của tôi. Trang người đàn ông nêu rõ điều này. Hơi kỳ lạ của nó. – pmr

Trả lời

7

Các trang người đàn ông cho fclose nói:

Lưu ý rằng fclose() chỉ flushes bộ đệm không gian người dùng được cung cấp bởi thư viện C. Để đảm bảo rằng dữ liệu được lưu trữ trên đĩa vật lý, bộ đệm hạt nhân cũng phải được xóa, ví dụ: với đồng bộ hóa (2) hoặc fsync (2).

Có nghĩa là bạn cần gọi fsync trước khi đóng bộ mô tả tệp.

+0

Cảm ơn bạn đã giải thích, bây giờ tôi hiểu nơi nào mất dữ liệu từ chương trình của tôi. – developer

1

Tôi nghĩ điều quan trọng ở đây là khởi động lại không bao giờ trả về, vì vậy chương trình của bạn không bao giờ thực sự thoát bình thường.

Trong điều kiện bình thường (ví dụ: một chương trình thoát hoặc thậm chí bị treo sau khi gọi fclose), bộ mô tả tệp bên dưới FILE * của bạn sẽ bị đóng và bộ đệm hạt nhân của chúng bị xóa.

Trong trường hợp này, tuy nhiên, vì khởi động lại không bao giờ trả về, tôi nghi ngờ rằng bộ đệm hạt nhân không được dọn dẹp theo cách thông thường và do đó công cụ không được ghi vào đĩa vì nó.

Cuộc gọi fsync có thể sẽ xử lý nó. Nếu bạn muốn bị hoang tưởng, hãy làm fsync, sau đó sử dụng fileno() để lấy một bộ mô tả tập tin và sử dụng sync() để đảm bảo rằng các bộ đệm được xóa. Tại thời điểm đó không nên có bất cứ điều gì của tập tin còn lại trong không gian địa chỉ quá trình, và cuộc gọi của bạn để khởi động lại không nên gây ra bất kỳ vấn đề nhiều hơn nữa.

+0

Đây là cách phù hợp, không có đồng bộ hóa. – developer

6

Vấn đề trước mắt là bạn không đồng bộ hóa tệp trước khi khởi động lại. Vấn đề thực tế là, bạn gọi trực tiếp số syscall reboot, bất kể điều gì khác đang xảy ra trên hệ thống. Những gì bạn làm là rất tương tự như chỉ cần nhấn nút reset HW; bạn chỉ cần cung cấp cho hạt nhân cơ hội để làm một chút dọn dẹp, nhưng sau đó mọi thứ đều bị giết một cách khó khăn. Đây là một cách chắc chắn chết để cuối cùng bị hỏng hệ thống tập tin và cấu trúc tập tin. Đừng làm điều này!.

Thay vào đó, bạn nên yêu cầu hệ thống init thực hiện khởi động lại đầy đủ. Gọi số syscall reboot yêu cầu quyền truy cập đặc quyền. Vì vậy, bạn chỉ có thể yêu cầu hệ thống init khởi động lại. Trên hầu hết các hệ thống có một liên kết tượng trưng /sbin/reboot trỏ đến chương trình sẽ bắt đầu khởi động lại sane nếu được gọi thông qua liên kết tượng trưng đó. Do đó tôi khuyên bạn nên thay thế reboot(RB_AUTOBOOT) bẩn của bạn bằng (lưu ý đặc điểm kỹ thuật kép của "/sbin/reboot" trong execlp - điều này là quan trọng).

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

Làm theo cách mà hệ thống có thể đóng cửa một cách duyên dáng, chấm dứt tất cả các chương trình đang chạy trong một cách sạch sẽ và unmount toàn bộ các hệ thống tập tin trước khi thực hiện khởi động lại, do đó keeing hệ thống tập tin và dữ liệu toàn vẹn.

Lưu ý rằng nếu bạn mong đợi chương trình của bạn không có quyền truy cập root, thì bạn sẽ phải nhảy một số vòng; trên các hệ thống có systemd, bạn có thể gửi yêu cầu khởi động lại bằng D-Bus. Nhưng ngoại trừ nó thất bại, nếu người dùng thực hiện lệnh không có đặc quyền khởi động lại.

0

Một giải pháp thay thế là để gọi đồng bộ theo man page reboot

LINUX_REBOOT_CMD_POWER_OFF (RB_POWER_OFF, 0x4321fedc; từ Linux 2.1.30). Thông báo "Tắt nguồn". được in, hệ thống bị dừng lại và tất cả các nguồn điện được lấy ra khỏi hệ thống, nếu có thể. Nếu không đứng trước đồng bộ (2), dữ liệu sẽ bị mất.

Các vấn đề liên quan