Vorlesung Betriebssysteme II - Thema 6: Dateisysteme/Massenspeicher Robert Baumgartl 30. Juni 2020

 
WEITER LESEN
Vorlesung Betriebssysteme II
Thema 6: Dateisysteme/Massenspeicher

          Robert Baumgartl

            30. Juni 2020

                                       1 / 44
Überblick

    I Implementierung von Dateisystemen
        I    Kontinuierliche Allokation
        I    Verkettete Liste
        I    Indizierte Speicherung
        I    Journaling
    I I/O-Scheduling
        I    FCFS, SSTF
        I    SCAN (Elevator) und Varianten
        I    Shortest Access Time First (SATF)
        I    Verfahren im Linux-Kernel

                                                 2 / 44
Kontinuierliche Allokation
    I fortlaufende Ablage der Daten einer Datei auf
      Massenspeicher
    I Dateiparameter: Anfangsblock, Länge
    I schneller sequentieller und wahlfreier Zugriff
    I bei konstanter Dateilänge (ROM) vorteilhaft
    I nachträgliche Einfüge- und Löschoperationen nur mit sehr
      großem Aufwand möglich
    I Beispiel: ISO9660

            Nr.   12      13     14     15      16   17   18

            ...   A       A      A      A       B    B    C    ...

                      Logische Speicherblöcke

           Abbildung: Kontinuierliche Allokation von Blöcken

                                                                     3 / 44
Verkettete Liste

    I Listenelement: Nutzdatenfeld + Zeiger auf Nachfolger
    I verwirklicht variable Dateigröße
    I Dateiparameter: Anfangsblock

   Nr.    13            14            15             16         17

   ...     A            A             A              A          B       ...
               0016         0015          FFFF           0014    0042

                                   Endekennzeichen

               Abbildung: Datei als einfach verkettete Liste

                                                                              4 / 44
Verkettete Liste
Nachteile

      I wahlfreier Zugriff sehr langsam (vom Start durch Liste
        suchen)
      I Unterbrechung der Liste führt zum Verlust aller dahinter
        liegenden Daten
      I Nutzdatenteil keine Zweierpotenz (ungünstig für Transfer
        Hauptspeicher ↔ Massenspeicher!)
    → mehrfache Verkettung ? (     behebt Nachteile nicht)

    → Trennung Nutzdaten und Verkettungsinformationen

                                                                     5 / 44
Liste mit Zuordnungstabelle

    I Ablage der Folgezeiger in separater Tabelle
    I Sicherungskopien der Tabelle generierbar
    I Totalverlust des Dateisystems, wenn Tabelle korrumpiert
    I wahlfreier Zugriff effizienter, da kompakte Abspeicherung
      der Verwaltungsinformationen
    I Beispiele: FAT12, FAT16, FAT32
         ...

        13     0016
        14     0015
                             Nr.     13   14     15      16      17
        15     FFFF
        16     0014
        17     0042            ...   A    A      A       A       B    ...
         ...

    Dateizuordnungstabelle

                      Abbildung: Prinzip der Zuordnungstabelle

                                                                            6 / 44
Indizierte Speicherung

    I alle zu einer Datei gehörigen Blocknummern in separaten
      Indexblocks abgelegt
    I Dateiparameter: Startadresse des Indexblocks, Länge
    I sequentieller und wahlfreier Zugriff effizient
    I Verlängern und Verkürzen von Dateien problematisch (→
      Umordnung der Indexeinträge nötig)
    I feste Größe der Indexblocks ebenfalls ungünstig

                                                                 7 / 44
Speicherung mit variablen Indexblocks

                        ...
                      0007
   Start A            0013
                      0016
                                   Indexblock für A
                      0014
   Ende A             0015
   Start B            0042
                                   Indexblock für B
   Ende B             0047
   Start C            0071
                        ...

                                                      8 / 44
Indirekt-indizierte Speicherung

    I Idee: Speicherung der Indexinformationen in separaten
      Blöcken (gleiche Größe wie Nutzdatenblöcke)
    I wenn ein Indexblock nicht ausreicht:
        I Anzahl Indexblöcke erhöhen
        I Anzahl Indirektionsstufen erhöhen
        I Kombination direkter mit indirekter Indexierung
    I langsamerer Zugriff als direkte Indexierung (zusätzliches
      Lesen der Indexblocks nötig)

                                                                   9 / 44
