$ dd if=/dev/zero of=testfile1 bs=1 seek=104857599 count=1 ; ls -ls testfile1
1+0 records in
1+0 records out
1 bytes transferred in 0.000114 secs (8775 bytes/sec)
8 -rw-r--r-- 1 user staff 104857600 5 25 13:24 testfile
$
Sie können eine Datei mit einer Dateigröße von 100 MB erstellen, die jedoch nur 8 Blöcke belegt. Ähnliches können Sie mit dem Linux-Befehl (coreutils) truncate oder dem qemu-Verwaltungsbefehl qemu-img tun.
$ truncate -s 100M testfile2
$ qemu-img create -f raw testfile3 100M
$
dd verwendet zwei Systemaufrufe, lseek (2) und write (2), und truncate und qemu-img verwenden einen Systemaufruf namens ftruncate (2), aber die Ergebnisse sind fast gleich [^ 1].
[^ 1]: Da am Ende von dd 1 Byte geschrieben wird, wird der Plattenblock dieses Teils zugewiesen. Auf der anderen Seite werden Truncate und Qemu-Img möglicherweise überhaupt nicht zugewiesen. Wie viel es tatsächlich zugewiesen wird, hängt von der Implementierung des Dateisystems ab.
Wenn diese Dateien gelesen werden, werden die mit "0" gefüllten Daten gelesen, und beim Schreiben wird der Plattenblock an diesem Punkt zugewiesen und die geschriebenen Informationen werden gespeichert. Der Teil, dem der Plattenblock nicht zugeordnet ist, wird als "Loch" oder "Loch" bezeichnet, und die Datei mit einem solchen Teil wird als "perforierte Datei" oder "Datei mit geringer Dichte" bezeichnet. Seit jeher wurden buchstäblich spärliche Dateien (große Dateien im Vergleich zur Informationsmenge) wie DB-Dateien häufig perforiert, und in letzter Zeit wurde das Image der virtuellen Festplatte der virtuellen Qemu / KVM-Maschine perforiert. Vielleicht.
Die Lücke in dieser Datei bezieht sich auf die Verwendung des Speicherplatzes im Dateisystem und wurde traditionell nicht in der API angezeigt. Zusätzlich zur normalen POSIX-API gab es auch eine DMAPI, die jedoch anscheinend nicht sehr beliebt war.
Seit jeher kann Linux Lücken in Dateien erkennen, indem es die Datei öffnet und ioctl (2) ausgibt. Ein ioctl namens FIBMAP gibt die Nummer des Plattenblocks zurück, in dem die Dateidaten gespeichert sind. Der Lochteil gibt 0 als Plattenblocknummer zurück, damit er erkannt werden kann. Das Problem ist, dass Sie Root-Rechte benötigen, um dieses ioctl auszuführen, und dass Sie ioctl (2) einmal ausgeben müssen, um einen Block nachzuschlagen.
Linux hat auch ein ioctl (2) namens FS_IOC_FIEMAP. Dies ist eine erweiterte Version von FIBM AP, mit der Sie Informationen zu einem bestimmten Dateibereich gleichzeitig abrufen können und für die keine Root-Rechte erforderlich sind. FIEMAP Oktober 2008, in Entwicklung von 2.6.28 Eingeführt in.
Sowohl FIBMAP als auch FS_IOC_FIEMAP waren Methoden, um den spezifischen Speicherort auf der Festplatte zu ermitteln, an dem die Dateidaten gespeichert wurden, und das Loch wurde als Nebeneffekt festgestellt. Als dedizierte API zum Erkennen von Löchern gibt es im Systemaufruf lseek (2) die Optionen SEEK_HOLE und SEEK_DATA. Es wurde ursprünglich unter Solaris und später unter FreeBSD und [Linux] implementiert (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=982d816581eeeacfe5b2b7c6d47d13a157616eff) ) Da es implementiert ist, scheint es gut zu denken, dass es nicht Standard ist, aber eine gewisse Portabilität aufweist.
SEEK_HOLE bewegt sich zum ersten Loch nach dem angegebenen Versatz. SEEK_DATA wird nach dem angegebenen Versatz in ein nicht erstes Loch verschoben. Mit der richtigen Kombination können Sie die Löcher in der gesamten Datei korrekt auflisten.
Wie wäre es andererseits mit einem Loch? Die am Anfang erwähnten Methoden lseek (2) + write (2) und ftruncate (2) können nur am Ende der Datei ein Loch hinzufügen. Mit anderen Worten, es ist nicht möglich, ein Loch in den Teil zu bohren, in dem der Datenblock bereits zugewiesen wurde.
Wiederum geht Solaris voran und ein Befehl namens F_FREESP wird zu fcntl (2) hinzugefügt. Sie können ein Loch in einen bestimmten Bereich der Datei bohren. Dieser API folgen keine anderen Betriebssysteme [^ 2], und unter Linux wurde ein Systemaufruf namens fallocate (2) als FALLOC_FL_PUNCH_HOLE gekennzeichnet. scm / linux / kernel / git / torvalds / linux.git / commit /? id = 79124f18b335172e1916075c633745e12dae1dac) können Sie auch ein Loch in den angegebenen Bereich bohren. In beiden Implementierungen gehen die im angegebenen Bereich aufgezeichneten Daten verloren (beim Lesen werden mit Nullen aufgefüllte Daten zurückgegeben).
[^ 2]: XFS hat jedoch lange Zeit ein Ioctl namens XFS_IOC_FREESP und kann dasselbe wie fcntl (F_FREESP) tun.
Noch vor dem Hinzufügen von APIs, die zum Erkennen von Löchern (ohne Root-Berechtigungen) verwendet werden konnten, konnten der Befehl coreutils cp und GNU tar perforierte Dateien erkennen und diese effizient kopieren und archivieren. Wie wurde dies festgestellt?
Gemäß dem Update-Verlauf von coreutils wurde die Locherkennung durch FS_IOC_FIEMAP in Mai 2010 implementiert. //git.savannah.gnu.org/gitweb/?p=coreutils.git; a = commit; h = dff2b95e4fb22f3e6f3360da0774c784d2f50ff9). Siehe vorherigen [Code](https://git.savannah.gnu.org/gitweb/?p=coreutils.git;a=blob;f=src/copy.c;h=9a014ad5aa672c78b7e3cd69de1e684c73563e1f;hb= Probier es einfach.
Eine Kopie einer regulären Datei ist ungefähr copy_reg () 9a014ad5aa672c78b7e3cd69de1e684c73563e1f; hb = e1aaf8903db97f3240b1551fd6936ccdc652dfc8 # l458). [Zeile 707](https://git.savannah.gnu.org/gitweb/?p=coreutils.git;a=blob;f=src/copy.c;h=9a014ad5aa672c78b7e3cd69de1e684c73563e1f;hb= Die Kopie wird in der while-Schleife von ausgeführt.
Aufmerksamkeit ist Zeile 746, Zähle die Anzahl der Nullen und suche so viel. Mit anderen Worten, wenn die Kopierquelldaten 0 sind, wird nicht 0 in das Kopierziel geschrieben, sondern nur gesucht, sodass es zu einer Lücke werden kann.
Selbst mit den neuesten Coreutils werden Löcher auf diese Weise verarbeitet, wenn FIEMAP nicht verwendet werden kann.
In einem anderen Sinne wurde der Begriff TRIM-Befehl einige Jahre vor der Verbreitung von SSDs zu einem heißen Thema. TRIM löst das Problem, dass das Schreiben von Daten relativ langsam ist, da der NAND-Flash-Speicher, der das Speichermedium der SSD ist, vor dem erneuten Schreiben gelöscht werden muss. Durch Benachrichtigen der SSD über nicht verwendete oder freigegebene Blöcke vom Betriebssystem (Dateisystem) kann die SSD im Voraus gelöscht werden. Darüber hinaus ist es auch beim Verschleißnivellieren vorteilhaft, die Schreibfrequenz für jeden Block von NAND-Flash auszugleichen, der eine Obergrenze für die Anzahl von Schreibvorgängen aufweist.
TRIM ist ein SATA-Befehl, aber SCSI hat auch einen ähnlichen Befehl namens UNMAP. Es gibt SSDs mit SCSI-Verbindung, aber es ist auch für Thin Provisioning mit SAN effektiv. Da der zugewiesene Bereich beim Löschen einer Datei in den Speicher zurückgegeben werden kann, erhöht sich der verwendete Bereich nicht nur monoton wie in der Vergangenheit.
Apropos Thin Provisioning: Virtuelle Festplatten für virtuelle Maschinen können durch Thin Provisioning bereitgestellt werden. Die Qcow2-Datei von Qemu ist beispielsweise ein Thin-Provisioning-Format. Wenn Sie ein Rohbild als perforierte Datei erstellen, wird es Thin-Provisioning-Datei. Also kam ich zurück zum Loch.
Die emulierten SATA- und SCSI-Festplatten von Qemu verstehen den Befehl TRIM (UNMAP), um das Backend-Festplatten-Image (optional) ordnungsgemäß zu bearbeiten.
Zunächst begann SATA, TRIM in [Commit im Mai 2011] zu interpretieren (https://git.qemu.org/?p=qemu.git;a=commitdiff;h=d353fb72f59cd0e1f67baf773e74719cda761a89). Die SCSI-Festplatte ist August 2012. Das VirtIO-Blockgerät ist viel neuer Februar 2019. In Bezug auf Qemu-Versionen entsprechen sie den Veröffentlichungszyklen von 0,15, 1,2,0 bzw. 4,0.
Auf der anderen Seite ist Qcow2 unter den Treibern für virtuelle Back-End-Image-Dateien Januar 2011, TRIM (UNMAP) Lassen Sie nun das Teil los. Die Dateigröße wird jedoch nicht reduziert, und der freigegebene Bereich kann durch Fallocate gestanzt werden. Im Rohformat wird der Bereich TRIM (UNMAP) von fallocate () zu [Januar 2013] hinzugefügt (https://git.qemu.org/?p=qemu.git;a=commitdiff;h=3d4fa43e648f3b169e7ab5dd4e21312e510805d7). Es wurde offen. Es scheint, dass XFS zuvor von ioctl () veröffentlicht wurde.
Wenn Sie nun tatsächlich eine virtuelle Maschine in der Umgebung von Ubuntu 20.04 LTS (Fokus, Linux-5.4-Serie, Qemu-4.2-Serie) erstellen, ist die virtuelle Festplatte (alle VirtIO SCSI-Beispiele) ein Gastbetriebssystem als "Thin Provisionable" -Diskette. Es ist sichtbar von. Wenn Sie beispielsweise den Linux-Gast starten, werden die Dateien thin_provisioning und Bereitstellungsmodus im folgenden sysfs-Festplatteneintrag wie folgt erstellt.
$ cat /sys/bus/scsi/devices/0\:0\:0\:0/scsi_disk/0\:0\:0\:0/thin_provisioning
1
$ cat /sys/bus/scsi/devices/0\:0\:0\:0/scsi_disk/0\:0\:0\:0/provisioning_mode
unmap
$
Wenn Sie sich für Windows-Gäste das Tool "Laufwerkdefragmentierung und -optimierung" (Start-> Unter Windows-Verwaltung) ansehen, lautet der Abschnitt "Medientyp" "Kompatibles Laufwerk mit virtueller Bereitstellung" und wählen Sie "Optimieren". Und TRIM werden ausgeführt (Z: in der Abbildung ist eine iSCSI-Festplatte).
Wenn Sie sich auf der Hostseite mit einer normalen Festplatte dieselbe Datei ansehen, die Sie im Linux-Gast gesehen haben,
$ cat /sys/bus/scsi/devices/0\:0\:0\:0/scsi_disk/0\:0\:0\:0/thin_provisioning
0
$ cat /sys/bus/scsi/devices/0\:0\:0\:0/scsi_disk/0\:0\:0\:0/provisioning_mode
full
$
Es ist ersichtlich, dass es sich nicht um Thin Provisioning (Thick Provisioning) handelt.
Wenn ein Gast einen UNMAP-Befehl ausgibt (weil es sich um SCSI handelt), verfügt das Blockgerät über eine Option zum Verwerfen. In diesem Fall gibt Fallocate (2) Speicherplatz frei. Wenn Sie einen Kernel mit Linux 4.9 oder höher wie Ubuntu 20.04 verwenden, funktioniert [fallocate (2)] nicht nur, wenn es sich bei der virtuellen Festplatte um eine Datei handelt, sondern auch, wenn es sich um ein Hostgerät handelt (z. B. ein LVM-Volume) (https :: //git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=25f4c41415e513f0e9fb1f3fce2ce98fcba8d263), TRIM (UNMAP) wird weitergeleitet.
Ich habe tatsächlich mit einem Rohbild experimentiert.
host# qemu-img create -f raw vol.img 20G
Formatting 'vol.img', fmt=raw size=21474836480
host# qemu-img info vol.img ; ls -lsh vol.img
image: vol.img
file format: raw
virtual size: 20 GiB (21474836480 bytes)
disk size: 4 KiB
4.0K -rw-r--r-- 1 libvirt-qemu kvm 20G May 30 13:42 vol.img
host#
Hängen Sie diese Festplatte an die Gast-SDB an. Mit libvirt die Option zum Verwerfen hinzufügen
<driver name='qemu' type='raw'/>
Wo es aussieht
<driver name='qemu' type='raw' discard='unmap'/>
Machen. Formatieren Sie den Gast, nachdem Sie ihn gestartet und identifiziert haben. Ich habe hier XFS gewählt.
guest:~# mkfs.xfs /dev/sdb
meta-data=/dev/sdb isize=512 agcount=4, agsize=1310720 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=1, sparse=0, rmapbt=0, reflink=0
data = bsize=4096 blocks=5242880, imaxpct=25
= sunit=0 swidth=0 blks
naming =version 2 bsize=4096 ascii-ci=0 ftype=1
log =internal log bsize=4096 blocks=2560, version=2
= sectsz=512 sunit=0 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
guest:~#
Wenn Sie das Bild auf der Hostseite untersuchen, können Sie feststellen, dass es um die Menge der Metadaten größer ist.
host# qemu-img info vol.img ; ls -lsh vol.img
image: vol.img
file format: raw
virtual size: 20 GiB (21474836480 bytes)
disk size: 10.3 MiB
11M -rw-r--r-- 1 libvirt-qemu kvm 20G May 30 13:51 vol.img
host#
Hängen Sie dies ein und erstellen Sie eine 512-MB-Datei.
guest:~# mount /dev/sdb /mnt
guest:~# dd if=/dev/urandom of=/mnt/randomfile bs=1M count=512
512+0 records in
512+0 records out
536870912 bytes (537 MB, 512 MiB) copied, 2.95154 s, 182 MB/s
guest:~#
Die Untersuchung des Bildes auf der Hostseite zeigt, dass es etwa 512 MB größer ist.
host# qemu-img info vol.img ; ls -lsh vol.img
image: vol.img
file format: raw
virtual size: 20 GiB (21474836480 bytes)
disk size: 522 MiB
523M -rw-r--r-- 1 libvirt-qemu kvm 20G May 30 13:52 vol.img
host#
Versuchen Sie als Nächstes, diese Datei zu löschen. TRIM wird ausgegeben, wenn der Mount-Option Discard hinzugefügt wird oder wenn fstrim (8) ausgeführt wird. Führen Sie daher fstrim aus (-v ist ausführlich).
guest:~# rm /mnt/randomfile
guest:~# fstrim -v /mnt
/mnt: 20 GiB (21464170496 bytes) trimmed
guest:~#
Wenn Sie das Bild untersuchen, können Sie feststellen, dass es ungefähr dieselbe Größe wie unmittelbar nach mkfs hat.
host# qemu-img info vol.img ; ls -lsh vol.img
image: vol.img
file format: raw
virtual size: 20 GiB (21474836480 bytes)
disk size: 10.1 MiB
11M -rw-r--r-- 1 libvirt-qemu kvm 20G May 30 13:53 vol.img
host#
Neuere Debian und Ubuntu haben einen Befehl namens zerofree, der den freien Speicherplatz von ext3 und ext4 auf Null zu füllen scheint. Wenn Sie dies mit der Option [erkenne-Nullen = Unmap] von qemu (https://www.qemu.org/docs/master/system/invocation.html#hxtool-1) kombinieren, wird ein zugewiesener Bereich des Disk-Images erstellt. Es kann möglich sein, es zu reduzieren.