Beim Schreiben wichtiger Dateien muss ein unerwartetes Herunterfahren des Betriebssystems usw. berücksichtigt werden. Wenn Sie nicht wissen, wie es geht, werden halbfertige und leere Dateien generiert, die beim Systemstart und im verknüpften System schwerwiegend sind.
Als Beispiel wird die Sprache C / Java / Python / JavaScript (node.js) angegeben, es ist jedoch erforderlich, Maßnahmen in fast allen Sprachen zu ergreifen.
Es ist ein schwerwiegendes Problem aufgetreten, bei dem die Software in der Produktion nicht gestartet wurde.
Als ich Protokolle und Konfigurationsdateien sammelte und analysierte, waren die Konfigurationsdateien vollständig beschädigt.
Die Konfigurationsdatei wird beim Start gelesen, kann jedoch nach Bedarf geschrieben werden. Wenn ich dem Code folge, stelle ich fest, dass er möglicherweise zur Hälfte geschrieben wird, wenn er während des Schreibvorgangs zwangsweise beendet wird.
Die Stromversorgung wird unterbrochen, wenn sie aufgebraucht ist, und das Timing kann sich auf wundersame Weise überlappt haben.
Wenn Sie es direkt in die Konfigurationsdatei schreiben, können Sie einen halbfertigen Zustand erhalten (wenn Sie beispielsweise 10 Zeichen schreiben möchten, haben Sie immer noch nur 1 Zeichen), auch wenn dies nur kurze Zeit dauert. Sobald Sie mit dem Schreiben in config.yml.tmp fertig sind und es dann in config.yml umbenennen, kann alles aktualisiert werden oder nicht oder atomar!
Es wird mit voller Zufriedenheit veröffentlicht, aber es gibt ein Problem, dass es nicht erneut gestartet wird. Diesmal ist die Konfigurationsdatei 0 KB groß. Wenn das Schreiben der tmp-Datei fehlschlägt, wird sie nicht umbenannt, daher sollte sie im Flow niemals 0 KB groß sein. Das Flush ist explizit und das Schließen ist ordentlich.
Ich fand einen bestimmten Gegenstand, als ich die Linux-Systemprogrammierung von O'Reilly durchblätterte, die ich zur Hand hatte.
Was was··? Schreiben Sie den schmutzigen Puffer auf die Festplatte ...?
An diesem Punkt bemerke ich endlich den Fehler und die Lösung. Nein, ich habe es als Wissen gelernt, aber es kam nicht heraus.
Selbst wenn der Schreibvorgang programmiert ist, wird die Datei nicht sofort geschrieben und vorübergehend im Format eines verschmutzten Puffers gespeichert.
Der Exportvorgang der Festplatte ist extrem langsam. Wenn Sie also jedes Mal exportieren, wird das Programm durcheinander gebracht (100 Mal oder mehr oder auf dieser Ebene). Um dies zu vermeiden, handelt es sich um eine Technik, die in neueren Dateisystemen immer angewendet wurde. Bei diesem Ansatz lagert der Prozess die langsame Schreibverarbeitung an das Betriebssystem aus und ermöglicht es ihm, fortzufahren. Wann das Betriebssystem auf die Festplatte schreibt, hängt vom Dateisystem ab, mit dem das Betriebssystem interagiert. In einigen Fällen kann es 10 Sekunden oder länger dauern, und die Vorlaufzeit ist mehr als ausreichend, um die Datei zu beschädigen.
Wenn das Betriebssystem aufgrund eines unerwarteten Stromausfalls in diesem Zustand ausfällt, wird eine fehlerhafte oder leere Datei erstellt, selbst wenn sie im Programmablauf geleert oder geschlossen wird. Mit anderen Worten, config.yml.tmp selbst war auf halbem Weg, und selbst wenn es in config.yml umbenannt wurde, war es auf halbem Weg.
Durch Aufrufen von fsync () wird der fehlerhafte Puffer sofort auf die Festplatte geschrieben. Dies ist abgeschlossen, bevor mit dem nächsten Schritt fortgefahren wird.
Unter Linux besteht eine Datei aus den folgenden zwei Datentypen.
Der Inode ist das Datum und die Uhrzeit, zu der die Datei geändert wurde, oder die Daten, die bei der Verwaltung in einem Verzeichnis angezeigt werden.
fsync () schreibt beide und fdatasync () schreibt nur die Daten der Datei selbst.
Wenn die Datei nicht sehr oft aktualisiert wird oder Sie sich keine Gedanken über die Leistung machen müssen, können Sie fsync () verwenden.
Verwenden Sie fdatasync (), wenn der Inode (dh Metadaten wie die letzte Aktualisierungszeit) im schlimmsten Fall nicht aktualisiert werden muss oder wenn er häufig aktualisiert wird und die Leistung ein Problem darstellt.
Wenn Sie wichtige Dateien generieren, können Sie zwangsweise auf die Festplatte schreiben, damit Sie sicher sein können, dass es zu einem unerwarteten Herunterfahren kommt. Unter Linux ist das Herunterfahren nach dem regulären Verfahren kein Problem.
Hier sind einige spezifische Beispiele für Code. Einige Fehlerbehandlungen entfallen.
sample.c
int main() {
const char* tmp_file_path = "./fsync_test.txt";
FILE* fp= fopen(tmp_file_path , "w");
int fd = fileno(fp);
fputs("fsync() test\n", fp);
fflush(fp);
//Das ist der Punkt!!
int fsync_ret = fsync(fd);
fclose(fp);
return fsync_ret;
}
FsyncTest.java
import java.io.File;
import java.io.FileOutputStream;
public class FsyncTest {
public static void main(String[] args) throws Exception {
File file = new File("./fsync.txt");
try (FileOutputStream output = new FileOutputStream(file);) {
output.write("Fsync() test\n".getBytes("UTF-8"));
//Das ist der Punkt!!
output.getFD().sync();
}
}
}
sample.py
import os
with open('./fsync_test.txt', 'w') as f:
f.write('fsync() test')
f.flush() #Schmutziger Puffer mit genau dem
#Das ist der Punkt!!
os.fsync(f.fileno())
fsync.js
const http = require('http');
const server = http.createServer((request, response) => {
const fs = require('fs');
fs.open('./fsync_test.txt', 'w', (err, fd) => {
fs.write(fd, 'fsync() test\n', () => {
//Das ist der Punkt!!
fs.fsyncSync(fd);
response.writeHead(200, {'Content-Type': 'text/plain'})
response.end('Write success\n');
fs.close(fd, ()=>{});
})
})
})
server.listen(9000);
Die Datei ist nicht mehr beschädigt.
Übrigens ist die Zeit als schmutziger Puffer ziemlich lang. Je nach Dateisystem kann dies etwa 30 Sekunden dauern. Vor der Gegenmaßnahme habe ich in Windows 7 eine Datei geschrieben, sie mit einem Texteditor geöffnet, bestätigt, dass der Inhalt geschrieben wurde, nach 15 Sekunden den Netzstecker gezogen und die Datei nach dem Start beschädigt War dort. Als ich es in der CentOS 6-Umgebung ausprobierte, war das Ergebnis fast das gleiche.
Nach den Maßnahmen wurde die Gewinnschwelle nicht unmittelbar nach dem Schreiben erreicht.
Unabhängig von der Sprache ist fsync () oder eine gleichwertige Verarbeitung beim Generieren wichtiger Dateien unerlässlich. Dies erzwingt jedoch extrem langsame Exporte synchroner Festplatten, was unter bestimmten Bedingungen schwerwiegende Auswirkungen auf die Leistung haben kann. Es ist NG, es in den dunklen Wolken zu tun, weil es sicher ist.
Sie haben in den Kommentaren einen Link angegeben, der für diesen Artikel von hoher Relevanz ist.
https://www.atmarkit.co.jp/flinux/rensai/watch2009/watch05a.html
Dies ist ein Problem, das durch langsames fsync () verursacht wird. Es ist ein Artikel, der die Verwendung von fdatasync () um den Betrag beschleunigt, um den der Inode nicht aktualisiert wird.
https://masahikosawada.github.io/2019/02/17/PostgreSQL-fsync-issue/ Das Problem ist, dass Sie fsync () nicht erneut aufrufen können, wenn fsync () fehlschlägt. Wenn in PostgreSQL fsync () fehlschlägt, stürzen Sie die Datenbank und das Transaktionsprotokoll (WAL) ab. Es scheint, dass er eine Korrektur vorgenommen hat, um es wiederherzustellen.
Ich habe mich gefragt, ob es überhaupt fehlschlagen würde, aber es scheint, dass es in SAN und NFS leicht auftritt. Wenn Sie in einem allgemeinen System schreiben, sollten Sie die zu schreibenden Daten () behalten und von write () neu beginnen.
fsync () ist eine Linux-Bibliotheksfunktion und kann unter Windows nicht verwendet werden. In Windows kann dies mithilfe der folgenden API realisiert werden.
BOOL FlushFileBuffers(HANDLE hFile);
In Windows 7 kann dies auch durch Festlegen des gesamten Betriebssystems realisiert werden. [1] Öffnen Sie das Bedienfeld [2] Öffnen Sie den Geräte-Manager [3] Wählen Sie eine Festplatte aus dem Festplattenlaufwerk aus und öffnen Sie die Eigenschaften [4] Deaktivieren Sie auf der Registerkarte Richtlinie das Kontrollkästchen "Geräteschreibcache aktivieren".
Beachten Sie, dass diese Methode alle Vorgänge verlangsamt, nicht nur die App.