Indirekt-indizierte Speicherung

                  ...
                                     0013
                                     0016
   Start A                           0014
   Start B                           0015      Indexblock
   Start C                           frei

                                         ...
                                     frei

                                  0042
                  ...

                                  0047
                                  frei

                                   ...
                                  frei

                                                            10 / 44
Beispiel: ISO 9660 (1988)

    I keine Freispeicherverwaltung nötig
    I Entwurfsziel: unter jedem Betriebssystem lesbar
      (→ kleinster Nenner – MS-DOS)
    I unterstützt Sets von CDs (maximal 216 − 1), mehrere
      Partitionen pro CD
    I alle Binärfelder sowohl big- als auch little endian-kodiert
    I erlaubte Zeichen im Namen: Großbuchstaben, Ziffern, ’ ’
    Bytes   1 1           8                    8         7          1    2    4 1      4−15
                    Startsektor         Dateigröße   Datum, Zeit             CD#     Dateiname

             Länge des Verzeichniseintrags            Länge des Namens

                                                            Flags             Name        Ext   ; Ver
                                                                                      .

            Abbildung: Verzeichniseintrag im Dateisystem ISO9660

                                                                                                        11 / 44
Beispiel: ISO 9660 (1988)

    I maximale Verschachtelungstiefe: 8
    I 3 verschiedene Levels der Dateibenennung
   Erweiterungen:
    I Rock Ridge – ermöglicht UNIX-Attribute und -Dateinamen
    I Joliet – längere Dateinamen, Unicode-Zeichen, größere
      Tiefe

                                                                12 / 44
Beispiel: UNIX-Dateisystem

    I Verwaltungsstruktur jeder Datei: Indexknoten (index node,
      inode)
    I enthält Attribute (Flags, Zeitstempel,
      Eigentümerinformationen) sowie Blockinformationen:
        I   12 direkte Blockadressen
        I   Adresse eines einfach indirekten Blockes
        I   Adresse eines doppelt indirekten Blockes
        I   Adresse eines dreifach indirekten Blockes

                                                                  13 / 44
Beispiel: UNIX-Dateisystem
Dateiadressierung mittels Inodes
                        Inode
                        Attri−
                         bute                 einfach indi−
                                              rekter Block
     direkte Adressen

                           ...

                                                doppelt indi−
                                                rekter Block

                                                                dreifach indi−
                                                                rekter Block
                                 Datenblock

                                                                                 14 / 44
Beispiel: UNIX-Dateisystem

    I Zugriff auf kurze Dateien effizient
    I Zugriff auf sehr lange Dateien dauert länger, da
      Indirektionsstufen abzuschreiten
    I trotzdem sehr große Dateien realisierbar
    I vgl. struct ext2 inode in
      include/linux/ext2 fs.h

                                                          15 / 44
Journaling
Nachteil traditioneller Dateisysteme

     Operationen über den Dateisystem-Metadaten müssen atomar
     ausgeführt werden, sonst drohen Inkonsistenzen.
     Beispiel: Löschen einer Datei besteht aus 2 Suboperationen.
      a) Löschen des Verzeichniseintrags
      b) Freigabe der Datenblöcke der zu löschenden Datei

    Was passiert bei Unterbrechung (infolge Stromausfall, Reset,
    Systemabsturz usw.)?
  nur a) → verlorene“ Datenblöcke
          ”
  nur b) → Datei ohne Inhalt“
          ”
     Es resultiert ein inkonsistentes Dateisystem, das repariert
     werden muss (z. B. mit fsck). Konsequenzen:
      I Zeitaufwand
      I Gefahr des Datenverlusts bei Nichtbeachtung
                                                                    16 / 44
Journaling
  Idee des Journals:
    I Schreiboperationen in einem reservierten Bereich des
      Massenspeichers, dem Journal, ‘loggen’
    I zu bestimmten Zeitpunkten das Journal auf den
      Massenspeicher ‘durchschreiben’ (Commit)
                    Schreib−
                  operationen

                                  Commit

                     Journal                   Dateisystem
                    (on−disk)                   (on−disk)
                                Dateisystem−
                                Reparatur

    I Bei Unterbrechung wird das Journal erneut
      durchgeschrieben.
                                                             17 / 44
Journaling
Betriebsmodi

    Writeback Mode
     I nur Metadaten im Journal
      I Datenblocks werden direkt geschrieben
    Ordered Mode
     I zuerst werden Datenblocks direkt geschrieben
      I danach Metadaten ins Journal
    Data Mode
     I sowohl Daten als auch Metadaten im Journal
      I am sichersten (kein Datenverlust möglich), aber am
        langsamsten (Daten zweimal geschrieben)

                                                              18 / 44
Journaling-Dateisysteme
Zusammenfassung

   Wann erfolgt Journal Commit?
    I bei Erreichen eines bestimmten Füllstandes“
                                    ”
    I zyklisch

   Vorteile gegenüber konventionellen Dateisystemen
    I kleinere Reparaturzeit nach Crash
     I geringere Inkonsistenzgefahr

   Beispiele: JFS, JFS2 (IBM), XFS (SGI), SFS (Smart File
   System), ext3fs, ext4fs, ReiserFS

                                                            19 / 44
Erweiterte Attribute

    I Standardattribute: Zugriffsrechte, Zeitstempel, Eigentümer,
      . . . (alles, was im struct stat steht)
    I moderne Dateisysteme bieten dem Nutzer (oder dem
      System) die Möglichkeit, eigene Attribute zu definieren und
      mit Werten zu belegen
    I meist als Paare Attributname – Attributwert“, die dem
                          ”
      Inode assoziiert sind, realisiert
    I Anwendungsbeispiele
        I Access Control Lists (ACL) zur feingranularen
          Zugriffssteuerung
        I Alternate Data Streams in NTFS

                                                                     20 / 44
Erweiterte Attribute in Linux-Dateisystemen

    I unterstützt im ext2, ext3, ext4, XFS (u. a.)
    I z. B. in ext2 muss beim Montieren die Option user xattr
      angegeben werden
    I Kommandos getfattr und setfattr zum Auslesen
      bzw. Setzen der (erweiterten) Attribute

                                                                21 / 44
Erweiterte Attribute in Linux-Dateisystemen
Benötigte Systemrufe

      I #include 

                        Ruf   Semantik
           getxattr()         Abfragen eines Attributwertes
           setxattr()         Setzen eines Attributwertes
          listxattr()         Abfragen der Liste aller Attributnamen
        removexattr()         Löschen eines Attributnamens

      I Unterscheide Löschen des Wertes vs. Löschen des
        Attributes!
      I jeweils 3 Varianten, die differieren in
            I Behandlung von symbolischen Links,
            I Adressierung des Inodes über Pfadangabe oder Deskriptor.

                                                                          22 / 44
Optimierung von Massenspeicherzugriffen
  Grundsätzliche Strategien:
    I Beeinflussung des Datenlayouts auf Festplatte → angewandt in
      (strombasierten) Multimedia-Dateisystemen, z. B. CMFS1
    I Scheduling (Umordnen) der wartenden
      Massenspeicher-Operationen
         a) Reduzierung der Positionierungszeit
         b) Reduzierung der Positionierungszeit und
            Rotationsverzögerung
  Zielstellung:
    1. Maximierung des Durchsatzes,
    2. Minimierung der Latenz,
    3. Fairness.
    4. (Garantie der Antwortzeit ↑ Echtzeitsysteme)
     1
     David P. Anderson, Yoshitomo Osawa und Ramesh Govindan. “A File
  System for Continuous Media”. In: ACM Transactions on Computer Systems
  (Nov. 1992), S. 311–337.
                                                                           23 / 44
First Come First Serve (FCFS)

       I Aufträge in Reihenfolge des Eintreffens ausgeführt,
       I fair,
       I leicht zu implementieren,
       I ineffizient → ungünstig bei großer Last.
  Beispiel (40 Sektoren):
  Reihenfolge des Eintreffens: 11, 1, 36, 16, 9, 12, 34
  Reihenfolge der Bearbeitung: 11, 1, 36, 16, 9, 12, 34
  mittlere Weglänge = 10+35+20+7+3+22
                              6         = 976 = 16.2

   0          5       10      15      20      25      30         35

   t

                                                                      24 / 44
Shortest Seek/Service Time First (SSTF)
   I Auftrag mit der kürzesten Distanz zum aktuellen Auftrag
       wird ausgeführt
   I minimiert mittlere Antwortzeit
   I hoher Durchsatz
   I unfair, entfernt“ liegende Aufträge werden benachteiligt,
              ”
   I Verhungern möglich: durch kontinuierliches Eintreffen
       neuer naher“ Forderungen werden entfernte Forderungen
              ”
       blockiert
  Beispiel (identische Voraussetzungen):
  Reihenfolge des Eintreffens: 11, 1, 36, 16, 9, 12, 34
  Reihenfolge der Bearbeitung: 11, 12, 9, 16, 1, 34, 36
  mittlere Weglänge = 1+3+7+15+33+2
                             6         = 61
                                          6 = 10.2
   0      5      10      15      20     25      30      35

   t                                                              25 / 44
Elevator-Algorithmus (SCAN)

    I unidirektionale Bewegung des Kopfes, Abarbeitung aller
      Aufträge, die passiert werden
    I Bewegung des Kopfes
        a) bis zum Ende der Platte oder
        b) bis keine Aufträge in dieser Richtung mehr anstehen
           (LOOK),
    I danach Umkehrung der Richtung und wiederum
      unidirektionale Bewegung usw.
    I etwas schlechtere mittlere Antwortzeit als SSTF, da
      schlechtere Ausnutzung der Lokalität
    I kein Verhungern mehr möglich
    I Benachteiligung der äußeren Sektoren
    I Aufträge, die in der aktuellen Bewegungsrichtung des
      Kopfes liegen, werden bevorteilt

                                                                  26 / 44
Elevator-Algorithmus (SCAN)

  Beispiel:
  Reihenfolge des Eintreffens: 11, 1, 36, 16, 9, 12, 34
  Reihenfolge der Bearbeitung: 11, 12, 16, 34, 36 (U), 9, 1
  (initiale Richtung: aufwärts)
  mittlere Weglänge = 1+4+18+2+27+8
                                 6     = 60
                                          6 = 10.0

   0       5      10      15      20      25      30      35

   t

                                                               27 / 44
Circular Scan (C-SCAN)

   I unidirektionale Bewegung des Kopfes bis keine Aufträge in
     dieser Richtung mehr anstehen, dann Zurückfahren des
     Kopfes an Anfang und erneuter Durchlauf in gleicher
     Richtung
   I geringer Zeitverlust durch Rücklauf des Kopfes (nur
     sinnvoll, wenn Rücklaufzeit vernachlässigt werden kann)
   I Benachteiligung der äußeren Sektoren eliminiert
   I geringere Varianz der Antwortzeit als bei SCAN
  Beispiel (identische Voraussetzungen):
  Reihenfolge des Eintreffens: 11, 1, 36, 16, 9, 12, 34
  Reihenfolge der Bearbeitung: 11, 12, 16, 34, 36 (R), 1, 9
  (Richtung: aufwärts)
  mittlere Weglänge = 1+4+18+2+35+8
                             6         = 68
                                          6 = 11.3

                                                                  28 / 44
SCAN und Varianten
   0         5      10      15     20      25     30     35

   t

       I SCAN und C-SCAN bei hoher Last besser als SSTF
         geeignet (fairer)
       I bei SCAN und C-SCAN kein Verhungern möglich
  Variante: FSCAN
   I 2 Warteschlangen
       I während Abarbeitung einer Schlange (SCAN), treffen neue
         Anforderungen in andere Schlange ein
       I kein Vordrängeln“ später eintreffender Requests mehr
              ”
         möglich
                                                                    29 / 44
Shortest Access Time First (SATF)

    I Einbeziehung der Rotationslatenz (!)
    I Es wird stets der Auftrag ausgewählt, dessen Zugriffszeit
      ta , bezogen auf die aktuelle Kopfposition, minimal ist.
    I greedy
    I Verfahren mit der besten Performance aber höchsten
      Gefahr des Verhungerns abseits gelegener“ Jobs
                               ”

                                                                   30 / 44
Shortest Access Time First (SATF)
Überlappung von Rotation und Seek

    Drehrichtung

   Zielsektor

                                                    tseek

                          trot
                                     Kopfposition
                                                            31 / 44
Shortest Access Time First (SATF)
Berechnung der Access Time ta

    Zur Ermittlung von ta sind notwendig
     I Rotationslatenz trot
      I Zeit für Seek zwischen Kopfposition und Zielsektor tseek
      I Zeit für eine komplette Umdrehung trev
    Fallunterscheidung:
     I Seek beendet, bevor Zielsektor erreicht (trot ≥ tseek ):
         ta = trot
     I Seek zu lang“, d. h. trot < tseek → ganze Umdrehung
                ”
         warten: ta = trot + trev
     I Anzahl zu wartender vollständiger Umdrehungen kann bei
         langen Seeks größer 1 sein
                                                        
                                            tseek − trot
                         ta = trot + trev
                                                trev

                                                                    32 / 44
Techniken zum Verhindern des Verhungerns
Batch-Algorithmen

      I Batched Shortest Access Time First (BSATF): Während
        ein Puffer (komplett) bedient wird, landen alle neu
        eintreffenden in einem anderen Puffer.
      I Je kleiner die Anzahl verbliebener Aufträge im Puffer,
        desto schlechter arbeitet das Verfahren
      I Variation: Two-Mode“ Batching: Im Falle des Verhungerns
                   ”
        BSATF, SATF ansonsten (limitiert negativen
        Performanceeinfluß des Batching)
      I Leaky BSATF: Wenn bei Bedienung eines Puffers noch
        Aufträge hinzukommen, die keinen zusätzlichen Aufwand
        verursachen, werden sie sogleich mit aufgenommen.

                                                                  33 / 44
Techniken zum Verhindern des Verhungerns
Erzwungenes Ausführen des ältesten Auftrags

      I Shortest Access Time First with Urgent Forcing (SATFUF):
        Sobald ein Auftrag eine bestimmte Wartezeit überschritten
        hat, wird er ausgeführt. Alle Aufträge, die auf dem Wege
        zu ihm keine zusätzliche Verzögerung verursachen,
        werden ebenfalls ausgeführt.
      I Alle wartenden Aufträge müssen betrachtet werden.
      I Bei hoher Last (und nur dann tritt Verhungern auf)
        schlechte Performance: nähert sich FCFS; keine Chance
        der Erholung“ der Wartezeit.
            ”
      I Modifikation: SATFUF(N) führt N älteste Aufträge aus,
        sobald Wartezeit ein Limit überschreitet (→ Erholung
        möglich)

                                                                     34 / 44
I/O-Scheduling im Linux-Kern
Grundlagen

     I Annahme: Abbildung LBN → PBN ist sequentiell
     I gewöhnlich write() asynchron, read() synchron
     I       Latenz beim Lesen kritisch, beim Schreiben nicht
     I Leseoperationen hängen häufig voneinander ab,
       Schreiboperationen gewöhnlich nicht
     I Phänomen: “Writes-Starving-Reads” (viele write() vs.
       wenige, voneinander abhängige read())
     I struct request in linux/blkdev.h

                                                                35 / 44
Writes-starving-Reads“
”

   Beobachtung:
    I Leseoperation i. a. synchron
        I Applikation wartet auf Komplettierung
        I Operationen hängen voneinander ab
    I Schreiboperationen i. a. asynchron
        I Daten landen im Buffercache des BS
        I Herausschreiben aus Cache erfolgt später
    I Schreiboperationen erfolgen häufig im Strom
        I es resultieren viele aufeinanderfolgende Schreibvorgänge
          benachbarter Blöcke

                                                                      36 / 44
Writes-starving-Reads“
”Beispiel

   Situation:
    I Kopf auf Block 15
    I Schreibaufträge für Block 42 und 44
    I Leseauftrag für Block 2415
    I neu hinzukommende Schreibaufträge für Blöcke 47–49

   LBN   0       15         42   44         47 48 49         2415
                      ...   W    W    ...   W W W      ...    R      ...

                                             neu
             Kopfposition             hinzugekommen

   D. h. , (lokale) Schreiboperationen drängeln sich vor, lesende
   Applikation muss (unverhältnismäßig) lange warten.
   Schreiboperationen könnten aber verschoben werden.

                                                                           37 / 44
Linus Elevator (Kernel 2.4)

    I 1 globale Auftragswarteschlange, geordnet nach LBN
    I abgearbeitet nach SCAN
    I empfindlich für “Writes-Starving-Reads” (Vordrängeln
      lokaler Schreiboperationen)
    I vgl. drivers/block/elevator.c
   Bei Eintreffen eines neuen Auftrags:
    1. Vereinigung mit benachbartem Auftrag, falls existent (1
       Durchlauf durch WS)
    2. Anhängen an Ende der WS, falls wenigstens 1 Eintrag
       verhungert
    3. Einordnen in Warteschlange

                                                                 38 / 44
Deadline I/O Scheduler

    I globale Auftrags-WS wie im Linus Elevator
    I zwei zusätzliche Queues für Lese- und Schreiboperationen
      (FIFO)
    I jeder Auftrag wird in globale und in Read- oder Write
      Queue eingeordnet
    I Bedienung von Aufträgen aus
        I FIFO Read Queue, wenn Auftrag am Queue-Anfang länger
          als 500 ms wartet,
        I FIFO Write Queue, wenn Auftrag am Queue-Ende länger
          als 5 s wartet,
        I ansonsten aus geordneter WS (via SCAN)
    I entschärft “Writes-Starving-Reads”
    I im Worst Case bessere Latenz für Leseaufträge
    I vgl. drivers/block/deadline-iosched.c

                                                                   39 / 44
Anticipatory I/O Scheduler
    I Erweiterung des Deadline-I/O-Schedulers um Heuristik
   Idee: Nach einer Leseoperation wird kurz (≈6 ms) gewartet, ob
   in dieser Region weitere Leseoperationen erfolgen (Wkt. wegen
   Lokalität des Zugriffs ziemlich hoch), bevor ein Seek zu einer
   anderen Position ausgeführt wird.
   Bsp: ein oder mehrere (langsame, da abhängige) Leseströme,
   gleichzeitig sehr viele Schreiboperationen
     I Scheduler führt Statistik für jeden Prozess über
         I “average think time”
         I “average seek distance”
    I Verbesserung der Reaktionsgeschwindigkeit für
      Leseaufträge
    I insgesamt hoher Durchsatz
    I Default-Scheduler im Kern 2.6
    I vgl. drivers/block/as-iosched.c
                                                                     40 / 44
CFQ und Noop

  Completely Fair Queueing (CFQ)
   I pro Prozess eine eigene Warteschlange
   I Entnahme einer identischen Anzahl Aufträge aus jeder WS
     für das Scheduling
   I entwickelt für Multimedia Storage Server
   I Ziel: Fairneß pro Prozeß
   I vgl. drivers/block/cfq-iosched.c
  Noop I/O Scheduler
   I FIFO
   I faßt benachbarte Aufträge zusammen
   I sonst keine weitere Optimierung
   I für Geräte, die nicht auf Platten basieren (z.B. MTD)

                                                                41 / 44
Praxis

    I Kernel 2.6 enthält: noop, anticipatory (as), deadline, cfq
    I default cfq (seit 2.6.18), bis dahin as
    I Wechsel des Schedulers (Beispiel):
      root@idir:˜$ echo cfq >
      /sys/block/hda/queue/scheduler
    I Auswahl eines geeigneten Schedulers a priori schwierig,
      aber für Desktop-Systeme unkritisch
    I Werkzeug iotop zur Beobachtung der I/O-Bandbreite

                                                                    42 / 44
Was haben wir gelernt?

    I Implementationstechniken typischer Dateisysteme
    I Was ist Journaling?
    I Was sind erweiterte Attribute?
    I Verfahren für I/O-Scheduling
    I I/O-Scheduling im Linux-Kernel

                                                        43 / 44
Lesevorschläge

    I Chris Ruemmler und John Wilkes. “An introduction to disk
      drive modeling”. In: IEEE Computer 27.3 (1994), S. 17–29
    I Michael Kerrisk. The Linux Programming Interface. No
      Starch Press, 2010, Chap. 16 (S. 311ff) (Extended
      Attributes)
    I Martin Pohlack. “Plattenscheduling für
      Quality-Assuring-Scheduling”. Magisterarb. Fakultät
      Informatik, Professur Betriebssysteme: Technische
      Universität Dresden, März 2003 (SATF)
    I Ohad Rodeh, Josef Bacik und Chris Mason. “BTRFS: The
      Linux B-Tree Filesystem”. In: ACM Computing Surveys 9.3
      (Aug. 2013), 9:1–9:32. DOI: 10.1145/2501620.2501623

                                                                 44 / 44
Sie können auch lesen