DE69838367T2 - Paralleles Dateiensystem und Verfahren zur unabhängigen Aufzeichnung von Metadaten - Google Patents

Paralleles Dateiensystem und Verfahren zur unabhängigen Aufzeichnung von Metadaten Download PDF

Info

Publication number
DE69838367T2
DE69838367T2 DE69838367T DE69838367T DE69838367T2 DE 69838367 T2 DE69838367 T2 DE 69838367T2 DE 69838367 T DE69838367 T DE 69838367T DE 69838367 T DE69838367 T DE 69838367T DE 69838367 T2 DE69838367 T2 DE 69838367T2
Authority
DE
Germany
Prior art keywords
file
node
hash
file system
token
Prior art date
Legal status (The legal status is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the status listed.)
Expired - Lifetime
Application number
DE69838367T
Other languages
English (en)
Other versions
DE69838367D1 (de
Inventor
Frank B. Campbell Schmuck
Radha R. Lake Katrine Kandadai
Anthony J. Pleasant Valley Zlotek
Robert J. West Hurley Curran
William A. Millerton Kish
Current Assignee (The listed assignees may be inaccurate. Google has not performed a legal analysis and makes no representation or warranty as to the accuracy of the list.)
International Business Machines Corp
Original Assignee
International Business Machines Corp
Priority date (The priority date is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the date listed.)
Filing date
Publication date
Application filed by International Business Machines Corp filed Critical International Business Machines Corp
Application granted granted Critical
Publication of DE69838367D1 publication Critical patent/DE69838367D1/de
Publication of DE69838367T2 publication Critical patent/DE69838367T2/de
Anticipated expiration legal-status Critical
Expired - Lifetime legal-status Critical Current

Links

Classifications

    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F9/00Arrangements for program control, e.g. control units
    • G06F9/06Arrangements for program control, e.g. control units using stored programs, i.e. using an internal store of processing equipment to receive or retain programs
    • G06F9/46Multiprogramming arrangements
    • G06F9/52Program synchronisation; Mutual exclusion, e.g. by means of semaphores
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F16/00Information retrieval; Database structures therefor; File system structures therefor
    • G06F16/10File systems; File servers
    • G06F16/18File system types
    • G06F16/1858Parallel file systems, i.e. file systems supporting multiple processors

Description

  • Gebiet der Erfindung
  • Diese Erfindung betrifft Rechner und Rechnersysteme und insbesondere ein Dateisystem, das auf mehreren Rechnern läuft, von denen jeder seine eigene Instanz eines Betriebssystems hat und zur Freigabe von Daten mit gemeinsam benutzten Platten, die an ein Netzwerk angeschlossen sind, verbunden ist, d.h., sie betrifft ein Dateisystem mit gemeinsam benutzten Platten.
  • Begriffsglossar
  • Zwar treten bestimmte hier verwendete Begriffe auch in ihrer Bedeutung laut Wörterbuch auf, doch dürfte sich das folgende Glossar mit einigen Begriffen, die sich auf unsere Erfindung beziehen, als nützlich erweisen:
    • – Daten/Dateisystem-Daten: Hierbei handelt es sich um beliebige Bitfolgen, die nur im Kontext einer bestimmten Anwendung eine Bedeutung haben.
    • – Datei: Eine benannte Folge von Bits, auf die eine Rechneranwendung zugreifen kann. Eine Datei hat bestimmte Standardattribute wie zum Beispiel eine Länge, einen Änderungszeitpunkt und einen Zeitpunkt des letzten Zugriffs.
    • – Metadaten: Dies sind die Steuerstrukturen, die von der Dateisystem-Software erzeugt werden, um die Struktur einer Datei und die Verwendung der Platten, die das Dateisystem enthalten, zu beschreiben. Bestimmte Arten von Metadaten, die für Dateisysteme dieser Art gelten, sind:
    • – Verzeichnisse: Hierbei handelt es sich um Steuerstrukturen, die einer Gruppe von Daten, die von einem Informationsknoten (Inode) dargestellt werden, einen Namen zuordnen.
    • – Ein Inode enthält die Attribute der Datei sowie eine Reihe von Zeigern auf Bereiche der Platte, die die Daten, die diese Datei bilden, enthalten. Ein Inode kann durch indirekte Blöcke ergänzt werden, die den Inode um zusätzliche Zeiger ergänzen, wenn die Datei groß ist.
    • – Zuordnungslisten: Hierbei handelt es sich um Steuerstrukturen, die angeben, ob bestimmte Bereiche der Platte (oder andere Steuerstrukturen wie zum Beispiel Inodes) belegt oder verfügbar sind. Dadurch kann die Software verfügbare Blöcke und Inodes wirksam neuen Dateien zuweisen.
    • – Protokolle: Hierbei handelt es sich um einen Satz von Aufzeichnungen, der dazu dient, die anderen Arten von Metadaten im Falle eines Ausfalls synchron zu halten. Ein Protokoll enthält einzelne Aufzeichnungen, die zusammenhängende Aktualisierungen an mehreren Strukturen beschreiben.
    • – Dateisystem: Eine Software-Komponente, die einen festgelegten Satz von Platten verwaltet, der auf verschiedene Arten Zugriff auf Daten ermöglicht, wobei diese Arten von der auf Dateidaten bezogenen Reihe der Standards Xopen und POSIX vorgegeben werden. Der Begriff dient auch zur Beschreibung der Gruppe von Daten und Metadaten, die in einem bestimmten Satz von Platten enthalten sind.
    • – Dateisystem mit gemeinsam benutzten Platten: Ein Dateisystem, bei dem mehrere Rechner ein Dateisystem gemeinsam verwalten, ohne einer einzigen Einheit die gesamte Verwaltung zu übertragen. Alle Rechner sind insofern gleichgestellt, als jeder Rechner jede Aufgabe wahrnehmen kann, die zur Verwaltung der Daten notwendig ist. Bestimmte Aufgaben können bei Bedarf bestimmten Rechnern zugewiesen werden.
    • – Anschluss von gemeinsam benutzten Platten: Dies ist ein Verfahren, das dazu dient, Platten mit mehreren Rechnern mittels eines Protokolls zu verbinden, das die Platten so erscheinen lässt, als wären sie lokal mit jedem Dateisystem verbunden. Das genaue Protokoll für den Anschluss an jeden Rechner ist für diese Arbeit nicht wichtig, beinhaltet aber verschiedene Formen des Anschlusses von Platten an ein Netzwerk, des Anschlusses von umschaltbaren Platten oder des Anschlusses für das Speicher- und Weiterleitungsverfahren (store and forward). Die wichtigsten Punkte sind, dass es dem Dateisystem lokal und allen Instanzen des Dateisystems gleich erscheint.
    • – Quota (Quote): Mittels dieser Funktion beschränkt ein Dateisystem die Nutzung durch einen bestimmten Benutzer oder durch eine benannte Gruppe von Benutzern in dem Dateisystem. Zum Beispiel kann der Administrator den Benutzer "john" auf eine Datenmenge von 100 Megabyte in dem Dateisystem beschränken. Quota ist der Funktionsname, der in der Unix-Umgebung (Warenzeichen von S.C.O.) verwendet wird.
    • – Zugriffssteuerungsliste (Access Control List): Dies ist ein Dateisystem-Verfahren, mittels dessen ein Benutzer den Zugriff auf Daten auf Benutzer beschränken kann, die in einer bestimmten Liste genannt sind.
  • Der Erfindung zugrunde liegender allgemeiner Stand der Technik
  • Es besteht Bedarf an der Bereitstellung von Dateidiensten für Rechner wie zum Beispiel einen MPP-Rechner oder für andere Gruppen von Rechnern, die Teil eines aus angeschlossenen Rechnern bestehenden Netzwerks bilden, welche als eine gemeinsame Datenverarbeitungsressource dienen.
  • Wir haben nun bestimmte "offene" (z.B. Xopen- und POSIX-)Standards in Bezug auf Dateidaten in einem Dateisystem mit gemeinsam benutzten Platten, in dem Datenverarbeitungsaufgaben, die auf verschiedenen Rechnern ausgeführt werden, den Zugriff auf dieselben Dateidaten in der Weise benötigen, als würden sich die Daten lokal an dem Rechner befinden, der die Aufgabe ausführt (um von IBM entwickelte Systeme für verschiedene Systeme betreiben zu können, siehe z.B. die US-Patentschriften 4 274 139 , 5 202 971 und 5 226 159 ). Wenn mehrere Rechner Teil eines Netzwerks und mehrere Platten Teil des Netzwerks sind, besteht ein Bedarf an der Erzeugung eines Dateisystems mit gemeinsam benutzten Platten, das mit den Standards vereinbar ist und dennoch keine Änderung an mehreren Instanzen von Betriebssystemen erforderlich macht, die auf den Rechnern ausgeführt werden, ungeachtet dessen, ob es MMP-Rechner oder Rechner in einem Rechnerverbund sind.
  • Shared File System (SFS) (siehe die US-Patentschrift 5 043 876 ) ist ein Begriff, der auf die Systeme S/390 von IBM angewendet wird, welche unter dem VM von IBM arbeiten, um Daten unter virtuellen Maschinen freizugeben. Gemeinsam benutzte Dateisysteme sind auch als Mittel zur Freigabe von Daten bekannt, wie zum Beispiel die Modelle IMS und GRS von IBM, sie wurden für eine Umgebung mit nur einem einzigen System entwickelt, und unter MVS wurde GRS in einem Verbund von Systemen eingesetzt, die den Plattenspeicher gemeinsam benutzen, und in einem solchen System konnte das GRS kleine Sperrdateien auf der gemeinsam benutzten Platte zuordnen, um den Zugriff auf Datensätze zu serialisieren. MVS muss den Zugriff auf das Inhaltsverzeichnis auf Platten oder auf den Katalog serialisieren, ungeachtet der RESERVES-Operationen, die das Betriebssystem durchführen muss. Dies verursacht einen ziemlichen Systemmehraufwand.
  • Die Datenbank DB2 von IBM ist für die Freigabe von Daten in einer Multiple-Virtual-Storage-(MVS-)/Enterprise-Systems-Architectures-(ESA-)Umgebung ausgelegt, indem sie die Kopplungseinrichtung von IBM nutzt, um die Datenfreigabe zwischen mehreren Systemen zu ermöglichen, was eine System/390-Parallel-Sysplex-Umgebung erforderlich macht, da die Kopplungseinrichtung benötigt wird, um äußerst wirksame und skalierbare Funktionen zur Datenfreigabe bereitzustellen, wobei die Kopplungseinrichtung Verbindungen zwischen Prozessoren verwaltet und ein Nachrichtenpfad-Mechanismus, der in der US-Patentschrift 5 463 736 kurz dargelegt ist, im Grunde der einzige Superserver für die gemeinsam benutzten Daten wird.
  • Bisherige Lösungen, die durch das möglicherweise Beste ihrer Art bei Audio/Video-Dateisystemen (dem VideoCharger-Server von IBM für AIX) vertreten sind und die sich mit Rechnersystemen befassen, die die Erfüllung von Standards ermöglichen würden, beruhen darauf, dass Anforderungen auf Dateisystemebene an einen einzigen Server gesendet werden, der die Daten abruft und sie zurückschickt, oder darauf, dass Anforderungen für Metadaten von einem Client an einen einzigen Server gesendet werden, der es dem ursprünglichen Rechner ermöglicht, die Daten direkt abzurufen. IBM bietet auch ein Programmprodukt mit der Bezeichnung Virtual Shared Disk (VSD) an, mit dem ein SP2-Benutzer Knoten als primäre und sekundäre IBM-VSD-Serverknoten konfigurieren kann. Die VSD-Software ermöglicht es mehreren Knoten, die unabhängige Abbilder des Betriebssystems ausführen, auf eine Platteneinheit, die nur mit einem der Knoten physisch verbunden ist, so zuzugreifen, als wäre die Platteneinheit mit allen Knoten verbunden, wobei IBM bei dem Betriebssystem AIX eine transparente Umschaltung zu einem sekundären Serverknoten vorgesehen hat, wenn der primäre Serverknoten für eine Gruppe von virtuellen gemeinsam benutzten Platten ausfällt. In beiden Fällen stellt das Vorhandensein des einzigen Servers einen Flaschenhals und eine mögliche Fehlerstelle dar, obgleich bei solchen Systemen mit nur einem einzigen Server wie zum Beispiel dem VideoCharger von IBM beträchtliche Fortschritte erzielt wurden, wie das Sperrenverwaltungsprogramm der US-Patentschrift 5 454 108 und die Verbundanordnung der US-Patentschriften 5 490 270 und 5 566 297 zeigen. Wie bei den Systemen der International Business Machines Corporation gibt es auch Funktionen zur Partitionierung einer Platte, auf die über ein Netzwerk zugegriffen wird, so dass ein bestimmter Rechner einen bestimmten Bereich der gemeinsam benutzten Platte verwaltet und darauf zugreift, während er die Bereiche, die einem oder mehreren anderen Rechnern zugewiesen sind, nicht in Anspruch nimmt.
  • Jedoch haben diese Systeme in der Vergangenheit keine zufrieden stellende Lösung geboten, bei der viele Rechner, die über ein Netzwerk Zugriff auf mehrere Platten haben, einem beliebigen Rechner den jederzeitigen Zugriff auf beliebige Daten gestatten dürfen, und insbesondere denjenigen Rechnern, die keine Änderung an einem Betriebssystem oder an einem Standard erforderlich machen, wie sie von uns entwickelt wurde und im Zusammenhang mit unserem Dateisystem mit gemeinsam benutzten Platten beschrieben wird. Nichtsdestotrotz wissen wir die Arbeit der Erfinder der US-Patentschrift 5 454 108 für ihre Fortschritte zu würdigen, da es uns möglich war, eine abgeänderte Version ihres Sperrenverwaltungsprogramms als unser weiterentwickelten Token-Verwaltungsprogramm in unserem eigenen Dateisystem mit gemeinsam benutzten Platten zu verwenden.
  • Shared Disk, UNIX-based, cluster file system (XP 000455986), IBM Technical Disclosure Bulletin, IBM CORP., New York, USA, Band 37(6B), Seiten 209 bis 210 betrifft ein Verfahren zum Aufbau eines Dateisystems, das einen direkten und gleichzeitigen Zugriff auf Dateidaten, die auf einer Platte gespeichert sind, durch zwei oder mehrere Prozessoren ermöglicht.
  • Es ist eine Aufgabe der Erfindung, ein Verfahren bereitzustellen, das die vorstehend genannten Nachteile nach dem Stand der Technik mildert.
  • Zusammenfassung der Erfindung
  • Gemäß der vorliegenden Erfindung stellen wir ein Verfahren nach Anspruch 1 bereit.
  • Eine bevorzugte Ausführungsform der vorliegenden Erfindung stellt ein Dateisystem mit gemeinsam benutzten Platten bereit, wobei eine Instanz des Dateisystems auf jedem Rechner den gleichen Zugriff auf alle Platten hat, die mit dem Dateisystem verbunden sind und einen Teil des Dateisystems bilden. Dies kann mit Hilfe eines Gateway-Prozessors, eines Vermittlungsnetzwerks, einer Hochgeschwindigkeits-Intranetkopplung, die TCP/IP unterstützt, und mit Hilfe von Buskopplungen für einen nichteinheitlichen Speicherzugriff oder anderen ähnlichen Verbindungen realisiert werden. Das Dateisystem mit gemeinsam benutzten Platten unterstützt Plattenlese- und -schreibaufrufe mit zugehörigen Verwaltungsaufrufen. Die Betriebsinstanz ist eine allgemein verfügbare oder standardmäßige Instanz und braucht zur Verwendung unseres Dateisystems mit gemeinsam benutzten Platten nicht verändert zu werden. Wir haben neue Dienste bereitgestellt, die erforderlich sind, damit unser Dateisystem mit gemeinsam benutzten Platten nutzbringend arbeitet.
  • Unser Dateisystem mit gemeinsam benutzten Platten arbeitet als ein paralleles Dateisystem in einer Umgebung mit gemeinsam benutzten Platten. Wir haben einen skalierbaren Verzeichnisdienst für das System mit einem stabilen Cursor eingerichtet. Wir haben eine segmentierte Zuordnungsliste bereitgestellt. Bei unserem skalierbaren parallelen Dateisystem haben wir den dynamischen Vorababruf realisiert. Die Geschwindigkeit in unserem skalierbaren parallelen Dateisystem wurde verbessert, indem die Leistungsfähigkeit des Cachespeichers und die Ausnutzung des Speicherplatzes verbessert wurden. Darüber hinaus unterstützen erweiterte Dateiattribute Zugriffskontrolllisten, die in der UNIX-Welt als ACLs bekannt sind und die sich erstmalig in einem parallelen Dateisystem einsetzen lassen, das in einer Umgebung mit gemeinsam benutzten Platten skalierbar ist.
  • Mit den von uns vorgenommenen Verbesserungen kann eine wirksame grundlegende Dateisteuerung in einer Umgebung mit gemeinsam benutzten Platten für mehrere Rechner erreicht werden, welche die Platte und die Dateiumgebung gemeinsam nutzen. Die den Verzeichnisdienst betreffenden Ansprüche ermöglichen ein wirksames Einfügen und Löschen von Dateien in Datenstrukturen, ohne dass die Datenstrukturen stark beschädigt werden. Dies ist bei parallelen Systemen, bei denen man die ausschließliche Kontrolle über Bereiche erlangen muss, die geändert werden sollen, äußerst wichtig.
  • Durch die Erzeugung unserer Zuordnungsliste ist es möglich, Speicher von derselben Gruppe von Platten parallel zuzuordnen, während die Konsistenz der Metadaten gleichzeitig vollständig gewahrt bleibt. Dies ist wichtig, da jeder der Rechner, der Zugriff auf das Dateisystem hat, weitere Daten erzeugen möchte, und zwar ungeachtet dessen, was in den anderen Rechnern vor sich geht. Unsere Vorababruf-Algorithmen berechnen die verfügbare E/A-Bandbreite und den Datenbedarf der Anwendung, um die Datenmenge zu ermitteln, die vorab abgerufen werden soll. Dies ist bei parallelen Systemen wichtig, bei denen der E/A-Bedarf die verfügbare Bandbreite übersteigen kann. Unsere Entwicklungen bei der Leistungsfähigkeit der Cachespeicher sorgen für Ausgewogenheit bei Gruppen mit Mehrfachzugriff, und obgleich dies nicht mit der Parallelverarbeitung zusammenhängt, stellt es eine allgemeine Verbesserung von Dateisystemen dar. Die Verwendung von Dateiattributen als ein unterstützender Mechanismus gilt auch für nichtparallele Dateisysteme; im Rahmen unserer gesamten parallelen Dateisystem-Mechanismen ist er jedoch sehr wichtig, da er eine wirksame Realisierung von Zugriffssteuerungslisten in einem parallelen Dateisystem ermöglicht.
  • Eine Funktion zur parallelen Aktualisierung derselben Datei oder desselben Verzeichnisses in einer Umgebung mit gemeinsam benutzten Platten wird bereitgestellt. Zudem stellen wir einen Metadaten-Knoten zur Verwaltung von Datei-Metadaten für parallele Lese- und Schreibvorgänge bereit. Bei unserem System dienen Token zur Auswahl und Kennzeichnung von Metadaten-Knoten, und wir verfügen über erweiterte Token-Betriebsarten zur Steuerung der Dateigröße sowie zur intelligenten Zwischenspeicherung von Bytebereich-Token im Cachespeicher, wobei Dateizugriffsmuster verwendet werden, sowie über einen Bytebereich-Sperralgorithmus, der eine Bytebereich-Token-Schnittstelle verwendet.
  • Parallele Aktualisierungen von Dateien machten weiterführende Entwicklungen notwendig, die sich um das Problem drehten, wie sich Metadaten wirksam erzeugen und aktualisieren lassen, während die gleiche Datei gleichzeitig von mehreren Rechnern aktualisiert wird. Eine unserer Lösungen besteht in der Erzeugung eines Metadaten-Knotens, der die konsistente Zusammenführung von bestimmten veränderbaren Metadaten von mehreren Ursprungs-Rechneranwendungen handhabt. Die zweite Lösung stellt ein Sperrschema bereit, um allen Rechnern gegenüber denjenigen Rechner auszuweisen, dessen Dienste sie benötigen. Dadurch entfällt die Notwendigkeit, eine feste Verwaltungsstelle einzurichten, die sich als Flaschenhals erweisen könnte.
  • Jetzt ist die Dateigröße eine Metadaten-Art, die sich in einer parallelen Aktualisierungssituation häufig ändert. Wir haben ein Verfahren bereitgestellt, mit dem man die korrekte Dateigröße zeitoptimal, d.h., wenn die ausführende Anwendung sie benötigt, abrufen kann. Überdies haben wir Sperrverfahren neu festgelegt, um den Mehraufwand des Token-Verwaltungsprogramms in dieser Umgebung zu verringern.
  • Wir haben eine Funktion zur Wiederherstellung des Dateisystems vorgesehen, falls ein Rechner, der an der Verwaltung von gemeinsam benutzten Platten beteiligt ist, nicht mehr verfügbar ist, was aus vielerlei Gründen einschließlich eines Systemausfalls passieren kann. Wir haben ein paralleles Dateisystem-Wiederherstellungsmodell sowie eine synchrone und asynchrone Übernahme eines Metadaten-Knotens vorgesehen.
  • Unser paralleles Dateisystem mit gemeinsam benutzten Platten ermöglicht es, die Steuerung von bestimmten Ressourcen vorübergehend einem ganz bestimmten Rechner zu Änderungszwecken zu übertragen. Während dies der Fall ist, können sich Strukturen auf der Platte, die für andere Rechner sichtbar sind, in einem nicht übereinstimmenden Zustand befinden und müssen im Falle eines Ausfalls korrigiert werden. Um dies zu bewerkstelligen, haben wir ein Verfahren zur Erweiterung der standardmäßigen Protokollierung und zur Wiederherstellung von Sperren bereitgestellt, damit diese Wiederherstellung stattfinden kann, während anderer Rechner weiterhin auf den größten Teil der Daten auf dem Dateisystem zugreifen. Ferner haben wir eine Funktion zur Behandlung des Ausfalls des Metadaten-Knotens vorgesehen. Diese Entwicklung beinhaltet die Korrektur von Metadaten, die geändert wurden, und die Festlegung eines neuen Rechners als Metadaten-Knoten für diese Datei, wie nachstehend beschrieben wird.
  • Nun, in der UNIX-Welt ist das Quotenkonzept "Quota" unter diesem Namen gut bekannt. Es ist ein allgemeines Konzept, das zur Verwaltung des anfänglichen Umfangs eines Speicherbereichs verwendet werden kann, und dieses Konzept wird mit anderen Betriebssystemen wie zum Beispiel denjenigen der Systeme S/390 verwendet. Allgemein gesprochen, wenn wir Quoten berücksichtigen, müssen sie aggressiv verwaltet werden, so dass Sperren nicht ständig benötigt werden, um für einen Benutzer neue Blöcke zuzuordnen. Wir haben wiederherstellbare lokale Anteile für die Quotenverwaltung (Quota Management) vorgesehen, wie nachstehend beschrieben wird.
  • Da eine Quote eine Begrenzung der Plattenkapazität darstellt, die ein Benutzer oder eine Gruppe von Benutzern in Anspruch nehmen kann, haben wir, um das Konzept in unserem parallelen Dateisystem zu verwenden, eine Möglichkeit geschaffen, mittels der lokale Anteile über ein Quoten-Verwaltungsprogramm (das auf die einzelne Quotendatei zugreift) zur parallelen Zuordnung verteilt werden können. Dies ist für diejenigen Fälle von zentraler Bedeutung, in denen ein Benutzer mehrere Anwendungsinstanzen auf verschiedenen Rechnern laufen hat, die ein Dateisystem gemeinsam benutzen. Unsere Entwicklung ermöglicht die sofortige Wiederherstellung in vielen Situationen, in denen zum Zeitpunkt des Ausfalls ausreichend Quoten vorhanden sind. In bestimmten Fällen ist die Ausführung eines Dienstprogramms wie zum Beispiel des standardmäßigen UNIX-Dienstprogramms mit der Bezeichnung "quotacheck" erforderlich, um die Wiederherstellung durchzuführen. Darüber hinaus haben wir ein Verfahren entwickelt, mit dem das Quotenprüfungs-Dienstprogramm "quotacheck" zur selben Zeit wie Anwendungen ausgeführt werden kann, die von Quoten Gebrauch machen, wobei die Beeinträchtigung so gering wie möglich gehalten wird.
  • Diese und andere Verbesserungen werden in der folgenden ausführlichen Beschreibung aufgezeigt. Um die Erfindung mit ihren Vorteilen und Merkmalen besser verstehen zu können, sei auf die Beschreibung und die Zeichnung verwiesen.
  • Zeichnung
  • 1 zeigt ein Dateisystem mit gemeinsam benutzten Platten gemäß unserer Erfindung, das ein Token-Verwaltungsprogramm für Knoten des Rechnersystems enthält.
  • Ausführliche Beschreibung der Erfindung
  • Ein Beispiel für die Ausführungsart von mehreren wichtigen Komponenten unserer bevorzugten Ausführungsform unseres Dateisystems mit gemeinsam benutzten Platten ist in 1 gezeigt. Wie in der Figur dargestellt ist, enthält unser System ein Token-Verwaltungsprogramm 11, das für die Rechner, die als die Knoten 1, 2 und 3 betrachtet werden und an der Verwaltung eines Dateisystems beteiligt sind, Sperrfunktionen bereitstellt. (NB: Für unser Token-Verwaltungsprogramm mussten wir an dem Sperrenverwaltungsprogramm der US-Patentschrift 5 454 108 Änderungen vornehmen.)
  • Unser Dateisystem-Code verwaltet Lese- und Schreiboperationen, die von Anwendungen angefordert werden. Bei dieser Verwaltung werden die Anwendungsanforderungen und die gemeinsam verwalteten Metadaten verwendet, um Daten in dem Dateisystem zu erzeugen und darauf zuzugreifen. Diese Funktion stellt den Großteil der Verarbeitung dar, und sie ist auf allen Rechnern gleich. Mit den geeigneten Token greift diese Verarbeitung über die Funktionen zum Lesen, Beschreiben und Steuern der Platte direkt auf die Platte zu.
  • Die in 1 gezeigte und vorstehend allgemein beschriebene Ausführungsart einer gemeinsam benutzten Platte hat mehrere entscheidende Vorteile gegenüber bisherigen parallelen und Verbunddateisystemen. Sie stellt den kürzesten verfügbaren Pfad zur Verfügung, um die Daten von der Platte an die die Daten nutzende Anwendung und von der die Daten nutzenden Anwendung an die Platte zu übertragen. In dem Pfad gibt es weder für Daten noch für Metadaten einen Dateisystem-Server. Jeder verfügbare Pfad kann verwendet werden, wodurch ein Server als möglicher Flaschenhals oder als eine einzelne Fehlerstelle vermieden werden kann. Da die in dem Sperrenverwaltungsprogramm benötigten zentralen Funktionen nicht an einen bestimmten Rechner gebunden sind, können sie von Rechner zu Rechner verlagert werden, um die Anforderungen an die Leistungsfähigkeit und die Verfügbarkeit zu erfüllen.
  • Um das System, das gerade beschrieben wird, zu erzeugen, mussten wir, wie bereits erwähnt, an dem in der US-Patentschrift 5 454 108 gezeigten Sperrenverwaltungsprogramm Änderungen vornehmen, um verschiedene Wiederherstellungsparadigmen auszuführen, die für Dateisysteme mit gemeinsam benutzten Platten erforderlich sind, und auch, um eine Erweiterung um zusätzliche Sperrzustände vorzunehmen, die für die Verarbeitung durch den Metaknoten erforderlich sind, welche wiederum notwendig ist, um dieselbe Datei parallel aktualisieren zu können. Diese sowie andere Einzelheiten sind nachstehend in den verschiedenen Unterabschnitten dieser ausführlichen Beschreibung näher ausgeführt.
  • Skalierbarer Verzeichnisdienst mit stabilem Cursor und erweiterbarem Streuwertverfahren (Hashing)
  • Für die Realisierung unseres Dateisystems mit gemeinsam benutzten Platten haben wir ein Verfahren zur Speicherung und Indexierung einer großen Gruppe von Datensätzen in einer Weise entwickelt, die sehr schnelle Einfüge-, Lösch- und Suchoperationen sowie den in Folge stattfindenden Abruf (die in Folge stattfindende "Abfrage" ("scan")) aller Datensätze in einer Umgebung unterstützt, die in jeder beliebigen Instanz eines Betriebssystems, selbst in nur einer einzigen Instanz, so realisiert werden kann, dass nicht gegen vorhandene Schnittstellenprogrammierstandards wie zum Beispiel die Spezifikation Single Unix von X/Open verstoßen wird. Folglich beginnen wir mit unserer in Folge stattfindenden Abfrage und den grundlegenden Verfahren zur Speicherung und zur Suche nach Datensätzen. Im Gegensatz zu bisher bekannten Indexierverfahren erzeugt unsere in Folge stattfindende Abfrage vorhersagbare Ergebnisse unter Verwendung von nur einer kleinen begrenzten Menge an Kontextinformationen ("Cursor"), selbst wenn Datensätze eingefügt oder gelöscht werden, während die Abfrage in Ausführung befindlich ist. Das von uns eingesetzte Verfahren gehört zu einem Gebiet der Technik, das als erweiterbares Streuwertverfahren (extendible hashing) bezeichnet wird. In der realisierten Weise kann das erweiterbare Streuwertverfahren Dateien mit freien Bereichen verwenden, ohne eine bestimmte Hash-Tabelle zu speichern. Bei Nutzung des erweiterbaren Streuwertverfahrens können wir folglich Verzeichnisse in einem Dateisystem realisieren, das dem Unix-Standard entspricht, obgleich es nicht auf diesen Standard beschränkt ist. Im Allgemeinen kann unsere bevorzugte Ausführungsform in einer Umgebung eines Unix-Betriebssystems realisiert werden, und diese Umgebung sollte sich als Hintergrund verstehen, wenngleich auch andere Betriebssysteme in Betracht kommen, die dieselben Funktionen verwenden. Tatsächlich kann das Basissystem derzeit mit vielen Betriebssystemebenen oberhalb der Ebene arbeiten, die eigentlich zur Steuerung der Maschine, die wir als Rechner bezeichnen, verwendet wird.
  • Sowohl Datenbanksysteme als auch Universaldateisysteme ermöglichen die Speicherung und den Abruf von Daten, indem ein "Schlüssel" angegeben wird, der einen Datensatz oder eine Datei kennzeichnet. In einem Universaldateisystem dient der Dateiname als Schlüssel zum Zugriff auf die Daten, die in der Datei gespeichert sind; die Struktur, die eine Gruppe von Dateinamen und die zugehörigen Informationen über den Dateizugriff speichert, wird allgemein als Verzeichnis bezeichnet. Wenn die Gruppe der Datensätze oder Dateinamen groß ist, wird häufig eine Hilfsdatenstruktur verwendet, die als Index bezeichnet wird, um die Suchoperationen zu beschleunigen. Ein Index ermöglicht das Auffinden eines Datensatzes in einer Datenbanktabelle oder einen Dateinamen in einem Verzeichnis, ohne dass die gesamte Datenbanktabelle oder das gesamte Verzeichnis abgefragt werden muss.
  • Es gibt mehrere bekannte Indexierverfahren, die auf Hash-Tabellen beruhen, sowie ausgeglichene Suchbäume wie zum Beispiel AVL-Bäume und B-Bäume. Um eine gute Leistung bei Suchoperationen zu erzielen, ist es bei diesen Verfahren erforderlich, zumindest einen Teil des Indexes neu zu organisieren, nachdem eine bestimmte Anzahl von Datensätzen eingefügt oder gelöscht worden ist. Um einen Datensatz in einen B-Baum einzufügen, kann es zum Beispiel notwendig sein, einen Knoten eines B-Baums in zwei neue Knoten aufzuteilen, um Platz für den neuen Datensatz zu schaffen. In der Folge müssen vorhandene Datensätze möglicherweise an einen anderen physischen Speicherort verschoben werden.
  • Dies stellt bei Anwendungen ein Problem dar, die eine Datenbanktabelle oder ein Verzeichnis eines Dateisystems in Folge durchsuchen müssen, zum Beispiel, um den Inhalt eines Verzeichnisses aufzuführen. Solche Anwendungen richten wiederholt Aufrufe an die Datenbank oder das Dateisystem, wobei sie bei jedem Aufruf einen oder mehrere Datensätze abrufen, bis alle Datensätze oder Verzeichniseinträge abgerufen worden sind. Zwischen den Aufrufen muss eine bestimmte Menge an Kontextinformationen, die häufig als "Cursor" bezeichnet werden, verwaltet werden, um verfolgen zu können, wie weit die Abfrage fortgeschritten ist. Dies ist notwendig, damit der nächste Aufruf mit dem Abrufen der verbleibenden Datensätze fortfahren kann. Ausführungsarten von Verzeichnissen von Dateisystemen verwenden üblicherweise den physischen Speicherort oder den Offset eines Eintrags innerhalb eines Verzeichnisses als Cursor für eine in Folge stattfindende Abfrage. Da bei der Aktualisierung eines Indexes wie beispielsweise der Aufteilung eines B-Baums vorhandene Einträge an eine andere Position in dem Verzeichnis verschoben werden können, hat das Einfügen oder Löschen von Verzeichniseinträgen während einer in Folge stattfindenden Abfrage unerwünschte Auswirkungen auf das Ergebnis der Abfrage: Wenn ein vorhandener Eintrag verschoben wird, könnte die in Folge stattfindende Abfrage den Eintrag übersehen oder denselben Eintrag zweimal zurückliefern.
  • Um dieses Problem mit bereits bekannten Indexierverfahren zu lösen, könnte man entweder den Index von den Datensätzen getrennt aufbewahren oder während einer Abfrage mehr Kontextinformationen speichern. Der erstere Ansatz macht Such-, Einfüge- und Löschoperationen aufgrund der zusätzlich notwendigen Dereferenzierungsebene aufwändiger und wesentlich komplexer als unseren bevorzugten Ansatz. Das letztere Verfahren, das Speichern von Kontextinformationen, kann nicht bei einem System angewendet werden, das mit vorhandenen Programmierschnittstellenstandards kompatibel sein muss. Die in der Spezifikation von X/Open Single Unix festgelegte Verzeichnisschnittstelle (die Funktionen readdir, telldir und seekdir) beispielsweise lässt nur einen einzigen 32-Bit-Wert als Cursor für eine in Folge stattfindende Verzeichnisabfrage zu.
  • Mit unserer bevorzugten Entwicklung, die das erweiterbare Streuwertverfahren nutzt, können wir zeigen, wie eine große Gruppe von Datensätzen in einer Weise gespeichert und indexiert werden kann, die sehr schnelle Einfüge-, Lösch- und Suchoperationen sowie eine in Folge stattfindende Abfrage unterstützt. Überdies wird bei unserer bevorzugten Entwicklung als vorteilhaft erkannt werden, dass ein kleiner, begrenzter Cursor-Wert (üblicherweise 32 Bit) ausreicht, um zu gewährleisten, dass eine in Folge stattfindende Abfrage keine doppelten Datensätze zurückliefert und alle vorhandenen Datensätze abruft, d.h. alle Datensätze, außer denjenigen, die eingefügt oder gelöscht wurden, während die Abfrage in Ausführung befindlich war.
  • Es ist nunmehr bekannt, dass das Streuwertverfahren ein Verfahren zur Speicherung von Datensätzen und zur Suche nach Datensätzen mittels eines Schlüssels ist, welches gut funktioniert, wenn ein ungefährer Begrenzungswert der Anzahl der Datensätze im Voraus bekannt ist. Beim Streuwertverfahren wird der verfügbare Speicherbereich in eine feste Anzahl von "Hash-Behältern" ("hash buckets") aufgeteilt. Um einen Datensatz zu speichern, wird eine als "Hash-Funktion" bekannte Abbildung angewendet, bei der der Schlüsselwert einer Hash-Behälternummer zugeordnet wird; der neue Datensatz wird in dem von dem Hash-Wert angegebenen Hash-Behälter gespeichert. Um einen Datensatz mittels eines Schlüssels aufzufinden, wird sein Hash-Wert berechnet; der angeforderte Datensatz kann dann aufgefunden werden, indem nur die Datensätze abgefragt werden, die in dem von dem Hash-Wert angegebenen Behälter gespeichert sind.
  • Im Allgemeinen ist die Anzahl der zu speichernden Schlüsselwerte im Voraus nicht bekannt und kann beliebig groß werden. Dies stellt Probleme bei dem standardmäßigen Hashing-Verfahren dar, bei dem die Höchstzahl der Hash-Behälter von Anfang an bekannt sein muss. Eine erweiterte Form eines Hashing-Algorithmus, die als "erweiterbares Streuwertverfahren" bekannt ist, löst dieses Problem, indem von dem Wert der Hash-Funktion eine veränderliche Anzahl von Bits verwendet wird. Wenn sich ein Hash-Behälter füllt, wird sein Inhalt "aufgeteilt", d.h., ein neuer Hash-Behälter wird hinzugefügt, und ein Teil der Datensätze wird aus dem vorhandenen Hash-Behälter in den neuen Hash-Behälter verschoben. Welche Datensätze verschoben werden, wird durch Neuberechnung der Hash-Funktion und durch Verwendung eines weiteren Bit zur Festlegung der Nummer des Hash-Behälters bestimmt: Datensätze, bei denen das zusätzliche Bit den Wert null hat, bleiben in dem vorhandenen Behälter, und diejenigen Datensätze, bei denen das zusätzliche Bit den Wert eins hat, wandern in den neuen Behälter.
  • Bei Verwendung unserer bevorzugten Ausführungsform, die das erweiterbare Streuwertverfahren nutzt, beginnt ein Index oder ein Verzeichnis mit einem einzigen Hash-Behälter, dem Behälter Nummer null. Solange sie hineinpassen, wandern alle Datensätze ungeachtet ihres Hash-Werts in den ersten Behälter, d.h., Null-Bits der Hash-Funktion dienen zur Festlegung der Nummer des Hash-Behälters. Wenn der erste Behälter voll ist, wird sein Inhalt aufgeteilt, indem ein neuer Hash-Behälter hinzugefügt wird, der Behälter Nummer eins. Nun werden die Bits mit dem Wert "1" der Hash-Funktion zur Platzierung der Datensätze verwendet: Diejenigen Datensätze, deren niedrigstwertiges Bit des Hash-Werts den Wert null hat, bleiben im Behälter Nummer null, und diejenigen Datensätze, deren niedrigstwertiges Bit den Wert eins hat, werden in den Hash-Behälter Nummer eins verschoben. Neue Datensätze werden dem Behälter Nummer null oder dem Behälter Nummer eins in Abhängigkeit von dem Wert des niedrigstwertigen Bits des Hash-Werts hinzugefügt. Nehmen wir nun beispielsweise an, dass sich der Hash-Behälter Nummer eins wieder füllt und sein Inhalt aufgeteilt werden muss. Mit den beiden letzten Bits der Hash-Funktion wird nun festgelegt, wo die Datensätze des Behälters Nummer eins platziert werden. Diejenigen Datensätze mit dem Bit-Wert "01" bleiben im Hash-Behälter Nummer eins, und diejenigen mit dem Bit-Wert "11" wandern in einen neuen Hash-Behälter mit der Nummer drei (binär 11 = dezimal 3). Die Datensätze im Hash-Behälter Nummer null sind von der Teilung nicht betroffen, d.h., Datensätze, deren letzten beiden Bits den Wert "00" oder "10" haben, bleiben solange im Behälter Nummer null, bis sich dieser füllt und sein Inhalt ebenfalls aufgeteilt werden muss. Es ist auch möglich, dass sich der Behälter Nummer eins ganz füllt und sein Inhalt noch einmal aufgeteilt werden muss, bevor der Inhalt des Behälters Nummer null überhaupt jemals aufgeteilt wird.
  • Die Verzeichnisstruktur nach mehreren Aufteilungen des Inhalts der Hash-Behälter kann durch einen binären Baum ("Hash-Baum") dargestellt werden, wie in dem Beispiel in Tabelle 1 gezeigt ist. Ein Datensatz kann aufgefunden werden, indem der Baum von der Wurzel zu einem Blattknoten (Hash-Behälter) durchlaufen wird, wobei die Bits des Hash-Werts zur Entscheidung, welcher Verzweigung an jedem inneren Knoten gefolgt wird, verwendet werden. In Abhängigkeit von der Verteilung der Hash-Werte kann eine Verzweigung des Hash-Baums länger als andere werden. Bei einer gut gewählten Hash-Funktion, d.h. einer Funktion, die gleichmäßig verteilte Hash-Werte erzeugt, gehen wir davon aus, dass alle Verzweigungen des Baums ungefähr dieselbe Tiefe haben. Eine in Folge stattfindende Abfrage des Verzeichnisses wird mittels eines Tiefendurchlaufs des Baums durchgeführt, bei dem die Blattknoten (Hash-Behälter) in der Reihenfolge von links nach rechts besucht werden. TABELLE 1
    Figure 00220001
    Tabelle 1: Beispiel für einen Hash-Baum nach 4 Aufteilungen:
    Der Behälter 0 wurde in den Behälter 0 und den Behälter 1 aufgeteilt,
    der Behälter 0 wurde erneut in den Behälter 0 und den Behälter 2 aufgeteilt,
    der Behälter 2 wurde erneut in den Behälter 2 und den Behälter 6 aufgeteilt,
    der Behälter 1 wurde erneut in den Behälter 1 und den Behälter 3 aufgeteilt.
  • Die Blattknoten des Baums werden mit der Nummer des Hash-Behälters in Binär- und Dezimalschreibweise gekennzeichnet.
  • Gemäß unserer bevorzugten Ausführungsform wird ein Hash-Baum als eine Datei mit freien Bereichen auf der Platte dargestellt, und Datensätze werden verschoben, wenn der Inhalt eines Hash-Behälters aufgeteilt wird, und eine in Folge stattfindende Verzeichnisabfrage durchläuft den Hash-Baum so, dass alle vorhandenen Einträge genau einmal zurückgeliefert werden. Jeder dieser Entwicklungsbereiche hat auf unser System anwendbare Verbesserungen hervorgebracht.
  • In unserem System werden bei der Umsetzung des erweiterbaren Streuwertverfahrens Dateien mit freien Bereichen verwendet. In einem Dateisystem werden Daten, die in eine gewöhnliche Datei geschrieben werden, in einem oder mehreren Plattenblöcken auf der Platte gespeichert. Bei Unix- und Unix ähnlichen Dateisystemschnittstellen ist es möglich, neue Daten hinter das aktuelle Ende einer Datei zu schreiben, indem zwischen Schreibaufrufen "Such"-Aufrufe ausgegeben werden. Dadurch können Dateien mit Lücken oder "Löchern" erstellt werden, d.h. Bereichen innerhalb einer Datei, in die nie Daten geschrieben wurden. Solche Dateien werden als "Dateien mit freien Bereichen" ("sparse files") bezeichnet. Leseoperationen in Dateien mit freien Bereichen liefern an den Stellen, an denen der Lese-Offset und die Länge ein Loch kreuzen, Nullen zurück. Ausführungsarten von Dateisystemen, die Dateien mit freien Bereichen wirksam unterstützen, ordnen Plattenspeicher nur für die Bereiche einer Datei zu, in die Daten geschrieben wurden, jedoch nicht für Löcher oder zumindest nicht für Löcher, die größer als die Blockgröße oder die Einheit der Plattenzuordnung ist, die von dem Dateisystem verwendet wird.
  • Ein Index oder ein Verzeichnis, der/das auf dem erweiterbaren Streuwertverfahren beruht, wird in unserer bevorzugten Ausführungsform mittels einer Datei mit freien Bereichen realisiert. Jeder Hash-Behälter wird in der Datei an einem Offset gespeichert, der mit i*s angegeben wird, wobei i die Nummer des Hash-Behälters (wobei mit der Nummer null begonnen wird) und s die Größe des Hash-Behälters (alle Hash-Behälter haben dieselbe Größe) ist. Das Verzeichnis beginnt als eine leere Datei. Wenn der erste Datensatz eingefügt wird, wird er im Hash-Behälter Nummer null gespeichert, dessen Inhalt anschließend in die Datei geschrieben wird und die Größe der Datei von null auf s erhöht. Wenn der Inhalt des Hash-Behälters Nummer null aufgeteilt werden muss, wird der Inhalt des Behälters 1 in die Datei geschrieben und erhöht die Dateigröße von s auf 2*s. Bei der nächsten Aufteilung des Inhalts des Hash-Behälters wird der Hash-Behälter 2 oder der Hash-Behälter 3 beschrieben, je nachdem, welcher der ersten beiden Behälter als Nächstes aufgeteilt werden muss. Wenn als Nächstes der Inhalt des Behälters Nummer eins aufgeteilt wird, wird der Inhalt des Hash-Behälters Nummer 3 in die Datei geschrieben, wodurch deren Größe von 2*s auf 4*s zunimmt und am Offset 2*s der Datei, an dem der Inhalt des Hash-Behälters 2 gespeichert würde, ein Loch zurückbleibt. Tabelle 2 zeigt, wie der Hash-Baum in dem Beispiel der Tabelle 1 in einer Datei mit freien Bereichen gespeichert werden würde. TABELLE 2
    Figure 00250001
    • Tabelle 2: Abbildung des Hash-Baums der Tabelle 1 in eine Datei mit freien Bereichen.
  • Wie vorstehend beschrieben wurde, könnte ein Datensatz mit einem gegebenen Schlüssel aufgefunden werden, indem der Hash-Baum von oben nach unten durchlaufen und dabei an der Wurzel (dem Behälter null) begonnen würde. Da wir jedoch davon ausgehen, dass alle Verzweigungen des Baums ungefähr die gleiche Tiefe haben, ist es sinnvoller, den Baum von unten nach oben zu durchlaufen. Dies geschieht wie folgt. Wenn die Dateigröße bekannt ist, können wir die Tiefe der längsten Verzweigung des Hash-Baumes berechnen, da alle Nummern von Hash-Behältern in einem Hash-Baum mit der höchstmöglichen Tiefe d eine Anzahl von d Bits oder weniger haben und mindestens ein Hash-Behälter eine Nummer haben muss, bei der das d-te Bit den Wert eins hat. Daher kann die maximale Tiefe d als die Anzahl der Bits in der höchsten Nummer des Hash-Behälters berechnet werden, die gegeben ist durch f/s – 1, wobei f die Dateigröße ist. Um nach einem Datensatz mit einem gegebenen Schlüssel zu suchen, berechnen wir zuerst die Nummer b des Hash-Behälters, die durch die d niedrigstwertigen Bits des Hash-Werts für den gegebenen Schlüssel angegeben ist. Wenn alle Verzweigungen des Hash-Baums dieselbe Tiefe hätten, würden wir den Datensatz ganz sicher in dem von diesem Schlüssel angegebenen Hash-Behälter auffinden. Da die Tiefe der Verzweigung, die den gegebenen Schlüssel speichert, kleiner als d sein kann, ist der Behälter b in dem Hash-Baum möglicherweise noch nicht vorhanden. Wenn dies der Fall ist, hat die Datei ein Loch an dem Offset, der durch b*s angegeben ist. Wenn also ein Loch angetroffen wird, berechnen wir eine neue Nummer b' des Hash-Behälters, indem wir ein Bit weniger vom Hash-Wert verwenden, was den Speicherplatz des Datensatzes ergibt, wenn die Verzweigung des Hash-Baums die Tiefe d-1 hat. Dieser Vorgang wird so lange wiederholt, wie ein Loch in der Datei angetroffen wird. Sobald ein Nicht-Loch gefunden wird, muss sich der Datensatz mit dem gegebenen Schlüssel in diesem Hash-Behälter befinden, sofern er vorhanden ist. Such- und Einfügeoperationen werden wie folgt gehandhabt:
  • Suchoperation:
    • 1. Berechne den Hash-Wert h des Schlüssels, nach dem gesucht wird.
    • 2. Berechne die Tiefe d des Hash-Baums als Logarithmus zur Basis 2 der Dateigröße, geteilt durch die Größe des Hash-Behälters, gerundet auf die nächste ganze Zahl.
    • 3. Berechne die Nummer b des Hash-Behälters als die d niedrigstwertigen Bits von h: b = h mod (2¬d).
    • 4. Rufe den Hash-Behälter aus der Datei am Offset b*s ab, wobei s die Größe des Hash-Behälters ist.
    • 5. Wenn der Hash-Behälter b nicht vorhanden ist (die Datei enthält ein Loch am Offset b*s), verringere d um eins, und schalte zurück zum Schritt 3.
    • 6. Suche nach dem Datensatz mit dem angegebenen Schlüssel im Hash-Behälter b; liefere den Datensatz zurück, sofern er gefunden wurde; gib ansonsten die Fehlermeldung "nicht gefunden" ("not found") zurück.
  • Einfügeoperation:
    • 1. Berechne die Tiefe d des Hash-Baums und die Nummer b des Hash-Behälters, wie in den Schritten 1 bis 5 für die Suchoperation beschrieben wurde, und verwende dabei den Schlüssel des Datensatzes, der eingefügt werden soll.
    • 2. Wenn im Hash-Behälter b bereits ein Datensatz mit dem gegebenen Schlüssel vorhanden ist, gib die Fehlermeldung "bereits vorhanden" ("already exists") zurück.
    • 3. Wenn im Hash-Behälter b ausreichend Platz für den neuen Datensatz vorhanden ist, speichere den Datensatz und kehre zurück. Andernfalls muss der Inhalt des Hash-Behälters b aufgeteilt werden, um Platz für den neuen Datensatz zu schaffen, wie in den nachfolgenden Schritten beschrieben wird.
    • 4. Berechne b' = 2¬d + b
    • 5. Wiederhole die folgenden Schritte für alle Datensätze im Hash-Behälter b: 5a. Berechne v = h mod (2 ¬(d + 1)), wobei h der Hash-Wert für den Schlüssel des Datensatzes ist. Beachte, dass v entweder gleich b oder b' sein muss, weil h mod 2¬d bei allen Datensätzen im Hash-Behälter b gleich b ist. 5b. Wenn v = b', verschiebe den Datensatz in den Hash-Behälter b'; belasse den Datensatz andernfalls in b.
    • 6. Erhöhe d um eins und berechne b neu als h mod (2¬d), wobei h der Schlüssel des Datensatzes ist, der eingefügt werden soll.
  • Gehe zum Schritt 3 zurück.
  • Obgleich die hier beschriebene Ausführungsart des erweiterbaren Streuwertverfahrens mit jeder beliebigen Größe eines Hash-Behälters funktioniert, ist sie leistungsfähiger, wenn die Größe des Behälters gleich der Blockgröße des Dateisystems oder gleich einem Vielfachen der Blockgröße ist. Dies liegt daran, dass eine wirksame Realisierung von Dateien mit freien Bereichen keine Platten-E/A-Operationen erforderlich macht, um ein Loch zu lesen, wenn das Loch an Blockgrenzen des Dateisystems ausgerichtet ist. Daher erfordern alle Suchoperationen höchstens eine Platten-E/A-Operation, um den eigentlichen Hash-Behälter zu lesen, der den Datensatz aufnehmen würde, sofern sich dieser Hash-Behälter nicht gerade im Cachespeicher befindet. Es sei angemerkt, dass hierbei davon ausgegangen wird, dass die Metadaten der Datei, die die Position der Plattenblöcke der Datei enthalten, im Zwischenspeicher abgelegt werden.
  • Bei gleichmäßig verteilten Hash-Werten gehen wir davon aus, dass wir durchschnittlich auf 0,5 Löcher pro Suchoperation stoßen. Wenn die Ausführungsart des erweiterbaren Streuwertverfahrens direkten Zugriff auf die Metadaten des Dateisystems hat (zum Beispiel, wenn sie zur Realisierung von Verzeichnissen in dem Dateisystem selbst verwendet wird), können Löcher erkannt werden, indem man direkt in den Metadaten der Datei nachschlägt. Andernfalls muss die Suchoperation für jede Nummer eines Hash-Behälters, die sie berechnet, mindestens einen Teil der Daten lesen und ein Loch daran erkennen, dass die Leseoperationen alle Nullen zurückgeliefert hat. Dies geschieht am einfachsten, indem Hash-Behälter mit einem kurzen Kopfbereich gespeichert werden, der einen Wert ungleich null enthält.
  • Nun ermöglichen wir Aufspaltungen und Zusammenführungen von Hash-Behältern. Datensätze werden in jedem Hash-Behälter gespeichert und bei der Aufteilung eines Hash-Behälters verschoben. Speicherplatz auf der Platte wird zurückgefordert, indem Hash-Behälter nach dem Löschen von Datensätzen zusammengeführt werden.
  • Jeder Hash-Behälter enthält einen Kopfbereich mit einem Feld "Stufe des Hash-Baums" ("hash tree level"). Der Wert dieses Felds zeigt die Stufe des Hash-Behälters in dem Hash-Baum an, d.h., wie weit der Behälter von der Wurzel des Hash-Baums entfernt ist. Anfangs hat der Baum nur einen Behälter, den Behälter null auf der Stufe null des Hash-Baums. Wenn der Behälter null aufgeteilt wird, ändert sich seine Stufe im Hash-Baum von null in eins; die neue Behälter-Nummer eins ist ein Bruder des Behälters null nach der Aufteilung, d.h., sie hat ebenfalls die Hash-Baum-Stufe eins. Jedes Mal, wenn ein Hash-Behälter aufgeteilt wird, wird seine Stufe um eins erhöht, und dem neuen Behälter, der hinzugefügt wird, wird dieselbe Stufe des Hash-Baums zugeordnet, die auch der Behälter hat, der aufgeteilt wurde.
  • Immer wenn ein neuer Datensatz zu einem Hash-Behälter hinzugefügt wird, speichern wir zu diesem Zeitpunkt zusammen mit dem Datensatz die Hash-Baum-Stufe des Hash-Behälters. Wenn der Inhalt des Hash-Behälters aufgeteilt wird, wird die Stufe des Hash-Baums, die in dem Kopfbereich des Behälters gespeichert ist, erhöht, aber die letzte Stufe des Hash-Baums, die mit jedem Datensatz gespeichert wird, bleibt unverändert.
  • Die Datensätze, die in den neuen Hash-Behälter verschoben werden, behalten ebenfalls ihre ursprünglichen Hash-Baum-Stufenwerte. Indem man die Werte der Hash-Baum-Stufen, die zu einem bestimmten Datensatz gehören, mit der Hash-Baum-Stufe vergleicht, die im Kopfbereich des Hash-Behälters gespeichert ist, ist es folglich möglich, festzustellen, ob der Datensatz eingefügt wurde, bevor oder nachdem der Behälter aufgeteilt worden ist. Die in Folge stattfindende Verzeichnisabfrage bedarf dieser Möglichkeit, wie nachstehend erklärt wird.
  • Ein weiteres Erfordernis der in Folge stattfindenden Abfrage besteht darin, dass der Offset eines Datensatzes in einem Hash-Behälter stabil bleibt, sobald der Datensatz eingefügt worden ist. Wenn wir einen Datensatz in einen Hash-Behälter einfügen oder in einem Hash-Behälter löschen, verbleiben vorhandene Datensätze daher an ihrer ursprünglichen Position, d.h., freier Speicherbereich wird nicht komprimiert. Wenn ein Datensatz aufgrund einer Aufteilung in einen neuen Hash-Behälter verschoben wird, speichern wir den Datensatz überdies in dem neuen Behälter an demselben relativen Offset wie in dem ursprünglichen Hash-Behälter. Zusammen mit den Stufen des Hash-Baums können wir damit den Inhalt eines Hash-Behälters, den er vor seiner Aufteilung hatte, rekonstruieren.
  • Nach einer bestimmten Anzahl von Löschoperationen ist es gegebenenfalls wünschenswert, nicht mehr benötigten Speicherplatz auf der Platte zurückzufordern. Dies kann geschehen, indem zwei Geschwisterblattknoten in dem Hash-Baum zusammengeführt werden, wenn die beiden Knoten über so wenige Datensätze verfügen, dass sie in einen einzigen Hash-Behälter passen. Die in Folge stattfindende Abfrage macht es jedoch erforderlich, dass Datensatz-Offsets sowohl während der Zusammenführungen als auch während der Aufteilungen erhalten bleiben. Dies bedeutet, dass es zur Feststellung, ob zwei Hash-Behälter zusammengeführt werden können, nicht ausreicht, einfach den freien Platz in beiden Behältern zu addieren; stattdessen muss sichergestellt werden, dass es keine zwei Datensätze gibt, die einander überlappen würden, wenn sie zu einem einzigen Hash-Behälter zusammengeführt würden. Der einfachste Weg, dies zu tun, besteht darin, die Zusammenführung von zwei Hash-Behältern so lange aufzuschieben, bis sich einer der beiden vollständig entleert hat.
  • Wenn zwei Hash-Behälter zusammengeführt werden, werden Datensätze von dem einen Hash-Behälter mit der höheren Nummer in den Hash-Behälter mit der niedrigeren Nummer verschoben, und die Stufe des Hash-Baums im Kopfbereich des Behälters mit der niedrigeren Nummer wird um eins verringert. Der Hash-Behälter mit der höheren Nummer wird aus der Datei entfernt, indem ihr Inhalt gelöscht wird. In einem Unix ähnlichen Dateisystem kann dies durch den Aufruf von "fclear" geschehen; wenn das Dateisystem Dateien mit freien Bereichen wirksam ausführt, wird dadurch ein Loch erzeugt, dass der zuvor von dem Hash-Behälter belegte Plattenspeicherbereich freigegeben wird.
  • In unserer bevorzugten Ausführungsform ist zur Unterstützung einer in Folge stattfindenden Abfrage aller Datensätze in einem Verzeichnis oder einem Index eine Abfrageoperation vorgesehen, die wiederholt aufgerufen werden kann, um den Inhalt des Hash-Baums zurückzuliefern, etwas, das wir als eine in Folge stattfindende Verzeichnisabfrage (sequential directory scan) bezeichnen. Jeder Aufruf liefert einen oder mehrere Datensätze sowie einen "Cursor"-Wert zurück, der dem nächsten Abfrageaufruf übergeben werden muss, um die nächste Gruppe von Datensätzen abzurufen. Wir beschreiben zuerst, wie diese Verzeichnisabfrage funktioniert, wenn keine Datensätze eingefügt oder gelöscht werden, während die Abfrage in Ausführung befindlich ist, und anschließend betrachten wir, wie mit Änderungen am Hash-Baum aufgrund von Einfüge- oder Löschoperationen zwischen Aufrufen der Abrufroutine umgegangen wird.
  • Die Verzeichnisabfrage beginnt mit dem Abruf von Datensätzen aus dem äußersten linken Hash-Behälter in dem Hash-Baum, der immer der Hash-Behälter Nummer null ist. Sobald alle Datensätze aus dem Behälter Nummer null zurückgeliefert worden sind, wird die Abfrage mit dem Bruder des Hash-Behälters Nummer null in dem Hash-Baum fortgesetzt. Aufgrund der Art und Weise, in der der Hash-Baum aufgebaut ist, unterscheiden sich die Nummern der Hash-Behälter von zwei Geschwistern in der Tiefe d im Hash-Baum nur in dem d-ten Bit: Das d-te Bit der Nummer des Hash-Behälters hat bei dem linken Bruder den Wert null und bei dem rechten Bruder den Wert eins. Daher ist der Bruder des Hash-Behälters null der Hash-Behälter b1 = 2¬(d-1) (ein einzelnes Bit mit dem Wert "eins" an der d-ten Position). Nachdem alle Datensätze aus dem Hash-Behälter b1 abgerufen worden sind, wird die Abfrage mit dem nächsten Hash-Behälter in dem Hash-Baum in einem Tiefendurchlauf fortgesetzt. Der nächste Hash-Behälter nach dem Behälter b1 ist kein Bruder, hat aber einen gemeinsamen Vorfahr mit dem Hash-Behälter b1 in einer Tiefe d-1 in dem Baum. Daher hat dieser nächste Hash-Behälter ein Bit mit dem Wert "1" an der Bitposition d-1 und ein Bit mit dem Wert null an der Position d, was eine Hash-Behälter- Nummer b2 = 2¬(d-2) ergibt. Unter der Annahme eines Hash-Behälters b in der Tiefe d in dem Hash-Baum wird der nächste Blattknoten bei einem Tiefendurchlauf des Baums im Allgemeinen gefunden, indem man die d niedrigstwertigen Bits von b verwendet, diese Bits umkehrt, zu dem sich ergebenden Wert eins modulo 2¬d addiert und das Ergebnis erneut umkehrt.
  • Eine Abfrage eines Hash-Baums kann daher mit einem Cursor c = (b, r) durchgeführt werden, der aus einer Hash-Behälter-Nummer b und einem relativen Offset r innerhalb eines Hash-Behälters besteht. Eine mit einem Cursor-Wert (b, r) aufgerufene Abfrageoperation prüft zuerst, ob es im Hash-Behälter b an einem Offset, der größer als oder gleich r ist, noch weitere Datensätze gibt. Wenn ja, liefert die Abfrage den nächsten Datensatz nach r und einen neuen Cursor-Wert (b, r') zurück, wobei r' der nächste Offset nach dem Datensatz ist, der zurückgeliefert worden ist. Wenn es im Behälter b an Offsets, die größer als oder gleich r sind, keine weiteren Datensätze gibt, wird die Abfrage mit einem Cursor-Wert von (b', 0) fortgesetzt, wobei b' die nächste Nummer des Hash-Behälters ist, die mittels der vorstehend beschriebenen Bitumkehr-/Biterhöhungsprozedur mit einem Wert von d berechnet wurde, welcher von der Stufe des Hash-Baums angegeben wird, die im Kopfbereich des Behälters b gespeichert ist. Wenn diese Berechnung einen Wert von 0 für b' ergibt, haben wir das Ende des Hash-Baums erreicht, und es gibt keine Datensätze mehr, die zurückgeliefert werden müssen.
  • Änderungen am Hash-Baum aufgrund von Einfüge- oder Löschoperationen werden zwischen Aufrufen der Abfrageroutine durchgeführt. Da wir vorhandenen Datensätze innerhalb eines Blocks nicht verschieben, um einen neuen Datensatz einzufügen oder einen alten Datensatz zu löschen, bleibt die in Folge stattfindende Abfrage von Einfüge- und Löschoperationen unbeeinflusst, solange diese nicht zu einer Aufteilung oder einer Zusammenführung von Hash-Behältern führen. Da vorhandene Datensätze in diesem Fall nicht verschoben werden, wird jeder Datensatz bei der Abfrage höchstens einmal gefunden, und es ist sichergestellt, dass bei der Abfrage alle vorhandenen Datensätze mit Ausnahme derjenigen, die gelöscht wurden, während die Abfrage in Ausführung befindlich war, zurückgeliefert werden. Ein neu eingefügter oder gelöschter Datensatz kann in Abhängigkeit von seiner Position (Hash-Behälter und Offset) und der Zeitsteuerung der Einfüge-/Löschoperation in Bezug auf den Durchlauf des Hash-Baums durch die in Folge stattfindende Abfrage gefunden oder auch nicht gefunden werden. Eine Aufteilung oder Zusammenführung eines Hash-Behälters hat ebenfalls keine Auswirkungen auf die in Folge stattfindende Abfrage, wenn die Aufteilung/Zusammenführung stattfindet, bevor die in Folge stattfindende Abfrage die Hash-Behälter erreicht, die von der Aufteilung/Zusammenführung betroffen sind, oder wenn die Aufteilung/Zusammenführung stattfindet, nachdem die Abfrage die betroffenen Behälter bereits durchlaufen hat.
  • Besondere Vorsicht ist nur in dem Fall geboten, in dem ein Hash-Behälter aufgeteilt oder zusammengeführt wird, wenn die in Folge stattfindende Abfrage einen Teil, aber nicht alle Datensätze in dem Hash-Behälter zurückgeliefert hat, der von der Aufteilung oder Zusammenführung betroffen ist. Bei der Aufteilung eines Blocks könnten einige der Datensätze, die bereits von vorherigen Aufrufen der Abfrageroutine zurückgeliefert worden sind, in den neuen Hash-Behälter verschoben werden, wobei die in Folge stattfindende Abfrage in diesem Fall dieselben Datensätze erneut zurückliefern würde, wenn sie den neuen Block besucht. Umgekehrt könnte eine Zusammenführung von Hash-Behältern dazu führen, dass die Abfrage Datensätze übersieht, die von einem Block, der von der Abfrage noch nicht besucht wurde, in den aktuellen Hash-Behälter an einen Offset verschoben werden, der kleiner als der Offset ist, der von dem aktuellen Wert des Abfrage-Cursors angegeben wird. Diese Erfindung löst diese Probleme, indem sie eine Aufteilung oder eine Zusammenführung eines Hash-Behälters erkennt, die sich auf die in Folge stattfindende Abfrage auswirken würde, und indem sie bei Bedarf den Zustand des Hash-Behälters wiederherstellt, den dieser vor der Aufteilung/Zusammenführung hatte, um die Abfrage fortzusetzen, ohne dass Datensätze übersehen oder dupliziert werden. Eine Aufteilung oder eine Zusammenführung kann festgestellt werden, indem man wie folgt eine Hash-Baum-Stufe in den Cursor-Wert aufnimmt, der von der Abfrageroutine zurückgeliefert wird. Wenn die Abfrageroutine den ersten Datensatz aus einem Hash-Behälter b zurückliefert, liefert sie auch einen Cursor-Wert c = (h, b, r) zurück, der die Hash-Behälter-Nummer b und den relativen Offset, die vorstehend beschrieben wurden, sowie den Wert h der Hash-Baum-Stufe enthält, den man zu dem Zeitpunkt, zu dem der erste Datensatz gelesen wird, im Kopfbereich des Hash-Behälters findet. Wenn dieser Cursor-Wert einem nachfolgenden Aufruf der Abfrageroutine übergeben wird, wird die von dem Cursor-Wert angegebene Stufe h des Hash-Baums mit der aktuellen Stufe h' des Hash-Baums verglichen, die man im Kopfbereich des Hash-Behälters findet. Wenn h' > h ist, muss der Hash-Behälter b zwischen den beiden Aufrufen der Abfrageroutine aufgeteilt worden sein; wenn h' < h oder wenn der Hash-Behälter b nicht mehr vorhanden ist (die Datei enthält jetzt ein Loch am Offset b*s), muss er mit einem anderen Hash-Behälter zusammengeführt worden sein.
  • Aufteilungen von Hash-Behältern (h' > h) werden durchgeführt, indem wieder der Zustand des Hash-Behälters hergestellt wird, den er zum Zeitpunkt der Erzeugung des Cursors hatte. Ein temporärer Pufferspeicher dient zur Aufnahme des wiederhergestellten Hash-Behälters. Nachkommen des ursprünglichen Hash-Behälters werden jeweils einzeln gelesen, und alle Datensätze, die in dem ursprünglichen Hash-Behälter b vorhanden waren, werden in den temporären Pufferspeicher kopiert. Die zu kopierenden Datensätze werden gekennzeichnet, indem die Stufe des Hash-Baums geprüft wird, die zusammen mit jedem Datensatz gespeichert wird, wie im vorherigen Abschnitt beschrieben wurde: Alle Datensätze, deren Hash-Baum-Stufe weniger als oder gleich h ist, waren bereits vorhanden, bevor der Hash-Behälter b aufgeteilt wurde, und werden deshalb kopiert. Da bei einer Aufteilung eines Hash-Behälters der ursprüngliche Offset der Datensätze, die bei der Aufteilung in einen neuen Hash-Behälter verschoben werden, beibehalten wird, ist gewährleistet, dass diese Datensätze an denselben Offset in dem temporären Pufferspeicher zurückkopiert werden können, so dass der temporäre Pufferspeicher genau so aussieht, wie der ursprüngliche Pufferspeicher zum Zeitpunkt der Erzeugung des Cursors aussah (mit Ausnahme der Datensätze, die seitdem gelöscht worden sind). Anschließend fährt die Abfrageroutine mit der Verarbeitung fort, wobei sie den wiederhergestellten Block im temporären Pufferspeicher verwendet. Wenn sie das Ende des temporären Pufferspeichers erreicht, berechnet die Abfrageroutine den nächsten zu besuchenden Hash-Behälter, wobei sie die vorstehend beschriebene Bitumkehr- /Biterhöhungsprozedur mit einem Wert von d verwendet, der von der Hash-Baum-Stufe h vom Abfrage-Cursor angegeben wird.
  • Schließlich werden Zusammenführungen von Hash-Behältern während einer in Folge stattfindenden Abfrage durchgeführt. Eine Zusammenführung wird festgestellt, wenn die Hash-Stufe h, die von dem Abfrage-Cursor c = (h, b, r) angegeben wird, größer als die Hash-Stufe h' ist, die man im Kopfbereich des Hash-Behälters b findet, oder wenn der Hash-Behälter b nicht mehr vorhanden ist, d.h., wenn stattdessen ein Loch angetroffen wurde. Ähnlich wie im Fall der Zusammenführung geschieht dies, indem wieder der Zustand der Hash-Behälter hergestellt wird, den sie zum Zeitpunkt der Erzeugung des Cursors hatten, d.h., bevor sie aufgeteilt wurden. In diesem Fall muss der vorherige Inhalt des Hash-Behälters jedoch nicht in einem gesonderten Pufferspeicher wiederhergestellt werden. Stattdessen führt die Abfrage Operationen an dem zusammengeführten Hash-Behälter aus, durchläuft den Behälter aber mehrfach. In jedem Durchlauf werden nur Datensätze aus einem der ursprünglichen Behälter zurückgeliefert; andere Datensätze werden ignoriert. Dies geschieht, indem der Hash-Wert eines jeden Datensatzes neu berechnet wird und die h niedrigstwertigen Bits des Hash-Werts mit der Nummer b des Hash-Behälters verglichen werden, die von dem aktuellen Abfrage-Cursor angegeben wird. Wenn sie gleich sind, wäre der Datensatz in den Hash-Behälter b gestellt worden, bevor dieser mit einem anderen Hash-Behälter zusammengeführt worden ist, und der Datensatz wird von der Abfrage zurückgeliefert. Andernfalls wird der Datensatz ignoriert. Es sei angemerkt, dass in dem Fall, in dem der Hash-Behälter b nicht mehr vorhanden ist (es wurde stattdessen ein Loch angetroffen), der Behälter, der das Ergebnis der Zusammenführung der Hash-Behälter enthält, gefunden wird, indem man eine oder mehrere Stufen in dem Hash-Baum nach oben wandert, bis ein Nicht-Loch angetroffen wird (ähnlich einer Suchoperation). Wenn die Abfrage das Ende von einem Durchlauf des zusammengeführten Hash-Behälters erreicht, berechnet sie die nächste Nummer b' des Hash-Behälters entsprechend der Bitumkehr-/Biterhöhungsprozedur, die vorstehend beschrieben wurde, mit einem Wert von d, der von der Hash-Baum-Stufe h vom Abfrage-Cursor angegeben wird. Wenn der neue Behälter b' ein weiterer Nachkomme des zusammengeführten Hash-Behälters ist, startet dies den nächsten Durchlauf durch den zusammengeführten Behälter mit dem neuen Cursor-Wert c' = (h, b', 0). Andernfalls ist der letzte Durchlauf durch den zusammengeführten Behälter abgeschlossen und die Abfrage wird normal mit dem Hash-Behälter b' und einem Cursor-Wert c' = (h'', b', 0) fortgesetzt, wobei h'' die Stufe des Hash-Baums ist, die man im Kopfbereich des Behälters b' findet.
  • Ein Programmierer kann das von uns beschriebene Verfahren in jeder Sprache programmieren, in der der nachstehend zusammengefasste Algorithmus für die Abfrageoperation geschrieben werden kann:
    • Eingabe: Cursor-Wert c = (h, b, r) Pufferspeicher zur Rückgabe von einem oder mehreren Datensätzen
    • Ausgabe: in dem bereitgestellten Pufferspeicher zurückgelieferte Datensätze, neuer Cursor-Wert
  • Hinweis: Beim ersten Aufruf der Abfrageroutine sollte ein Cursor-Wert von (0,0,0) übergeben werden; bei nachfolgenden Aufrufen sollte der von dem vorherigen Aufruf zurückgelieferte Cursor-Wert dem nächsten Abfrageaufruf übergeben werden.
    • 1. Setze h' = h, b' = b
    • 2. Lies den Hash-Behälter b' aus der Datei am Offset b*s, wobei s die Größe des Hash-Behälters ist. Wenn der Hash-Behälter b' nicht vorhanden ist (die Datei enthält ein Loch am Offset b'*s), verringere h' um eins, berechne b' neu als b' modulo 2||h', und gehe zum Anfang des Schritts 2 zurück.
    • 3. Lege h' als die Stufe des Hash-Baums fest, die man im Kopfbereich des Hash-Behälters b' findet. Wenn h, b und r alle den Wert null haben (Beginn der Abfrage), setze h auf denselben Wert wie h'.
    • 4. Vergleiche h' mit h. Fahre in Abhängigkeit von dem Ergebnis des Vergleichs mit dem Schritt 5, 6 oder 7 fort, wie nachstehend angegeben ist:
    • 5. Wenn h' = h: Es sei angemerkt, das b in diesem Fall gleich b' sein muss. 5.1 Suche im Hash-Behälter b nach dem nächsten Datensatz an einem Offset, der größer als oder gleich r ist. In Abhängigkeit davon, ob es solch einen Datensatz noch gibt, fahre mit dem Schritt 5.2 oder dem Schritt 5.3 fort, wie nachstehend angegeben ist. 5.2 Wenn es einen solchen Datensatz gibt: Prüfe, ob in dem bereitgestellten Pufferspeicher noch Platz vorhanden ist, um den Datensatz zurückzugeben. Wenn ja, kopiere den Datensatz in den bereitgestellten Pufferspeicher, aktualisiere den Offset r in dem Abfrage-Cursor so, dass er der nächste Offset nach dem Datensatz ist, der soeben kopiert wurde, und gehe anschließend zum Schritt 4 zurück. Wenn es in dem bereitgestellten Pufferspeicher keinen Speicherplatz mehr gibt, verlasse die Abfrageroutine und gib den aktuellen Cursor-Wert zurück. 5.3 Wenn es einen solchen Datensatz nicht gibt: Berechne b'' so, dass er der nächste Hash-Behälter in einem Tiefendurchlauf ist: b'' = reverse(reverse(b, h) + 1, h), wobei Umkehr von (x, n) bedeutet, die n niedrigstwertigen Bit von x zu nehmen und sie umzukehren. Wenn b'' gleich null ist, haben wir das Ende der Abfrage erreicht. Verlasse in diesem Fall die Abfrageroutine, und gib den aktuellen Cursor-Wert zurück. Aktualisiere andernfalls den Cursor c = (h, b, r) wie folgt: Setze b und b' gleich b''. Setze r auf null. Lies den von dem neuen Wert von b angegebenen Hash-Behälter, und lege h und h' als die Stufe des Hash-Baums fest, die man im Kopfbereich des Hash-Behälters findet. Gehe dann zum Schritt 4 zurück.
    • 6. Wenn h' > h: Dieser Fall bedeutet, dass der Hash-Behälter b aufgeteilt worden ist. 6.1 Falls noch nicht geschehen, stelle den Inhalt des Hash-Behälters b wieder so her, wie er vor der Aufteilung vorhanden war, indem alle Nachkommen des Hash-Behälters b in dem Hash-Baum in einem temporären Pufferspeicher zusammengeführt werden. Dies ist für den Behälter b in einem vorherigen Wiederholungslauf möglicherweise schon geschehen; in diesem Fall, kann dieser Schritt übersprungen werden. 6.2 Suche den nächsten Datensatz im temporären Pufferspeicher an einem Offset, der größer als oder gleich r ist. In Abhängigkeit davon, ob es solch einen Datensatz noch gibt, fahre mit dem Schritt 5.2 oder dem Schritt 5.3 fort, wie vorstehend angegeben ist.
    • 7. Wenn h' < h: Dieser Fall bedeutet, dass der Hash-Behälter b mit einem anderen Hash-Behälter zusammengeführt worden ist. 7.1 Suche den nächsten Datensatz im Hash-Behälter b' an einem Offset, der größer als oder gleich r ist. In Abhängigkeit davon, ob es solch einen Datensatz noch gibt, fahre mit dem Schritt 7.2 oder dem Schritt 7.3 fort, wie nachstehend angegeben ist. 7.2 Wenn es einen solchen Datensatz gibt: Berechne den Hash-Wert des Schlüssels in dem Datensatz, und setze b'' so, dass es die h niedrigstwertigen Bit des Hash-Werts darstellt. Wenn b'' ungleich b ist, überspringe diesen Datensatz, d.h., aktualisiere den Offset r in dem Abfrage-Cursor so, dass er der nächste Offset nach diesem Datensatz ist, und gehe zum Schritt 7.1 zurück. Prüfe, ob in dem bereitgestellten Pufferspeicher noch Platz vorhanden ist, um den Datensatz zurückzugeben; wenn nicht, kehre mit dem aktuellen Cursor-Wert zurück. Wenn genügend Platz vorhanden ist, kopiere den Datensatz in den bereitgestellten Pufferspeicher und aktualisiere den Offset r in dem Abfrage-Cursor so, dass er der nächste Offset nach dem Datensatz ist, der soeben kopiert wurde. Gehe zum Schritt 4 zurück. 7.3 Wenn es einen solchen Datensatz nicht gibt: Berechne b'' so, dass er der nächste Hash-Behälter in einem Tiefendurchlauf ist: b'' = reverse(reverse(b, h) + 1, h) Wenn b'' gleich null ist, haben wir das Ende der Abfrage erreicht. Verlasse in diesem Fall die Abfrageroutine, und gib den aktuellen Cursor-Wert zurück. Prüfe andernfalls, ob (b mod 2¬h') gleich (b' mod 2¬h') ist. Wenn ja, bedeutet dies, dass der nächste zu besuchende Behälter noch einer der Behälter ist, die zum Behälter b' zusammengeführt worden sind. Setze in diesem Fall r auf null, und gehe zum Anfang des Schritts 7 zurück, was den nächsten Durchlauf durch den zusammengeführten Behälters b' startet. Andernfalls ist der letzte Durchlauf des zusammengeführten Behälters beendet. Fahre in diesem Fall wie im Schritt 5.3 angegeben fort, d.h., setze b und b' auf b'', setze r auf null, lege h und h' als die Stufe des Hash-Baums fest, die man im Kopfbereich des Hash-Behälters b findet, und gehe anschließend zum Schritt 4 zurück.
  • Nachdem wir diese Ausführungsart unserer in Folge stattfindenden Abfrageprozedur beschrieben haben, wenden wir uns nun dem zur Codierung des Cursor-Werts angewandten Verfahren zu.
  • Um die Anzahl der Bits, die erforderlich sind, um einen Cursor-Wert aufzunehmen, auf ein Mindestmaß zu verringern, können die Stufe des Hash-Baums und die Nummer des Hash-Behälters zu einem einzigen Wert zusammengefasst werden, der nur ein Bit mehr als die Anzahl der Bits erfordert, die zur Aufnahme der größten zulässigen Behälternummer benötigt werden. Dies ist möglich, weil die Behälternummer immer kleiner als oder gleich 2 ¬ L sein muss, wobei L die Stufe ist. Die Codierung wird nachstehend beschrieben. Ein von dieser Codierung verwendeter Parameter ist die größtmögliche Stufe des Hash-Baums, d.h. die größtmögliche Tiefe, die eine Verzweigung des Baumes erreichen kann.
  • Die Codierung des Cursors für die Stufe L des Hash-Baums und die Nummer B des Hash-Behälters ist wie folgt:
    Sei M = die größtmögliche Stufe des Hash-Baums
    Berechne H = M – L
    Berechne R = bitweise Umkehr von B
    Codiere die Behälternummer und die Stufe als 2¬H + R*2¬(H + 1).
  • Zähle zur Decodierung die Anzahl der niederwertigen Null-Bits, und subtrahiere diesen Wert von M, um die Stufe (L) zu erhalten. Um die Behälternummer zu erhalten, verschiebe den codierten Wert um L + 1 Bit nach rechts, und führe eine bitweise Umkehr des Ergebnisses durch.
  • Selbstverständlich kann sich der Fachmann auch optionale Funktionen vorstellen, nachdem er diese Beschreibung gelesen hat. Zum Beispiel kann das System eine Sperr- und Gleichzeitigkeitssteuerung durchführen, um gleichzeitige Aktualisierungen in verschiedenen Hash-Behältern zu ermöglichen und auch Überlaufblöcke zu realisieren. Da wir nicht wirklich einen temporären Pufferspeicher benötigen, um Aufteilungen während einer in Folge stattfindenden Abfrage durchzuführen, könnten wir den vom Aufrufer bereitgestellten Pufferspeicher verwenden. Insbesondere könnte man sich in den Fällen, in denen es keinen Sinn macht, einen ganzen Behälter wiederherzustellen, nur um einen einzigen Datensatz zurückzuliefern, Anwendungen vorstellen, die eine Schnittstelle für eine in Folge stattfindende Abfrage verwenden, welche nur jeweils einen Datensatz (z.B. database?) zurückliefert.
  • Zuordnen von Speicherplatz in einem Dateisystem mit gemeinsam benutzten Platten
  • Die parallele Zuordnung ist ein Merkmal unserer bevorzugten Ausführungsform. Dies bedeutet, dass wir zur Codierung eine Zuordnungsliste bereitstellen (z.B. eine Bitmap), die im Vergleich zu einer herkömmlich codierten Zuordnungsliste die Beeinträchtigungen zwischen mehreren Knoten verringert, die gleichzeitig Plattenblöcke auf mehreren Platten zuordnen, welche eine Dateistruktur mit gemeinsam benutzten Platten bilden. Unser System ermöglicht auch die gleichzeitige Freigabe von Plattenblöcken durch mehrere Knoten bei geringerer gegenseitiger Beeinträchtigung.
  • Zwar gibt es Zuordnungskonzepte, die in einem Dateisystem realisiert werden, und es gibt auch herkömmliche Verfahren, die von einem Dateisystem zur Zuordnung von Speicherplatz verwendet werden können, doch gibt es Probleme bei den herkömmlichen Verfahren, die in einem Dateisystem mit gemeinsam benutzten Platten angewendet werden, und dies hat eine Erfindung notwendig gemacht, die die Zuordnung und die Freigabe von Speicherplatz ermöglicht und die in einem Dateisystem mit gemeinsam benutzten Platten, wie sie in einem parallelen Dateisystem verwendet werden, gut funktioniert.
  • Im Allgemeinen ist ein Dateisystem ein Rechnerprogramm, das es anderen Anwendungsprogrammen ermöglicht, Daten auf Datenträgern wie zum Beispiel Plattenlaufwerken zu speichern und von ihnen abzurufen. Der Kürze halber wird in der nachfolgenden Erörterung der Begriff Platte verwendet, aber die Konzepte gelten für jedes ähnliche Speichermedium mit Blockstruktur. Eine Datei ist ein benanntes Datenobjekt, das eine beliebige Größe haben kann. Das Dateisystem ermöglicht Anwendungsprogrammen, Dateien anzulegen und einen Namen für sie zu vergeben, Daten in ihnen zu speichern (oder Daten in sie zu schreiben), Daten aus ihnen zu lesen, sie zu löschen und andere Operationen an ihnen durchzuführen.
  • Im Allgemeinen handelt es sich bei einer Dateistruktur um die Anordnung von Daten auf den Plattenlaufwerken. Neben den Dateidaten selbst enthält die Dateistruktur Metadaten: ein Verzeichnis, das den entsprechenden Dateien Dateinamen zuordnet, Datei-Metadaten, die Informationen über die Datei enthalten, vorrangig den Speicherort der Dateidaten auf der Platte (d.h., welche Plattenblöcke die Dateidaten enthalten), eine Zuordnungsliste, die erfasst, welche Plattenblöcke gerade zur Speicherung von Metadaten und Dateidaten verwendet werden, und einen Superblock, der allgemeine Informationen über die Dateistruktur (z.B. den jeweiligen Speicherort des Verzeichnisses, der Zuordnungsliste und anderer Metadatenstrukturen) enthält.
  • Andererseits muss man erkennen, dass ein Dateisystem mit gemeinsam benutzten Platten ein Dateisystem ist, bei dem auf eine Dateistruktur, die sich auf einer oder auf mehreren Platten befindet, von mehreren Dateisystemen zugegriffen wird, die auf getrennten Rechnern laufen. Zum Zweck unserer bevorzugten Ausführungsform gehen wir hinsichtlich der Dateistruktur davon aus, dass diese Rechner (oder Knoten) keinen gemeinsam benutzten Speicher haben (obgleich sie über einen lokalen Speicher und mindestens einen Teil eines gemeinsam benutzten Speichers, wie ihn viele SMPs haben, verfügen könnten und in vielen geeigneten Ausführungsarten auch darüber verfügen würden) und mit den Platten, auf denen sich die Dateistruktur befindet, über ein Mittel wie beispielsweise einen Bus oder ein Vermittlungsnetzwerk verbunden sind, von denen jedes für diese Zwecke als ein Übertragungsnetzwerk betrachtet werden kann. Darüber hinaus gehen wir davon aus, dass die Knoten über ein ähnliches Mittel miteinander Daten austauschen. Ein Dateisystem mit gemeinsam benutzten Platten ermöglicht es, eine Berechnung, die die Dateistruktur verwendet, in mehrere Teile zu gliedern, die parallel auf mehreren Knoten ausgeführt werden können. Dadurch kann die Verarbeitungsleistung von diesen mehreren Knoten bei der Berechnung vorteilhaft zum Tragen kommen.
  • Eine Zuordnungsliste ist Teil unserer Dateistruktur. Betrachten wir eine Dateistruktur, die auf N Platten, D0, D1, DN-1, gespeichert ist. Jeder Plattenblock in der Dateistruktur wird von einem Paar (i, j) gekennzeichnet, zum Beispiel kennzeichnet (5,254) den 254. Block auf der Platte D5. Die Zuordnungsliste wird üblicherweise in einer Matrix A gespeichert, wobei der Wert des Elements A(i, j) den Zuordnungszustand (zugeordnet/frei) des Plattenblocks (i, j) angibt.
  • Die Zuordnungsliste wird üblicherweise auf der Platte als Teil der Dateistruktur gespeichert, die sich in einem oder mehreren Plattenblöcken befindet. Herkömmlicherweise ist A(i, j) das k-te sequenzielle Element in der Liste, wobei k = M + j und M eine Konstante ist, die größer als die höchste Blocknummer auf einer beliebigen Platte ist.
  • Um einen freien Block des Plattenspeichers zu finden, liest das Dateisystem einen Block A in einen Speicherpuffer und durchsucht den Pufferspeicher, um ein Element A(i, j) zu finden, dessen Wert anzeigt, dass der entsprechende Block (i, j) frei ist. Vor der Verwendung des Blocks (i, j) aktualisiert das Dateisystem den Wert von A(i, j) in dem Pufferspeicher, um anzuzeigen, dass der Block (i, j) den Zustand "zugeordnet" hat, und dann schreibt es den Inhalt des Pufferspeichers auf die Platte zurück. Um einen Block (i, j) freizugeben, der nicht mehr gebraucht wird, liest das Dateisystem den Block, der A(i, j) enthält, in einen Pufferspeicher, aktualisiert den Wert von A(i, j), um anzuzeigen, dass der Block (i, j) freigegeben ist, und schreibt den Block vom Pufferspeicher auf die Platte zurück.
  • Die Steuerung des gemeinsamen Zugriffs auf die Zuordnungsliste stellt ein besonderes Erfordernis dar. Wenn die Knoten, die ein Dateisystem mit gemeinsam benutzten Platten bilden, ihren Zugriff auf die gemeinsam benutzten Platten nicht ordnungsgemäß synchronisieren, können sie die Dateistruktur beschädigen. Dies gilt insbesondere für die Zuordnungsliste. Um dies zu veranschaulichen, betrachten wir den Prozess der Zuordnung eines freien Blocks, der vorstehend beschrieben wurde. Nehmen wir an, zwei Knoten versuchen gleichzeitig, einen Block zuzuordnen. Dabei könnten sie beide denselben Zuordnungslistenblock lesen, beide dasselbe Element (A(i, j) finden, das den freien Block (i, j) beschreibt, beide A(i, j) aktualisieren, um anzuzeigen, dass der Block (i, j) zugeordnet ist, beide den Block zurück auf Platte schreiben und beide damit beginnen, den Block (i, j) für verschiedene Zwecke zu nutzen, wodurch sie die Unversehrtheit der Dateistruktur verletzen würden. Ein subtileres, aber genauso ernstes Problem tritt selbst dann auf, wenn die Knoten gleichzeitig verschiedene Blöcke, X und Y, zuordnen, sowohl A(X) als auch A(Y) aber in demselben Zuordnungsblock enthalten sind. In diesem Fall setzt der erste Knoten A(X) auf "zugeordnet", der zweite Knoten setzt A(Y) auf "zugeordnet", und beide schreiben gleichzeitig ihre im Pufferspeicher abgelegten Kopien des Zuordnungsblocks auf Platte. In Abhängigkeit davon, welche Schreiboperation zuerst durchgeführt wird, erscheint entweder der Block X oder der Block Y in dem Abbild auf der Platte frei. Wenn zum Beispiel die Schreiboperation des zweiten Knotens nach der Schreiboperation des ersten Knotens durchgeführt wird, ist der Block X in dem Abbild auf der Platte frei. Der erste Knoten beginnt mit der Verwendung des Blocks X (zum Beispiel, um einen Datenblock einer Datei zu speichern), aber zu einem späteren Zeitpunkt könnte ein anderer Knoten den Block X zu einem anderen Zweck zuordnen, wieder mit der Folge, dass die Unversehrtheit der Dateistruktur verletzt wird.
  • Um zu verhindern, dass die Dateistruktur beschädigt wird, muss ein Knoten für jeden Bit-Zuordnungsblock eine Zeichenfolge (Token) abrufen, bevor er ihn in den Speicher lesen kann, und wenn der Knoten den Block ändert (d.h. indem er einen Block zuordnet oder freigibt), muss er den Block auf Platte schreiben, bevor er das Token freigibt. Token erhält man normalerweise von einem Verwaltungsprogramm für verteilte Token ("distributed token manager"), an das sie auch freigegeben werden, wie zum Beispiel dem in der US-Patentschrift 5 454 108 beschriebenen Sperrenverwaltungsprogramm. Der zusätzliche Aufwand für den Erhalt von Token vom Token-Verwaltungsprogramm und für das Zurückschreiben von Zuordnungsblöcken auf Platte, bevor ein auf dem Block gehaltenes Token freigegeben wird, kann die Leistungsfähigkeit eines Dateisystems mit gemeinsam benutzten Platten erheblich herabsetzen.
  • Wir gestatten es, Daten auf mehrere Platten zu verteilen, wie es in einer RAID-Umgebung der Fall ist. Das Verteilen von Daten auf mehrere Platten (Striping) ist ein Verfahren zur Speicherung von aufeinander folgenden Datenblöcken (z.B. von einer Datei) auf verschiedenen Platten. Zu den Vorteilen des Striping gehören eine hohe Leistungsfähigkeit und die Verteilung der Last. Beim Striping schreibt das Dateisystem aufeinander folgende Blöcke einer Datei auf verschiedene Platten, und zwar entsprechend einer zyklischen Umordnung (Permutation) der Plattennummern 0, ..., N-1. Bei der herkömmlich strukturierten Zuordnungsliste müssen zum Schreiben einer Datei mit N Blöcken oder einer längeren Datei N Zuordnungsblöcke (oder die gesamte Zuordnungsliste, wenn sie kleiner als N Blöcke ist) gesperrt, gesucht, aktualisiert und geschrieben werden. Dieser zusätzliche Aufwand, der hierfür notwendig ist, ist wesentlich höher als die fortlaufende Zuordnung von N Blöcken auf einer einzelnen Platte. In einem Dateisystem mit gemeinsam benutzten Platten kann überdies der Knoten, der die Datei schreibt, beträchtliche Verzögerungen erfahren, während er darauf wartet, dass andere Knoten Sperren freigeben, mit denen die benötigten Zuordnungslistenblöcke belegt sind.
  • Vor diesem Hintergrund haben wir eine Plattenzuordnungseinheit bereitgestellt, die eine segmentierte Zuordnungsliste verwendet, welche das Speichern und Verwalten einer Zuordnungsliste ermöglicht, die das Verteilen von Dateien auf mehrere Platten unterstützt, während der für das Belegen mit Sperren, für die Ein-/Ausgabe und die Suche notwendige zusätzliche Aufwand bei der Zuordnung von Blöcken so gering wie möglich gehalten wird. Im Vergleich zu der herkömmlichen Zuordnungsliste, die vorstehend beschrieben wurde, verringert unsere Plattenzuordnungseinheit die Anzahl der Zuordnungslistenblöcke erheblich, auf die bei der Zuordnung einer auf mehrere Platten verteilten Datei zugegriffen wird. In einem Dateisystem mit gemeinsam benutzten Platten verringert sie überdies die Sperren-Konflikte und die Lese- und Schreiboperationen für den Zuordnungslistenblock, wenn mehrere Knoten gleichzeitig Dateien zuordnen, die auf mehrere Platten verteilt sind.
  • Die grundlegende Idee hinter der Plattenzuordnungseinheit, die hier beschrieben wird, besteht in der Unterteilung der Zuordnungsliste in mehrere Bereiche. Wenn die Liste in K Bereiche aufgeteilt wird, steuert jeder Bereich 1/K der Blöcke auf jeder der N Platten. Das Dateisystem sperrt Bereiche und nicht einzelne Zuordnungslistenblöcke, um den Zugriff auf die Liste zu synchronisieren. Durch die Verwendung von verschiedenen Bereichen können mehrere Knoten gleichzeitig Dateien zuordnen, die auf mehrere Platten verteilt sind, ohne sich gegenseitig zu beeinträchtigen.
  • Bei Platten mit M Blöcken enthält jeder Bereich MN/K Elemente der Zuordnungsliste. Idealerweise passen diese MN/K Elemente in einen einzelnen Zuordnungslistenblock, aber wenn die Anzahl der Platten (oder die Größe einer jeden Platte) groß genug ist oder wenn die Anzahl der Bereiche klein genug ist, können Bereiche größer als Zuordnungslistenblöcke sein. Damit die Zuordnungsliste die gleiche Blockgröße wie gewöhnliche Dateien verwendet, werden Bereiche aus einem oder mehreren Segmenten gebildet, wobei jedes Segment höchstens die Größe eines Zuordnungsblocks hat und die Zuordnung von Blöcken auf einer Teilmenge der N Platten steuert. Wenn Bereiche weniger als halb so groß wie Zuordnungsblöcke sind, werden mehrere Bereiche in jeden einzelnen Zuordnungsblock gepackt.
  • Die Parameter, die den Aufbau der segmentierten Zuordnungsliste bestimmen, sind die Anzahl der Bereiche, K, sowie die Anzahl der Platten, N, und die Plattenkapazität, die als die Anzahl der Blöcke pro Platte, M, ausgedrückt wird. Die Anzahl der Bereiche sollte so gewählt werden, dass sie mindestens so groß wie die Anzahl der Dateisystemknoten ist, so dass jeder Knoten Zuordnungen von einem anderen Bereich vornehmen kann.
  • Wenn B Zuordnungslistenelemente in einen Block passen, ist die Mindestzahl der Blöcke und folglich die Mindestzahl der Segmente, die zur Speicherung eines jeden Bereichs erforderlich sind, gegeben durch
    ceil((NM/K)/B),
    da jeder Bereich 1/K der Elemente für jede Platte speichert, d.h. NM/K Elemente je Bereich. Um einen Block auf einer bestimmten Platte zuzuordnen, ist es jedoch wünschenswert, alle Elemente der Zuordnungsliste, die auf dieselbe Platte verweisen, in demselben Segment, d.h. in demselben Block der Zuordnungsliste, zu halten. Bei dieser Vorgabe kann jedes Segment Zuordnungselemente für d verschiedene Platten halten, wobei d gegeben ist durch
    d = floor(B/(/K) = floor(BK/M).
  • Es sei angemerkt, dass K so gewählt werden muss, dass es mindestens M/B ist: Andernfalls hat d den Wert null, d.h., die Elemente der Zuordnungsliste, die auf dieselbe Platte verweisen, passen nicht in einen einzelnen Block. Die Anzahl der Segmente je Bereich ist deshalb gegeben durch:
    L = ciel(N/d) = ceil(N/floor(BK/M)).
  • Wir verwenden die Notation S(p, q), um auf das q-te Segment des p-ten Bereichs der Zuordnungsliste zu verweisen, wobei p von 0 bis K-1 und q von 0 bis L-1 reicht. Die Elemente der Zuordnungsliste werden dann wie folgt Segmenten zugewiesen. Das Element A(i, j), das den Zuordnungszustand des j-ten Blocks auf der i-ten Platte angibt, wird im Segment S(p, q) gespeichert, wobei
    p j mod K
    und
    q = floor(i/d).
  • Segmente werden in aufeinander folgenden Zuordnungslistenblöcken in der folgenden Reihenfolge angeordnet:
    S(0,0), S(1,0), S(2,0), ..., S(K-1,0),
    S(0,1), S(1,1), S(2,1), ..., S(K-1,1),
    ...
    S(0,L-1), S(1,L-1), S(2,L-1), ..., S(K-1,L-1).
  • Anders ausgedrückt, das erste Segment eines jeden Bereichs wird am Anfang der Zuordnungsliste gespeichert, gefolgt von dem zweiten Segment eines jeden Bereichs und so weiter. Diese Anordnung macht es möglich, das Dateisystem zu erweitern, indem weitere Platten hinzugefügt werden, ohne dass die Zuordnungsliste komplett umgestaltet werden muss: Das Hinzufügen von weiteren Platten zu dem Dateisystem macht es erforderlich, dass mehr Elemente der Zuordnungsliste in jedem Bereich gespeichert werden, wodurch jeder Bereich gegebenenfalls um ein oder mehrere Segmente erweitert werden muss. (Die Zahl der erforderlichen Segmente wird durch Neuberechnung von L mit einem neuen Wert für N ermittelt). Die zusätzlichen Segmente werden einfach an das Ende der vorhandenen Zuordnungsliste angefügt.
  • Um aufeinander folgende Blöcke einer auf mehreren Platten verteilten Datei zuzuordnen, beschafft sich ein Knoten ein Token für einen Bereich und ordnet aufeinander folgende Blöcke entsprechend der für das Verteilen von Daten auf mehreren Platten vorgesehenen Permutation unter Verwendung von freien Blöcken in dem Bereich zu (d.h. von Blöcken, deren Zuordnungslistenelemente ihren Zustand als "frei" angeben). Vor der Freigabe des Token schreibt der Knoten den Bereich auf die Platte zurück. Wenn bei dem Versuch, einen Block auf einer bestimmten Platte zuzuordnen, festgestellt wird, dass der Bereich keinen freien Block auf dieser Platte enthält, wechselt der Knoten die Bereiche: Er schreibt den Bereich auf Platte zurück und gibt das Token frei, ruft dann ein Token für einen anderen Bereich ab und versucht, von diesem Bereich eine Zuordnung vorzunehmen. Wenn der Knoten bei dem Versuch, einen freien Block auf einer bestimmten Platte zu finden, alle Bereiche ohne Erfolg ausprobiert, kann er (in Abhängigkeit von den für das Verteilen von Daten auf mehreren Platten gültigen Regeln) entweder einen Block auf einer anderen Platte zuordnen oder eine Zustandsmeldung "nicht genügend Speicherplatz vorhanden" ("out of space") an die Anwendung zurücksenden. Im ersteren Fall, wenn alle Platten ohne Erfolg ausprobiert wurden, schickt das Dateisystem die Zustandsmeldung "nicht genügend Speicherplatz vorhanden" zurück. Als Leistungsverbesserung würde das Dateisystem üblicherweise anderen Knoten gestatten, zwischen Schreiboperationen von Dateiblöcken das Token für ihren Bereich zu "stehlen". Als Reaktion auf die Anfrage, das Token stehlen zu dürfen, schreibt der Knoten den Bereich auf die Platte und tritt das Token ab. Um die Zuordnung eines Blocks aufzuheben, führt das Dateisystem in dem Bereich, der die Zuordnungsliste enthält, welche den Block beschreibt, eine Leseoperation durch, aktualisiert seinen Zustand auf "frei" und schreibt den Bereich auf Platte zurück, bevor es das Token freigibt.
  • Zwar verringern der vorstehend beschriebene Aufbau der Zuordnungsliste und der vorstehend beschriebene Algorithmus die gegenseitige Beeinträchtigung von Knoten, die zur gleichen Zeit Dateien erstellen, beträchtlich, doch ist eine gewisse Beeinträchtigung dennoch möglich. Dies liegt daran, dass ein Knoten beim Wechseln von Bereichen über keine Informationen verfügt, auf deren Grundlage er eine Auswahl des Bereichs, in den er wechseln könnte, treffen könnte. Idealerweise sollte er in einen Bereich wechseln, der gerade nicht von einem anderen Knoten verwendet wird und der über ausreichend freie Blöcke verfügt, damit er seine Schreiboperationen ohne weitere Bereichswechsel fortsetzen kann.
  • Um ein Mittel bereitzustellen, das es einem Knoten ermöglicht, seine Bereichswahl gut informiert zu treffen, führen wir ein Zuordnungsverwaltungsprogramm (allocation manager) ein, bei dem es sich um ein Programm handelt, das überwacht, welcher Knoten (gegebenenfalls) jeden Zuordnungsbereich verwendet und wie viel freier Speicherplatz in jedem Bereich ungefähr übrig bleibt. Während der Initialisierung des Dateisystems prüft das Zuordnungsverwaltungsprogramm jeden Bereich, um die Anzahl der freien Blöcke in jedem Bereich zu zählen, und hinterlegt diese Informationen in einer Tabelle. Vor einem Bereichswechsel sendet ein Knoten des Dateisystems eine Nachricht an das Zuordnungsverwaltungsprogramm, um es über den Bereich in Kenntnis zu setzen, aus dem er wechselt (einschließlich des derzeit freien Speicherplatzes in dem Bereich), und um einen Bereich vorgeschlagen zu bekommen, in den er wechseln könnte. Das Zuordnungsveraltungsprogramm aktualisiert seine Tabelle, um den freien Speicherplatz in dem Bereich anzuzeigen, aus dem der Wechsel stattfindet, und um ihn als einen nicht mehr verwendeten Bereich auszuweisen. Anschließend prüft es seine Tabelle, um einen anderen Bereich zu ermitteln, der nicht verwendet wird und der den größten freien Speicherplatz aufweist, es antwortet dem Dateisystemknoten unter Angabe der Nummer dieses Bereichs und aktualisiert seine Tabelle, um anzuzeigen, dass der Bereich verwendet wird. Wenn alle anderen Bereiche bereits verwendet werden, wählt das Zuordnungsverwaltungsprogramm willkürlich einen Bereich aus. Dieses Protokoll verringert die Anzahl der Bereichswechsel, indem ein Wechsel in nicht verwendete Bereiche begünstigt wird.
  • Obgleich der vorstehende Algorithmus Zugriffe auf die Zuordnungsliste zum Zweck des Anlegens von Dateien begrenzt, ist es dennoch möglich, dass Löschoperationen von Dateien häufige Bereichswechsel verursachen und deshalb Knoten beeinträchtigen, die gleichzeitig Dateien erstellen. Selbst wenn die Blöcke in einzelnen Dateien auf einen einzigen Bereich begrenzt werden, kommt es dennoch häufig vor, dass ein Knoten ein paar Dateien löscht (z.B. den Inhalt eines Verzeichnisses), die von verschiedenen Knoten oder zu verschiedenen Zeitpunkten angelegt und deshalb aus verschiedenen Bereichen zugeordnet wurden. Dies führt zu einer Aufhebung der Zuordnung und verursacht folglich häufige Bereichswechsel.
  • Um diese Bereichswechsel zu verringern, stellen das Zuordnungsverwaltungsprogramm und das Dateisystem Mittel bereit, um die Aufhebung der Zuordnung des Blocks (gegebenenfalls) dem Knoten zu übertragen, der gerade den Bereich verwendet, welcher den Block steuert, der gerade freigegeben wird. Dies geht folgendermaßen vonstatten:
    Um einen Block zu löschen, sendet das Dateisystem zuerst eine Nachricht an das Zuordnungsverwaltungsprogramm, um die Kennung des Knotens zu erhalten, der den Bereich gerade verwendet. Das Zuordnungsverwaltungsprogramm antwortet unter Angabe der Kennung des Knotens oder eines Hinweises, dass der Bereich derzeit nicht verwendet wird. Im letzteren Fall hebt der Knoten die Zuordnung des Blocks auf. Im ersteren Fall sendet der Knoten eine Nachricht an denjenigen Knoten, der von dem Zuordnungsverwaltungsprogramm angegeben wird, und weist ihn an, die Zuordnung des Blocks aufzuheben. Wenn der zweite Knoten den Bereich tatsächlich verwendet, hebt er die Zuordnung des Blocks auf und teilt dies dem ersten Knoten in einer Antwort mit. Wenn der zweite Knoten den Bereich gerade nicht verwendet, antwortet er dem ersten Knoten, um ihn darüber zu informieren, woraufhin der erste Knoten die Zuordnung des Blocks aufhebt.
  • Um den Nachrichtenverkehr zu verringern, können Nachrichten über die Aufhebung der Zuordnung im Stapelbetrieb verarbeitet werden. Beim Löschen einer Datei können die Blöcke, die zu der Datei gehören, beispielsweise nach dem Zuordnungsbereich sortiert werden, und eine einzige Nachricht über die Aufhebung der Zuordnung, die Blöcke enthält, welche zu demselben Bereich gehören, kann dann an den Knoten gesendet werden, der diesen Bereich gerade verwendet.
  • Handhabung der Beeinträchtigung von Dateisystemen mit gemeinsam benutzten Platten:
  • Unser System gestattet mehreren Knoten, die ein Dateisystem mit gemeinsam benutzten Platten bilden, Speicherbereich ohne unnötige gegenseitige Beeinträchtigung gleichzeitig zuzuordnen. Verschiedene Verbesserungen wurden vorgenommen, um dies zu erreichen.
  • Dynamischer Vorababruf bei einem skalierbaren parallelen Dateisystem
  • Der Vorababruf ist ein Verfahren, das in Dateisystemen angewendet wird, um die E/A-Latenzzeit zu verringern, indem Blöcke von Dateien, auf die in Folge zugegriffen wird, im Voraus gelesen werden, d.h., bevor die Daten von Anwendungsprogrammen angefordert werden. Unser System geht das Problem der dynamischen Terminierung und Anpassung von Dateisystem-Ressourcen an, die für den Vorababruf bestimmt sind, um einen höchstmöglichen Durchsatz zu erzielen und die E/A-Latenzzeit in einem parallelen Dateisystem auf ein Mindestmaß zu verringern, d.h. in einem Dateisystem, in dem Daten für dieselbe Datei über mehrere Platteneinheiten verteilt sind.
  • In dem System gibt es einen Systemdienst, der als "Pufferspeicherverwaltungsprogramm" ("buffer manager") bezeichnet wird und über die Inanspruchnahme der Speicherressourcen durch die verschiedenen Systemkomponenten, die um den Speicher konkurrieren, entscheidet. Jede Komponente muss dem Pufferspeicherverwaltungsprogramm Informationen bereitstellen, die es benötigt, um zu entscheiden, wie viel Speicherplatz jeder Komponente zugeordnet wird. Diese Informationen bestehen aus den folgenden zwei Zahlen:
    • 1. der gewünschten Speichergröße. Diese Zahl gibt an, wie viel Speicherplatz eine Komponente tatsächlich nutzen könnte, sofern er verfügbar wäre.
    • 2. dem aktuellen Auslastungsgrad. Diese Zahl muss ein Maß für die Häufigkeit der Speicherbelegung einer Komponente angeben, wobei dies üblicherweise in Form des Speichervolumens ausgedrückt wird, auf das je Zeiteinheit zugegriffen wird.
  • Das Pufferspeicherverwaltungsprogramm wiederum informiert jede Komponente über das Speichervolumen, das es zur Verwendung durch diese Komponente zugeordnet hat.
  • Eine der Komponenten, die um Ressourcen konkurrieren, ist die Pufferspeichergruppe des Dateisystems, die zur Zwischenspeicherung von Dateidaten, auf die unlängst zugegriffen wurde, und von Daten, die für aufeinanderfolgende Leser vorab abgerufen wurden, verwendet wird. Wir stellen dem Pufferspeicherverwaltungsprogramm geeignete Informationen bereit, damit es für den Vorababruf benötigte Ressourcen berücksichtigen kann, und terminieren die von dem Pufferspeicherverwaltungsprogramm zugewiesenen Ressourcen, um einen höchstmöglichen Durchsatz des Dateisystems zu erzielen und die E/A-Latenzzeit so gering wie möglich zu halten.
  • Das Folgende gibt einen Überblick darüber, wie dies realisiert wird. Weitere Einzelheiten sind in den Tabellen 3 und 4 aufgeführt und werden im Anschluss an diese Übersicht ausführlicher erklärt.
    • – Die Pufferspeichergruppe des Dateisystems wird logisch in zwei Teile aufgeteilt, wobei einer für den Vorababruf ("Vorababruf-Gruppe") und einer für die Zwischenspeicherung von Dateiblöcken, auf die unlängst zugegriffen wurde ("allgemeine Gruppe"), verwendet wird. Mit "logisch aufgeteilt" meinen wir, dass einzelne Pufferspeicher nicht ausdrücklich der einen Gruppe oder der anderen Gruppe zugewiesen werden müssen; vielmehr stellt sich diese Aufteilung in der Form dar, dass eine einzelne Zahl verwaltet wird, die anzeigt, wie viel von dem gesamten Pufferspeicherplatz für den Vorababruf verwendet werden soll.
    • – Diese beiden Gruppen stellen sich dem Pufferspeicherverwaltungsprogramm als zwei getrennte Komponenten dar, d.h., das Dateisystem berechnet getrennte gewünschte Speichergrößen und den Auslastungsgrad für die allgemeine Gruppe und für die Vorababruf-Gruppe.
    • – Der Auslastungsgrad von beiden Gruppen wird mit Hilfe von herkömmlichen Verfahren wie zum Beispiel der Anzahl der Referenzen berechnet, die die Datenzugriffsraten messen. Da die beiden Gruppen nur logisch getrennt sind, geschieht dies, indem für jede Gruppe getrennte Zählstände erfasst werden; bei jedem Zugriff auf den Pufferspeicher wird der entsprechende Zählstand auf der Grundlage der Feststellung aktualisiert, ob der Zugriff auf den Pufferspeicher mittels sequenzieller oder zufälliger Ein-/Ausgabe stattfindet.
    • – Die gewünschte Größe der allgemeinen Gruppe wird berechnet, indem unter Verwendung von Referenzbits und Zählern Arbeitsmengen gemessen werden, um die gesamte Menge an bestimmten Dateidaten zu ermitteln, auf die über einen gewissen Zeitraum zugegriffen wurde.
    • – Die gewünschte Größe der Vorababruf-Gruppe wird jedoch anders berechnet. Bei dieser Berechnung werden die Anzahl und das Leistungsvermögen der Platteneinheiten berücksichtigt, die zu dem Dateisystem gehören, sowie die Anzahl der Dateien, auf die in Folge zugegriffen wird, und die Geschwindigkeit, mit der die Daten gelesen werden. Diese Berechnung wird nachstehend näher erklärt und ist in Tabelle 3 ausführlich beschrieben.
    • – Die in dem vorherigen Schritt berechnete jeweilige Anzahl wird dem Pufferspeicherverwaltungsprogramm bereitgestellt, das sie zur Ermittlung des Speichervolumens verwendet, das den beiden Komponenten zuzuweisen ist, die die allgemeine Gruppe und die Vorababruf-Gruppe des Dateisystems darstellen. Das Dateisystem legt die Gesamtgröße seiner Pufferspeichergruppe als die Summe des Speichervolumens fest, das diesen beiden Komponenten zugewiesen wird. Mittels des Speichervolumens, das der Komponente zugewiesen wird, die die Vorababruf-Gruppe darstellt, wird die vorab abzurufende Datenmenge ermittelt.
  • Die in den Tabellen 3 und 4 gezeigten Algorithmen lassen sich am besten erklären, indem mit einem einfachen Beispiel einer einzigen Anwendung begonnen wird, die Daten aus einer Datei liest, die auf einem nichtparallelen (nur über eine Platte verfügenden) Dateisystem gespeichert ist; anschließend prüfen wir, wie mit mehreren Anwendungen und Dateisystemen mit mehreren Platten verfahren wird.
  • In dem einfachen Beispiel reicht eine Doppelpufferung (zwei Vorababruf-Pufferspeicher) aus, um einen optimalen Durchsatz und eine optimale Leistung zu erreichen. Wenn die Anwendung mit dem Lesen der Datei beginnt, liest das Dateisystem den ersten Block der Datei in einen der Vorababruf-Pufferspeicher. Sobald die erste E/A-Operation abgeschlossen wird, liest das Dateisystem den zweiten Block der Datei in den anderen Vorababruf-Pufferspeicher. Während die zweite E/A-Operation in Ausführung befindlich ist, werden Leseanforderungen von der Anwendung erfüllt, indem Dateidaten aus dem ersten Pufferspeicher abgerufen werden. Wenn das Ende des ersten Pufferspeichers erreicht ist, können nachfolgende Leseanforderungen aus dem zweiten Pufferspeicher erfüllt werden, sobald die zweite E/A-Operation abgearbeitet ist. Sobald die zweite E/A-Operation abgeschlossen ist und die Anwendung das letzte Byte von dem ersten Block gelesen hat, wird der erste Vorababruf-Pufferspeicher erneut verwendet, um den dritten Block der Datei vorab abzurufen, und so weiter.
  • Wenn die Lesegeschwindigkeit der Anwendung geringer als die der Platte ist, sind Vorababruf-E/A-Operationen abgeschlossen, bevor die Anwendung mit dem Lesen der Daten in dem vorherigen Block fertig ist. In diesem Fall wird die nächste Vorababruf-E/A-Operation gestartet, sobald die Anwendung das letzte Byte des vorherigen Pufferspeichers gelesen hat. In diesem Fall werden die Daten mit derselben Geschwindigkeit geliefert, mit der die Anwendung sie liest, und die Anwendung muss nie auf eine Platten-E/A-Operation warten. Dies ist optimal. Wenn die Anwendung die Daten schneller liest als sie von der Platte abgerufen werden können, muss sie jedes Mal, wenn sie das Ende von einem Block erreicht, warten, bis die gerade aktive E/A-Operation abgeschlossen ist, und eine neue Vorababruf-E/A-Operation wird gestartet, sobald die vorherige endet. In diesem Fall werden die Daten so schnell gelesen, wie sie von der Platte abgerufen werden können, was ebenfalls optimal ist.
  • Der in Tabelle 3 gezeigte Algorithmus verallgemeinert dieses Verhalten auf mehrere Anwendungsprogramme und mehrere Platten pro Dateisystem; er berechnet die Anzahl der Vorababruf-Pufferspeicher, die benötigt werden, so dass: (1) Wenn die gesamte Datenrate, mit der alle Anwendungsprogramme versuchen, Daten zu lesen, geringer als die gesamte verfügbare Bandbreite der Platte ist, werden die Daten einer jeden Anwendung so schnell zur Verfügung gestellt, wie diese sie liest, ohne E/A-Wartezeiten. (2) Wenn die gesamte Datenrate der Anwendungsprogramme höher als die gesamte verfügbare Bandbreite der Platte ist, werden die Daten so schnell gelesen, wie sie von der Platte abgerufen werden können.
  • In beiden Fällen ist es erforderlich, die Geschwindigkeit zu ermitteln, mit der jedes Anwendungsprogramm versucht, Daten zu lesen. Dies geschieht, indem die Bedenkzeit ("think time") der Anwendung gemessen wird, d.h. die Zeit, die die Anwendung mit der Verarbeitung der von dem Dateisystem gelieferten Daten verbringt. Die Bedenkzeit beinhaltet den zusätzlichen Zeitaufwand beim Leseaufruf des Systems, der erforderlich ist, um auf Daten in der Pufferspeichergruppe des Dateisystems zuzugreifen und sie in den Pufferspeicher der Anwendung zu kopieren, beinhaltet aber nicht die Zeit, die in dem Dateisystem mit dem Warten auf Daten verbracht wird, die von der Platte gelesen werden sollen. Wir definieren die "Datenverarbeitungsrate" ("data consumption rate") der Anwendung über ein bestimmtes Zeitintervall als die Datenmenge, die von der Anwendung während des Intervalls gelesen wird, geteilt durch die gesamte Bedenkzeit in diesem Intervall.
  • Betrachten wir zuerst den Fall, in dem die gesamte Verarbeitungsrate geringer als die gesamte Bandbreite der Platte ist. In diesem Fall sollte ein ordnungsgemäßer Vorababruf in der Lage sein, die gewünschten Daten zu liefern, ohne dass eine der Anwendungen je auf eine E/A-Operation warten muss. Wenn die gesamte Verarbeitungsrate höher als die Bandbreite einer einzigen Platte ist, sind parallele Vorababruf-E/A-Operationen auf mehreren Platten erforderlich, um die gewünschte Datenrate zu halten. Die Mindestanzahl der parallelen E/A-Operationen, die erforderlich sind, lässt sich berechnen, indem man die gesamte Verarbeitungsrate durch die Bandbreite einer einzigen Platte teilt und das Ergebnis auf die nächste ganze Zahl aufrundet. Wir bezeichnen diese Zahl als den "Parallelitätsfaktor". Um die gewünschten Daten zu liefern, ohne dass eines der Anwendungsprogramme auf eine E/A-Operation warten muss, müssen genügend zusätzliche Pufferspeicher zur Verfügung stehen, so dass jedes Anwendungsprogramm zuvor abgerufene Daten aus einem anderen Pufferspeicher lesen kann, während Vorababruf-E/A-Operationen im Gang sind. Die optimale Anzahl an Pufferspeichern für einen Vorababruf ist daher durch Addition der Anzahl der Dateiinstanzen, die für in Folge stattfindende E/A-Operationen geöffnet sind, zum Parallelitätsfaktor gegeben. Während ein Anwendungsprogramm die letzten Daten aus einem zuvor abgerufenen Block liest, wird dieser Pufferspeicher verfügbar, um die nächste Vorababruf-E/A-Operation durchzuführen. Wie in dem Algorithmus in der Tabelle 4 gezeigt ist, wird dieser Pufferspeicher dann verwendet, um den nächsten Datenblock für die Anwendung vorab abzurufen, die dem Ende des Pufferspeichers, aus dem sie gerade Daten liest, am nächsten ist. Mit "dem Ende eines Pufferspeichers am nächsten gelegene Anwendung" meinen wir die Anwendung, die entsprechend ihrer aktuellen Datenverarbeitungsrate am frühesten Daten aus dem nächsten Block anfordert.
  • Verwendet man die optimale Anzahl von Vorababruf-Pufferspeichern, braucht keine Anwendung auf eine E/A-Operation zu warten, vorausgesetzt, dass sie Daten nie vor dem Zeitpunkt liest, der auf der Grundlage der gemessenen Datenverarbeitungsrate vorhergesagt wurde. Wenn die tatsächlichen Verarbeitungsraten nicht konstant sind, kann die Anzahl der Vorababruf-Pufferspeicher erhöht werden, um Schwankungen bei den Verarbeitungsraten Rechnung zu tragen. Dies geschieht, indem nicht nur die durchschnittlichen Bedenkzeiten gemessen werden, sondern auch die Abweichung der Bedenkzeit bei jeder Anwendung. Damit wird dann eine "abweichungsangepasste Verarbeitungsrate" ("variance adjusted consumption rate") berechnet, d.h. eine Rate, bei der nahezu alle Leseanforderungen (z.B. 90% oder 95% aller Anforderungen) nicht vor dem Zeitpunkt ankommen, der auf der Grundlage der abweichungsangepassten Verarbeitungsrate vorhergesagt wurde. Mit dieser abweichungsangepassten Verarbeitungsrate wird dann der Parallelitätsfaktor anstelle der durchschnittlichen Verarbeitungsrate berechnet.
  • Betrachten wir nun den Fall, in dem die gesamte Verarbeitungsrate aller Anwendungen die gesamte Plattenbandbreite des Dateisystems überschreitet. In diesem Fall ist der Parallelitätsfaktor, der wie vorstehend beschrieben berechnet wurde, eine Zahl, die größer als die Anzahl der Platten ist, die dem Dateisystem zur Verfügung stehen. Da es nicht möglich ist, eine höhere Anzahl von gleichzeitigen E/A-Operationen zu starten, als es Platten gibt, macht es keinen Sinn, mehr Pufferspeicher für Vorababruf-E/A-Operationen zuzuordnen, als es Platten gibt. Die gewünschte Anzahl von Vorababruf-Pufferspeichern wird daher als die Anzahl der Dateiinstanzen berechnet, die für in Folge stattfindende E/A-Operationen geöffnet sind, zuzüglich der Anzahl der Platten oder des Parallelitätsfaktors, je nachdem, welcher Wert kleiner ist. Wenn die Verarbeitungsrate die gesamte Bandbreite der Platte überschreitet, ist diese Anzahl von Vorababruf-Pufferspeichern ausreichend, um weiterhin alle Platten zu beschäftigen, d.h., um eine neue Vorababruf-E/A-Operation zu starten, sobald die vorherige E/A-Operationen auf einer Platte abgeschlossen ist. Folglich werden Daten so schnell zur Verfügung gestellt, wie sie von der Platte abgerufen werden können.
  • Schließlich beschreiben wir zwei Verfeinerungen an der vorstehend beschriebenen Berechnung, die den Eigenschaften des E/A-Teilsystems Rechnung tragen, mit dem die Platten des Dateisystems verbunden sind. Die erste gilt für Systeme, in denen es zu einer erheblichen Verzögerung zwischen dem Zeitpunkt kommt, zu dem eine E/A-Anforderung dem Einheitentreiber übergeben wird, und dem Zeitpunkt, zu dem die tatsächliche E/A-Operation gestartet wird. Eine solche Verzögerung tritt beispielsweise bei Platten auf, die mit einem Netzwerk verbunden sind (z.B. VSD-Platten), bei denen eine E/A-Anforderung durch das Netzwerk geleitet werden muss, bevor sie die Platte erreicht. Um einen höchstmöglichen Plattendurchsatz zu erreichen, muss die nächste an eine Platte gerichtete E/A-Anforderung an den Einheitentreiber ausgegeben werden, bevor die vorherige E/A-Operation abgeschlossen ist. Dazu muss ein Vorababruf-Pufferspeicher, um die nächste E/A-Operation zu starten, zu einem früheren Zeitpunkt als üblich verfügbar sein. Folglich muss die Anzahl der Pufferspeicher, die für Vorababruf-E/A-Operationen bestimmt sind, um einen Faktor von (1 + Epsilon) größer als die Anzahl der Platten sein, wobei Epsilon durch das Verhältnis der durchschnittlichen E/A-Anforderungsverzögerung und der durchschnittlichen Platten-E/A-Zeit gegeben ist.
  • Die zweite Verfeinerung an der Berechnung des Pufferspeichers berücksichtigt Beschränkungen der Komponenten des E/A-Teilsystems wie zum Beispiel von Plattensteuereinheiten und des E/A-Busses. Wenn das Dateisystem über eine hohe Anzahl von Platten verfügt, kann das Addieren von Plattenbandbreite eine Zahl ergeben, die größer als der gesamte E/A-Durchsatz der Platte ist, den das System unterstützen kann. Wenn dies der Fall ist, braucht die Anzahl der Vorababruf-Pufferspeicher, die für Vorababruf-E/A-Operationen bestimmt sind, nicht so groß wie die Anzahl der Platten zu sein. Stattdessen reicht eine Anzahl von Pufferspeichern, die gleich dem gesamten E/A-Durchsatz, geteilt durch die Bandbreite einer einzelnen Platte, ist, aus, um so viele Platten-E/A-Operationen parallel zu starten, wie das System tatsächlich unterstützen kann. Der gesamte E/A-Durchsatz der Platte kann entweder anhand der Hardware-Spezifikationen ermittelt werden, indem speziell der Durchsatz zum Zeitpunkt der Installation des Dateisystems gemessen wird, oder indem der höchstmögliche Durchsatz, der während der Ausführung des Dateisystems je beobachtet wurde, erfasst wird.
  • Beide Verfeinerungen, die vorstehend beschrieben wurden, können durch Berechnung einer "wirksamen Anzahl von Platten" ausgedrückt werden, die dann anstelle der tatsächlichen Anzahl von Platten in den Berechnungen für den Vorababruf-Pufferspeicher verwendet wird, wie in Tabelle 3 gezeigt ist.
  • TABELLE 3
  • Berechnen der gewünschten Größe der Vorababruf-Gruppe
    • 1. Berechne die wirksame Anzahl der Platten als n_eff = MIN(ceil((1 + L_start/L_io)*n_disks), ceil(T_sys/T_disk)), wobei n_disks = Anzahl der Platten, die dem Dateisystem zur Verfügung stehen L_io = durchschnittliche E/A-Latenzzeit, um einen Block von der Platte zu lesen L_start = durchschnittliche E/A-Anfangslatenzzeit T_sys = höchstmöglicher gesamter E/A-Durchsatz des Platten-Teilsystems T_disk = durchschnittlicher E/A-Durchsatz einer einzelnen Platte
    • 2. Bei jeder geöffneten Dateiinstanz i, auf die in Folge zugegriffen wird. Berechne eine angepasste Verarbeitungsrate c_i, so dass ein Teil f (z.B. 90%) aller Anforderungen für den nächsten Datenblock nicht früher als zu dem Zeitpunkt ankommen, der von der angepassten Verarbeitungsrate vorhergesagt wird, d.h. in Intervallen, deren Länge durch die Blockgröße des Dateisystems, geteilt durch c_i, gegeben ist. Dies kann statistisch berechnet werden, indem man die durchschnittliche Verarbeitungsrate und Abweichung für die Instanz berechnet. Berechne die gesamte angepasste Verarbeitung als die Summe der angepassten Verarbeitungsraten von allen aufeinander folgenden geöffneten Dateiinstanzen: c_total = sum c_i, for i = 1 ... n_inst wobei n_inst = Anzahl der geöffneten Dateiinstanzen, auf die in Folge zugegriffen wird.
    • Berechne den gewünschten Vorababruf-Parallelitätsfaktor als n_para = c_total/T_disk
    • 3. Die gewünschte Anzahl der Vorababruf-Pufferspeicher wird dann wie folgt berechnet, wobei die in den Schritten 1 und 2 berechneten Werte verwendet werden: n_bufs_desired = MIN(n_para, n_eff) + n_inst
  • TABELLE 4
  • Terminierung einer Vorababruf-E/A-Operation
  • Die Eingabe in diese Prozedur ist die tatsächliche Anzahl der Vorababruf-Pufferspeicher, n_bufs_assigned, die von dem Pufferspeicherverwaltungsprogramm auf der Grundlage der gewünschten Anzahl der Pufferspeicher, n_bufs_desired, die wie in Tabelle 3 gezeigt berechnet wurde, zugewiesen wurde.
  • Der Algorithmus verwaltet zwei globale Zähler: n_io_total ist die Anzahl der Vorababruf-E/A-Operationen, die gerade in Ausführung befindlich sind (oder die dem Einheitentreiber übergeben wurden), und n_prefetched ist die Anzahl der Pufferspeicher, die vorab abgerufene Blöcke halten, die noch nicht von der Anwendung gelesen wurde, für die der Block vorab abgerufen wurde. Die Summe dieser beiden Zahlen ist die Anzahl der Pufferspeicher, die für den Vorababruf gerade verwendet werden.
  • Weiterhin überwacht der Algorithmus für jede geöffnete Instanz i, auf die in Folge zugegriffen wird, den vorhergesagten Zeitpunkt, zu dem die Anwendung auf den nächsten Block zugreifen wird, für den noch keine Vorababruf-E/A-Operation gestartet worden ist. Wir bezeichnen diese Zahl mit t_next[i].
    • 1. Setze n_io_total und n_prefetched auf den Anfangswert null. Setze für jede geöffnete Dateiinstanz i, auf die in Folge zugegriffen wird, n_io[i] auf den Anfangswert null, und lege t_next[i] als den Zeitpunkt fest, zu dem die Anwendung auf der Grundlage der angepassten Verarbeitungsrate c_i den nächsten Datenblock anfordert. Erstelle eine geordnete Instanzenliste, indem alle geöffneten Instanzen, auf die in Folge zugegriffen wird, nach t_next[i], kleinster Wert zuerst, sortiert werden.
    • 2. Wenn n_io_total + n_prefetched größer als oder gleich n_bufs_assigned ist, gehe zum Schritt 4; fahre ansonsten mit dem nächsten Schritt fort.
    • 3. Übergib die nächste Vorababruf-E/A-Anforderung für die erste Instanz i in der geordneten Instanzenliste (dies ist die Instanz mit dem kleinsten Wert von t_next[i]. Aktualisiere t_next[i] so, dass er der vorhergesagte Zeitpunkt ist, zu dem die Anwendung den nächsten Datenblock nach dem Datenblock anfordert, für den die Vorababruf-E/A-Operation soeben gestartet worden ist. Ordne diese Instanz in der geordneten Instanzenliste aller Instanzen entsprechend ihrem neuen Wert von t_next[i] neu ein. Erhöhe n_io_total. Gehe zum Schritt 2 zurück.
    • 4. Warte, bis eines der folgenden Ereignisse stattfindet: a) Eine Vorababruf-E/A-Operation wird ausgeführt: Verringere n_io_total, und erhöhe n_prefetched. Gehe zum Anfang von Schritt 4 zurück (warte auf das nächste Ereignis). b) Eine Leseoperation erreicht das Ende eines Blocks, der vorab abgerufen worden war: Da die Leseoperation die Daten aus dem Vorababruf-Pufferspeicher in den Adressraum der Anwendung kopiert, steht dieser Pufferspeicher nun für eine weitere Vorababruf-Operation zur Verfügung. Verringere n_prefetched, und gehe zum Schritt 2 zurück. c) Das Pufferspeicherverwaltungsprogramm hat die Anzahl der Pufferspeicher geändert, die der Vorababruf-Gruppe zugeordnet worden waren (n_bufs_assigned): Gehe zum Schritt 2 zurück. d) Eine geöffnete Instanz i wird geschlossen. Entferne die Instanz aus der Liste mit den geordneten Instanzen. Verringere n_prefetched um die Anzahl der Pufferspeicher, die für diese Instanz vorab abgerufen wurden. Gehe zum Schritt 2 zurück.
  • Verwaltung des Pufferspeichers bei verbesserter Leistungsfähigkeit des Cachespeichers
  • Unser paralleles Dateisystem wurde zum Einsatz auf Rechnern von IBM entwickelt, bei denen die Leistungsfähigkeit ein entscheidender Faktor ist. Einer der Aspekte, der sich auf die Leistungsfähigkeit auswirken kann, ist die Auslastung des Cachespeichers des Dateisystems. Das Problem besteht darin, dass dem System Anforderungen für Cachespeicherplatz unterschiedlicher Größe übergeben werden und dies auf nicht vorhersagbare Weise geschieht. Wir haben ein Cachespeicher-Verwaltungsschema realisiert, bei dem wir das aktuelle Belegungsmuster in dem System ermitteln, das Verhalten des Cachespeichers entsprechend anpassen und auf diese Weise sowohl die Leistungsfähigkeit als auch die Speicherauslastung verbessern. Im Allgemeinen verbessern wir die Leistungsfähigkeit des Cachespeichers, die Speicherauslastung und die Verteilung durch unsere Analyse des Belegungsmusters.
  • Die Wirksamkeit unseres Cachespeicher-Belegungs- und -Ersetzungsschemas wird enorm verbessert, da unser System die Art der Arbeitslast erkennt, unter der es gerade arbeitet, und wir das Verhalten des Cachespeichers entsprechend darauf abstimmen. Die beiden Arten der Arbeitslast, die von dem vorgeschlagenen Schema erkannt werden und auf die es reagiert, sind die sequenzielle Arbeitslast und die zufällige Arbeitslast. Das Grundprinzip hinter dieser Trennung ergibt sich aus der unterschiedlichen Definition der Arbeitsmengengröße zwischen den beiden Arbeitslasten. Das zukünftige Verhalten wird vorhergesagt, indem der aktuelle Zustand ausgewertet wird. Sobald das aktuelle Belegungsmuster in dem System festgestellt worden ist und davon ausgegangen wird, dass es verhältnismäßig stabil ist, wird der Cachespeicher veranlasst, entsprechend zu reagieren.
  • Der gesamte Cachespeicher wird in verschiedene Arbeitseinheiten aufgeteilt, von denen jede einen Teil des gesamten Cachespeicherbereichs steuert und für Pufferspeicher unterschiedlicher Größe verantwortlich ist. Jede Arbeitseinheit besteht aus zwei Teileinheiten, die die beiden Arten der Arbeitslast, mit denen das System arbeitet, überwachen. Die Menge der verschiedenen Arbeitseinheiten und die Größen der Pufferspeicher, für die sie verantwortlich sind, ändern sich dynamisch. Das Cachespeicher-Verwaltungsprogramm erkennt in jedem Moment die jeweilige Größe des Pufferspeichers, für die es mit hoher Wahrscheinlichkeit einen hohen Bedarf geben wird, und legt die Arbeitseinheiten entsprechend fest. Es ist immer eine weitere Arbeitseinheit vorhanden, die sich um eingehende Anforderungen für Pufferspeicherkapazitäten kümmert, welche sich von der festen Größe aller anderen Arbeitseinheiten unterscheiden. Dies verbessert die Antwortzeit des Cachespeichers dadurch, dass eingehende Anforderungen direkt an den Teil des Cachespeichers verwiesen werden, der Pufferspeicher der gewünschten Größe beherbergt. Dieser Aspekt trägt dazu bei, das Problem der Fragmentierung des Cachespeichers zu mildern, indem das Problem auf eine Arbeitseinheit beschränkt wird und zusätzliche Maßnahmen ergriffen werden, wie zum Beispiel, dass nur dort Zusammenführungen und Neuzuordnungen vorgenommen werden. Statistiken zur Belegung werden für jede Teileinheit einer jeden Arbeitseinheit ständig aktualisiert.
  • Die erfassten Belegungsstatistiken werden in regelmäßigen Abständen geprüft. In der Folge wird der Speicherplatz des Cachespeichers unter den verschiedenen Arbeitseinheiten neu aufgeteilt. Da unser System zukünftige Belegungsmuster vorhersagt, indem es aktuelle Belegungsmuster auswertet, wirkt sich die Neuaufteilung des Speicherbereichs nicht sofort aus, sondern wird vielmehr erst wirksam, wenn Bedarf besteht. Jede Arbeitseinheit hat zwei Arten von Bereichsgrenzen, nämlich eine interne und eine externe. Die interne Bereichsgrenze trennt die beiden Teilarbeitseinheiten. Die externe Bereichsgrenze wird in zwei weitere Grenzarten unterteilt, nämlich in eine physische Grenze und in eine virtuelle Grenze. Die physische Grenze stellt die tatsächliche Speicherkapazität dar, die von dem Verteilungsschema des Belegungsmusters gesteuert wird, das zu der einzelnen Arbeitseinheit gehört. Die virtuelle Grenze ist die Grenze, die von dem Vorhersageprozess der Belegungsmusterauswertung als die physische Grenze projiziert wird, welche von dieser Arbeitseinheit versuchsweise erreicht werden sollte. Die virtuelle Grenze dient zur Folgerung, ob sich die physische Grenze einer bestimmten Arbeitseinheit ausdehnen darf oder ob die Arbeitseinheit gezwungen ist, einen Teil des von ihr gesteuerten Speicherbereichs nach einer Anforderung von einer Arbeitseinheit, die sich vergrößern darf, aufzugeben, d.h., ob sie sich im Grunde verkleinern darf.
  • Der Prozess der Festlegung von neuen virtuellen Grenzen funktioniert wie folgt. Die Statistiken der Teilarbeitseinheiten werden ausgewertet und zur Ableitung des Belegungsmusters und des Auslastungsgrades herangezogen, die den für ihre Erfordernisse optimalen Speicherbereich bestimmen. Jede Teilarbeitseinheit versucht, die Speicherkapazität zu erhalten, die sie als die für ihre Erfordernisse optimale Speicherkapazität ermittelt hat (ihre Arbeitsmengengröße). Der relative Auslastungsgrad der Teilarbeitseinheit stellt eine Obergrenze für das benötigte Speicherbereichsoptimum dar.
  • Der Erwerb von neuem Speicherbereich wird von einem Schema geregelt, bei dem sich physische und virtuelle Grenzen innerhalb einer jeden Arbeitseinheit wie folgt gegenseitig beeinflussen. Wenn eine Anforderung für einen neuen Pufferspeicher eintrifft, wird sie von der Arbeitseinheit bedient, die die angeforderte Größe steuert. Wenn es in der Arbeitseinheit einen freien Pufferspeicher oder einen Pufferspeicher gibt, der sehr leicht und schnell zu bekommen ist, wird er zur Erfüllung der eintreffenden Anforderung verwendet. Die Arbeitseinheit fährt dann mit einem Vergleich ihrer physischen Grenze mit ihrer virtuellen Grenze fort. Wenn die physische Grenze nicht kleiner als die virtuelle Grenze ist, fährt die Arbeitseinheit damit fort, den Speicherbereich zu suchen, der am einfachsten zu bekommen ist und den sie bereits steuert. Andernfalls sucht die aktuelle Arbeitseinheit die Arbeitseinheit, die sich am meisten verkleinern darf, und richtet eine Speicherbereich-Erwerbsanforderung an sie. Die Empfangsarbeitseinheit sucht den am einfachsten zu bekommenden Speicherbereich, der von ihr gesteuert wird, und gibt die Steuerung dieses Speicherbereichs ab. Die ursprüngliche Arbeitseinheit übernimmt dann die Kontrolle über den neuen Speicherbereich und verwendet ihn, um die eintreffende Anforderung zu erfüllen.
  • Die Häufigkeit, mit der der Belegungsmuster-Erkennungsprozess ausgeführt wird, kann sich gegebenenfalls entscheidend auf die Wirksamkeit des ganzen Schemas auswirken. Wenn der Prozess zu häufig ausgeführt wird, könnte er zu heftig auf sehr kurze Aktivitätsspitzen in einer bestimmten Teilarbeitseinheit reagieren. Wenn dieser Prozess andererseits in großen Zeitabständen ausgeführt wird, büßt er im Laufe der Zeit an Wirksamkeit und Genauigkeit ein. Folglich bestimmt der Prozess jedes Mal, wenn er ausgeführt wird, den Zeitpunkt, zu dem er das nächste Mal ausgeführt werden sollte. Diese Berechnung beruht auf dem Zeitpunkt, zu dem alle Arbeitseinheiten erwartungsgemäß auf den gesamten von ihnen gesteuerten Speicherbereich zugreifen. Dieser Zeitraum wird einer vorher festgelegten Ober- und Untergrenze unterworfen. Dieses Intervall ermöglicht dem Belegungsmuster-Erkennungsprozess, die aktuelle Verteilung der Arbeitslast abzuleiten, ohne von einem einzelnen belastenden Ereignis beeinflusst zu sein. Die Arbeitsmenge von Clients mit zufälligen Arbeitslasten kann ebenso wie der Speicherbereich, der für das Vorauslesen von Clients mit sequenziellen Arbeitslasten benötigt wird, abgeleitet werden.
  • Dieses Schema schließt eine erweiterte Leistungsfähigkeit und Belegung von verfügbarem Cachespeicherbereich in einer Mehrzweckumgebung ein.
  • Diejenigen, die mit den bisherigen Möglichkeiten der Verwaltung eines Cachespeichers eines Dateisystems vertraut sind, werden nun verstehen, wie unser Verfahren zur Optimierung der Auslastung eines Cachespeichers durch das Erkennen von Belegungsmustern eine Verbesserung gegenüber der bisherigen Behandlung darstellt, bei der der Cachespeicher als eine einzelne Arbeitseinheit betrachtet wurde und bei der eintreffende Anforderungen nach der LRU-Methode erfüllt wurden.
  • Wenn wir die Art der eintreffenden Anforderungen vorhersehen und uns darauf vorbereiten, wird jede eintreffende Anforderung an den Cachespeicherbereich geleitet, bei dem die Wahrscheinlichkeit, dass er verwendet wird, um die Anforderung zu erfüllen, hoch ist. Überdies kennen wir das Speichervolumen, das für jede Arbeitslast in jeder Arbeitseinheit vorgesehen werden kann, und können folglich andere Systemaktionen (z.B. die Vorababrufrate) entsprechend darauf abstimmen.
  • Erweiterte Dateiattribute zur Unterstützung der Zugriffssteuerungslisten
  • Wie wir bereits erwähnt haben, sind wir zu dem Schluss gekommen, dass es wünschenswert wäre, Zugriffssteuerungslisten für unser Dateisystem mit gemeinsam benutzten Platten zur parallelen Ausführung durch verschiedene Rechner in der Umgebung bereitzustellen. Dazuhaben wir erweiterte Dateiattribute der in der Unix-Umgebung bekannten Art zur wirksamen Unterstützung von Zugriffssteuerungslisten vorgesehen.
  • Erweiterte Attribute ermöglichen auch die Zuordnung von Informationen veränderlicher Länge zu einer Datei, auf die getrennt von den Daten zugegriffen werden kann, die in der Datei selbst gespeichert sind. Eine Verwendungsmöglichkeit von erweiterten Attributen ist die Speicherung von Zugriffssteuerungslisten (access control lists oder kurz "ACLs"), mit denen gesteuert wird, welche Benutzer oder Benutzergruppen in welcher Weise auf eine Datei zugreifen dürfen (Lesezugriff, Schreibzugriff usw.). ACLs stellen Anforderungen an die Ausführungsart eines erweiterten Attributs und sie unterscheiden sich von vielen anderen Verwendungsmöglichkeiten von erweiterten Attributen: Da alle Operationen des Dateisystems, die die Zugriffsberechtigung prüfen, auf die ACL der Datei zugreifen müssen, ist ein schneller und wirksamer Zugriff auf die ACL-Daten entscheidend für die Leistungsfähigkeit des Dateisystems. Andererseits sind ACLs üblicherweise kurz, sie ändern sich nicht häufig, und selbst wenn jede Datei eine ACL hat, sind viele dieser ACLs gleich, d.h., es gibt gewöhnlich wesentlich weniger unterschiedliche ACL-Werte, als es Dateien gibt. Wir beschreiben nun, wie erweiterte Attribute in einer Weise realisiert werden, die sich die Nutzungsmerkmale von ACLs zunutze macht und einen platzsparenden Attributspeicher bereitstellt, der einen schnellen Zugriff auf die Attributdaten ermöglicht. Außerdem unterstützt diese Ausführungsart sehr wirksam die Attributvererbung. Sie ist besonders zur Realisierung von POSIX-ACLs geeignet.
  • Im Wesentlichen verwendet unsere Ausführungsart der erweiterten Attribute in dieser Erfindung die folgenden Komponenten:
    • – die Attributdatei (kurz "AttrFile"). Dies ist eine spezielle Datei, die alle Attributdaten speichert. Sie besteht aus einer Folge von Einträgen; jeder Eintrag ist von einer der folgenden beiden Arten: ein Attributeintrag, der den Wert eines bestimmten Attributs enthält, oder ein Eintrag zu freiem Speicherbereich, der freien Speicherbereich in der Attributdatei kennzeichnet, d.h. Speicherbereich, der das nächste Mal wiederverwendet werden kann, wenn ein neuer Attributeintrag zur Attributdatei AttrFile hinzugefügt werden muss. Beide Arten von Einträgen haben eine veränderliche Länge, sind aber auf geeignete Grenzen ausgerichtet (z.B. ein Vielfaches von 8 oder 16 Byte), um die Fragmentierung zu verringern. Die Wahl einer bestimmten Ausrichtungsgröße hängt von der Mindestgröße und der durchschnittlichen Größe der Attributeinträge ab.
    • – Attributverweise (kurz "AttrRefs"). Dies sind kurze Werte, die im Inode einer jeden Datei gespeichert werden und das Auffinden der Attributdaten für diese Datei in der Attributdatei AttrFile ermöglichen. Dieser Speicherort wird durch den Offset des Attributeintrags in der Attributdatei AttrFile dargestellt, welcher in Einheiten der Ausrichtungsgröße angegeben wird, d.h., ein AttrRef wird als der Byte-Offset geteilt durch die Ausrichtungsgröße berechnet.
    • – der Attributindex (kurz "AttrIndex"). Dies ist eine Datenstruktur, die das Auffinden eines bestimmten Attributwerts in der Attributdatei AttrFile ermöglicht. Der Aufbau und die Verwendung von AttrIndex wird im nächsten Abschnitt unter "Nachschlagen des Attributwerts" ausführlicher beschrieben.
    • – ein Attribut-Müllaufsammler (garbage collector). Dies ist ein Prozess, der zu geeigneten Zeitpunkten gestartet wird, um Attributeinträge aus der Attributdatei AttrFile zu entfernen, auf die keine der vorhandenen Dateien mehr verweist.
  • Gemeinsame Benutzung des Attributwerts
  • In unserer bevorzugten Ausführungsform eines Dateisystems mit gemeinsam benutzten Platten ist die gemeinsame Benutzung des Attributwerts als eine Ausführungsart eines erweiterten Attributs vorgesehen. Dadurch kann physischer Attributspeicher von allen Dateien, die Attribute mit genau gleichen Werten haben, gemeinsam benutzt werden. Realisiert wird dies, indem alle Attributdaten an einem gemeinsamen Ort gespeichert werden, an dem Ort, den wir die Attributdatei AttrFile nennen würden. Der im Inode einer Datei "f" gespeicherte Attributverweis AttrRef enthält den Speicherort des Eintrags, der die Attributdaten für "f" in der Attributdatei AttrFile enthält, und wird durch den Offset des Eintrags in AttrFile dargestellt. Dateien mit identischen Attributwerten enthalten dieselben Werte von AttrRef in ihrem Inode. Diese gemeinsame Benutzung des Attributwerts wird auf die folgenden beiden Arten realisiert:
    • 1. Attributvererbung: Attributvererbung bedeutet, dass beim Anlegen einer neuen Datei deren erweiterte Attribute auf dieselben Werte wie die der vorhandenen Datei gesetzt werden, von der sie abgeleitet wird. Beim Kopieren einer Datei können beispielsweise die Attributwerte der Kopie auf dieselben Werte wie die der ursprünglichen Datei gesetzt werden. POSIX-ACLs sind ein Beispiel für eine andere Art der Attributvererbung: Der vorgeschlagene POSIX-ACL-Standard legt fest, dass beim Anlegen einer neuen Datei oder eines neuen Verzeichnisses die ACL der Datei auf einen standardmäßigen ACL-Wert gesetzt wird, der zu dem Verzeichnis gehört, in dem die Datei angelegt wird. Anders ausgedrückt, bei POSIX-ACLs erbt eine neue Datei ihre ACL von ihrem Elternverzeichnis. Gemäß unserer Erfindung wird diese Attributvererbung realisiert, indem einfach der Attributverweis AttrRef von dem Inode der Datei oder des Verzeichnisses kopiert wird, von der beziehungsweise dem das Attribut geerbt wird. Auf diese Weise benutzt das geerbte Attribut denselben physischen Speicher gemeinsam mit dem Attribut, von dem es geerbt wird.
    • 2. Nachschlagen des Attributwerts: Um ein Attribut auf einen Wert zu setzen oder auf einen Wert zu ändern, der nicht von einer anderen Datei geerbt wird, wird der Attributindex zur Feststellung verwendet, ob in der Attributdatei AttrFile bereits ein Eintrag mit demselben Wert vorhanden ist. Ein Indexierungsverfahren wie zum Beispiel Hashing kann zu diesem Zweck verwendet werden: Um einen Attributwert zu setzen oder zu ändern, wird eine Hash-Funktion auf die Attributdaten angewandt. Der resultierende Hash-Wert wird als ein Index in die Hash-Tabelle verwendet, in der man auf eine Liste mit Attributverweisen (AttrRefs) trifft, die auf Einträge in der Attributdatei AttrFile mit Attributdaten verweisen, welche mit Hilfe der Streuspeichertechnik auf denselben Hash-Wert abgebildet werden. Die neuen Attributdaten, die gespeichert werden sollen, werden mit den Daten in allen dieser Einträge verglichen. Wenn eine Übereinstimmung festgestellt wird, wird ein Attributverweis AttrRef, der auf den vorhandenen Eintrag verweist, im Inode der Datei gespeichert. Wenn keine Übereinstimmung festgestellt wird, wird ein neuer Eintrag, der den neuen Attributwert enthält, zur Attributdatei AttrFile hinzugefügt, und ein Attributverweis AttrRef auf den neuen Eintrag wird im Inode der Datei und auch in der Hash-Tabelle gespeichert, so dass zukünftige Aktualisierungen von Attributen, die denselben Attributwert verwenden, den neuen Eintrag finden. Um die Wahrscheinlichkeit zu erhöhen, dass Attributwerte gemeinsam benutzt werden, werden neue Attributwerte, wenn möglich, in eine kanonische Form umgewandelt, bevor sie gespeichert oder bevor nach ihnen gesucht wird. Die Einträge in einer Zugriffssteuerungsliste können zum Beispiel nach der Benutzerkennung oder der Gruppenkennung sortiert werden; dadurch können zwei ACLs, die funktional gleichwertig sind, denselben Speicher in der Attributdatei AttrFile gemeinsam benutzen, obgleich die beiden ACLs möglicherweise nicht in genau demselben Format übergeben wurden, als sie festgelegt wurden.
  • In der realisierten Weise eignet sich unser System zur Speicherung von erweiterten Attributen insbesondere zur Speicherung von ACLs und zu anderen ähnlichen Zwecken. Zwar ist es möglich, dass ein Benutzer eine große Anzahl von Dateien besitzt, doch ist es sehr unwahrscheinlich, dass er jeder seiner Dateien eine andere ACL zuordnet. Vielmehr gibt es üblicherweise Gruppen von zusammengehörenden Dateien, denen allen dieselben Zugriffsrechte zugeordnet sind. Dateien beispielsweise, die zu einem bestimmten Projekt gehören, hätten üblicherweise alle dieselbe ACL, die Zugriff auf Benutzer gewährt, die dem Projekt zugeordnet sind. Als ein weiteres Beispiel benutzen Dateien in demselben Verzeichnis oder in demselben Teilbaum der Verzeichnishierarchie oftmals dieselbe ACL gemeinsam. Tatsächlich hat die Vererbung der ACL in dem vorgeschlagenen POSIX-ACL-Standard den Zweck, einem Benutzer die Verwaltung einer gemeinsamen ACL für Dateien in demselben Verzeichnis zu erleichtern. Deshalb gehen wir davon aus, dass die Gesamtzahl der unterschiedlichen ACL-Werte in einem Dateisystem wesentlich geringer als die Gesamtzahl der Dateien ist; tatsächlich gehen wir davon aus, dass sie um einen großen Faktor geringer ist. Das bedeutet, dass im Vergleich zu der Vorgehensweise, bei der jede ACL einzeln gespeichert wird, die gemeinsame Benutzung des ACL-Speichers durch Dateien mit gleichen ACLs den zusätzlichen Platzbedarf zur Speicherung von ACLs um mindestens den gleichen Faktor verringert.
  • Außerdem enthalten ACLs üblicherweise keine lange Liste mit einzelnen Benutzern, da solche Listen schwierig zu verwalten sind. Die meisten Systeme ermöglichen vielmehr die Festlegung von Benutzergruppen; mit einer Gruppe kann dann in einer ACL auf die Benutzer verwiesen werden, die zu dieser Gruppe gehören. Es ist daher unüblich, dass ACLs sehr lang sind, was bedeutet, dass eine ACL gewöhnlich wenig Speicherplatz in Anspruch nimmt. Diese Tatsache in Verbindung mit der gemeinsamen Benutzung der ACL bedeutet, dass es möglich ist, ACL-Daten für viele Dateien im Cachespeicher abzulegen. Dadurch kann die ACL für eine Datei schnell abgerufen werden, da die Daten der ACL wahrscheinlich im Cachespeicher abgelegt sind, so dass ohne zusätzliche Platten-E/A-Operation darauf zugegriffen werden kann.
  • Wenn ACLs für sehr viele Dateien geändert werden, werden viele dieser ACLs wahrscheinlich auf den gleichen neuen Wert geändert. Eine solche Änderung würde zum Beispiel stattfinden, um einem neuen Benutzer den Zugriff auf die Dateien zu gewähren, die zu einem bestimmten Projekt gehören. Aufgrund der gemeinsamen Benutzung einer ACL erfordert nur die erste einer Reihe von zusammengehörenden Änderungsoperationen an der ACL die Aktualisierung der Attributdatei AttrFile: Bei späteren Änderungsoperationen an der ACL, bei denen derselbe ACL-Wert verwendet wird, muss nur der ACL-Wert im Attributindex AttrIndex nachgeschlagen werden. Dies bedeutet, dass der Zugriff selbst bei einer Arbeitslast mit sehr vielen gleichzeitigen ACL-Aktualisierungen meist ein Nur-Lese-Zugriff ist. Daher verursacht die Tatsache, dass alle Attribute an einem gemeinsamen Ort gespeichert werden, kein Problem in Form eines Engpasses. Dies ist besonders in einer verteilten Umgebung wichtig, in der es wünschenswert ist, Attributdaten lokal im Cachespeicher abzulegen, was Aktualisierungen an der Attributdatei AttrFile aufgrund dessen, dass Attributdaten, die an anderen Knoten im Cachespeicher abgelegt sind, ungültig gemacht werden müssen, wesentlich aufwändiger macht.
  • Die Bereinigung des Speichers (garbage collection) ist eine Funktion, die bereitgestellt werden muss, da sie laufend benötigt wird. Die gemeinsame Benutzung des Attributwerts macht es etwas schwierig, Speicherbereich in der Attributdatei AttrFile zurückzufordern, wenn ein Attributeintrag nicht mehr benötigt wird. Das Problem besteht darin, festzustellen, wann es sicher ist, den Eintrag zu löschen, d.h., wenn die letzte Datei, die auf den Eintrag verwies, gelöscht oder wenn ihr Attribut geändert wird. Dieses Problem wird gewöhnlich so gelöst, dass für jeden Eintrag die Anzahl der Referenzen festgehalten wird; die Referenzanzahl würde erhöht werden, wenn ein Attributverweis AttrRef, der auf den Eintrag verweist, im Inode einer Datei gespeichert wird, und sie würde verringert werden, wenn ein AttrRef gelöscht wird. Der Eintrag in der Attributdatei AttrFile könnte dann gelöscht werden, wenn die Referenzanzahl auf null zurückgeht. Bei dieser Lösung wäre es jedoch notwendig, die Referenzanzahl jedes Mal zu aktualisieren, wenn ein Attribut geerbt, gespeichert oder aktualisiert wird, selbst wenn der neue Attributwert in der Attributdatei AttrFile bereits vorhanden wäre. Folglich wäre der Zugriff auf die Attributdatei AttrFile in den meisten Fällen kein Nur-Lese-Zugriff mehr, was möglicherweise einen Engpass verursachen könnte.
  • Statt die Referenzanzahl festzuhalten, fordert diese Erfindung Attributspeicherbereich mittels Speicherbereinigung zurück. Bei der Speicherbereinigung werden nicht verwendete Attributeinträge wie folgt aufgefunden und gelöscht. Ein Teil eines jeden Attributeintrags ist eine Verweismarkierung, kurz "RefFlag", die immer gesetzt wird, wenn ein neuer Eintrag in die Attributdatei AttrFile aufgenommen wird. Die Speicherbereinigung findet in den folgenden drei Phasen statt:
    • Phase 1: Fragt die ganze Attributdatei AttrFile ab und schaltet die Markierung RefFlag in jedem Attributeintrag in der Datei aus.
    • Phase 2: Fragt alle Inodes ab. Für jeden in einem Inode gefundenen Attributverweis AttrRef schaltet sie die Markierung RefFlag für den entsprechenden Attributeintrag in der Attributdatei AttrFile wieder ein.
    • Phase 3: Fragt die Attributdatei AttrFile erneut ab und löscht alle Attributeinträge, bei denen die Markierung RefFlag immer noch ausgeschaltet ist.
  • Um sicherzustellen, dass der Prozess der Speicherbereinigung keine Einträge löscht, für die während des Prozesses neue Verweise erzeugt werden, muss sich der Prozess der Speicherbereinigung mit der Suchoperation synchronisieren, die Teil des Vorgangs ist, bei dem ein Dateiattribut gesetzt oder geändert wird, wie unter "Nachschlagen des Attributwerts" im Abschnitt "Gemeinsame Benutzung des Attributwerts" vorstehend beschrieben wurde. Da die Speicherbereinigung verhältnismäßig lange dauern kann – insbesondere die Phase 2 –, ist es nicht erwünscht, einfach alle Operationen, bei denen Attribute gesetzt oder geändert werden, zu sperren, während die Speicherbereinigung ausgeführt wird. Wenn eine Operation, bei der Attribute gesetzt oder geändert werden, in der Attributdatei AttrFile einen vorhandenen Eintrag mit einem Wert findet, der mit dem neuen Wert, der gerade gesetzt wird, übereinstimmt, prüft sie auch, ob die Markierung RefFlag in dem Eintrag eingeschaltet ist, bevor sie AttrRef im Inode der Datei speichert. Auf diese Weise ist eine ausdrückliche Synchronisation zwischen der Speicherbereinigung und dem Nachschlagen des Attributwerts nur während der letzten Phase der Speicherbereinigung notwendig, und dann nur, wenn die Suchoperation nach dem Attributwert einen Attributeintrag findet, bei dem die Markierung RefFlag ausgeschaltet ist.
  • Der Prozess des Startens der Speicherbereinigung ist wichtig. Ohne Speicherbereinigung könnte die Datei AttrFile grenzenlos weiter an Größe zunehmen, selbst wenn die Gesamtmenge der aktiven Attributdaten (Attributwerte, auf die immer noch verwiesen wird) nicht weiter zunimmt. Die Geschwindigkeit, mit der die Datei AttrFile an Größe zunehmen würde, hängt von der Geschwindigkeit der Operationen ab, bei denen Attribute gesetzt oder geändert werden. Bei Attribut-Verwendungszwecken wie zum Beispiel ACLs ist die Geschwindigkeit solcher Operationen im Grunde nicht vorhersagbar. Daher ist eine Vorgehensweise, die die Speicherbereinigung in festen regelmäßigen Intervallen (z.B. einmal pro Tag) startet, nicht geeignet. Stattdessen überwachen wir den Gesamtumfang der Attributdaten, d.h. die Größe der Datei AttrFile abzüglich des gesamten freien Speicherbereichs in AttrFile. Die Speicherbereinigung wird jedes Mal gestartet, wenn die Menge der Attributdaten um einen bestimmten Faktor (z.B. 1,5 oder 2) zugenommen hat. Mit dieser Vorgehensweise kann wirksam verhindert werden, dass die Datei AttrFile an Größe zunimmt, wenn die Menge der aktiven Attributdaten gleich bleibt.
  • Funktionsweise des Metadaten-Knotens
  • Dieser Abschnitt beschreibt die Funktionsweise des Metadaten-Knotens, der die Leistungsfähigkeit in den Fällen verbessert, in denen mehrere Rechner dasselbe Datenobjekt aktualisieren oder vergrößern müssen. Wir beginnen mit der Erzeugung eines Metaknotens für diese Funktionen und fahren anschließend mit der Beschreibung von Verfahren zur Kennzeichnung des Metadaten-Knotens und seiner Wiederherstellung fort.
  • Verwendung eines Metadaten-Knotens
  • Der erste Abschnitt über unseren Metadaten-Knoten beschreibt allgemein, was unser Metadaten-Knoten ist und welches Problem er löst. Ein Metadaten-Knoten wird in unserem System zur Verwaltung von Datei-Metadaten für parallele Lese- und Schreiboperationen in der Umgebung mit gemeinsam benutzten Platten verwendet. In dem parallelen Dateisystem können mehrere Prozessoren auf alle Platten, die das Dateisystem bilden, unabhängig voneinander zugreifen. Um diese Fähigkeit nutzen zu können, sollte eine Datei von mehreren Prozessoren sowohl für Lese- als auch Schreiboperationen gemeinsam benutzt werden.
  • Es gibt mehrere Probleme, die die Leistungsfähigkeit eines solchen Zugriffs erheblich beeinträchtigen können. Obgleich Knoten aus verschiedenen Bereichen der Datei lesen und diese beschreiben können, wenn sie auf die Abschnitte, die sie gerade lesen oder beschreiben, eine entsprechende Sperre gesetzt haben, müssen sie alle auf dieselben Metadaten zugreifen. Zu den Metadaten gehören die Dateigröße, der Zeitpunkt, zu dem auf die Datei zugegriffen wurde, der Zeitpunkt, zu dem die Datei geändert wurde, und die Adressen der Datenblöcke der Datei. Alle Operationen zum Beispiel, die die Datei lesen und in die Datei schreiben, müssen wissen, ob sie die Dateigröße überschreiten, und die Datei aktualisieren, wenn sie sie erweitern. Eine solche einzelne Interessenfrage kann einen gravierenden Engpass darstellen, wenn eine Datei für wirklich parallele Schreiboperationen freigegeben werden muss.
  • Wir haben ein System realisiert, das es jedem Knoten ermöglicht, beim Lesen und Beschreiben derselben Dateien so unabhängig wie möglich zu agieren, und wir haben einen Mechanismus entwickelt, mit dem diese Operationen synchronisiert werden können, so dass von allen Knoten eine übereinstimmende Ansicht der Datei verfügbar ist, indem unser Verfahren zur Verwaltung von Metadaten-Informationen bereitgestellt wird. Unser Verfahren zur Verwaltung von Metadaten-Informationen für eine Datei in einem Dateisystem mit gemeinsam benutzten Platten sieht vor, dass für jede Datei ein einzelner Knoten als Metadaten-Knoten (oder Metaknoten) für diese Datei ausgewählt wird. Der Metaknoten ist für die Abwicklung der gesamten E/A-Aktivität der Metadaten von und auf die Platte (oder die Platten), auf der beziehungsweise auf denen sich die Metadaten befinden, verantwortlich.
  • Alle anderen Knoten tauschen mit dem Metadaten-Knoten Daten aus, um Metadaten-Informationen abzurufen oder zu aktualisieren. Diese Knoten greifen jedoch nicht direkt auf die Metadaten-Informationen auf der Platte zu.
  • Der Metadaten-Knoten wird zum ersten Knoten bestimmt, der auf die Datei zugreift. Wenn folglich nur ein Knoten auf die Datei zugreifen muss, entsteht kein Mehraufwand, da der Knoten direkt auf die Metadaten zugreifen kann. Weitere Knoten greifen zum Erhalt von Metadaten auf den Metaknoten zu.
  • Durch die Einführung eines Metaknotens wird eine beträchtliche Menge an Plattenaktivität vermieden, was bei einem parallelen Dateisystem mit einer schnellen Datenaustausch-Vermittlungsstelle eine deutliche Leistungsverbesserung darstellt.
  • Der Metaknoten hält eine im Cachespeicher abgelegte Kopie der Metadaten, die die Metadaten auf der Platte widerspiegelt. Andere Knoten halten ebenfalls eine zwischengespeicherte Kopie der Metadaten, die sie in der Vergangenheit von dem Metaknoten gelesen und nach Bedarf erweitert haben (indem sie zum Beispiel die Zugriffszeit änderten).
  • Jedes Element der Metadaten (Zugriffszeit, Änderungszeitpunkt, Dateigröße, Plattenadresse des Datenblocks) hat sein eigenes Nutzungsmuster und seine eigenen speziellen Merkmale. Unser System erfordert beispielsweise keine sehr genaue Zugriffszeit, sondern lediglich eine Zugriffszeit mit einer Toleranz von fünf Minuten. Folglich müssen Aktualisierungen des Metaknotens nicht häufig stattfinden, wodurch ein beträchtlicher Datenübertragungsaufwand eingespart wird.
  • Auch die Dateigröße muss nicht auf allen Knoten ganz genau sein, solange sich das System beständig gleich verhält. Eine durchdachte Vorgehensweise bei der Überwachung der Dateigröße auf allen Knoten ermöglicht die Umsetzung eines parallelen Schreibschemas, bei dem mehrere Knoten die Datei gleichzeitig erweitern können.
  • Durch die Verwendung eines Algorithmus für eine verzögerte Synchronisation können viele Plattenzugriffe eingespart werden. Eine Synchronisations-Hintergrundroutine (sync daemon) ist eine Softwarekomponente, die als Teil des Betriebssystems auf jedem Knoten läuft. Die Synchronisations-Hintergrundroutine versucht, alle N Sekunden verfälschte Daten und Metadaten auf die Platte zu schreiben. Wenn M Knoten parallel in die Datei schreiben, bedeutet dies, dass nur für die Metadaten alle N Sekunden M Plattenzugriffe stattfinden. Bei parallelen Schreiboperationen senden alle Knoten ihre aktualisierten Metadaten an den Metaknoten, der den Inhalt der Datei alle N Sekunden löscht, wenn er ein Signal von der Synchronisations-Hintergrundroutine empfängt.
  • Jeder Knoten würde der Reihe nach auf die Platte zugreifen, um Metadaten zu lesen oder zu schreiben.
  • Die Verwendung von Token
  • Der zweite von den Abschnitten dieser Beschreibung, in denen es um parallele Schreiboperationen geht, betrifft unsere Verwendung von Sperrmodi, um den Metadaten-Verwaltungsknoten zu finden. Token, die zum Auffinden des Metadaten-Verwaltungsknotens Sperrmodi verwenden, dienen zur Auswahl und Kennzeichnung von Metadaten-Knoten in unserem parallelen Dateisystem, in dem mehrere Prozessoren unabhängig voneinander auf alle Platten, die das Dateisystem bilden, zugreifen können. Um diese Fähigkeit nutzen zu können, sollte eine Datei von mehreren Prozessoren sowohl für Lese- als auch Schreiboperationen gemeinsam benutzt werden.
  • Bei diesem System wird für jede Datei ein Knoten bestimmt, der für den Zugriff auf und die Aktualisierung von den Metadaten der Datei zuständig ist. Dieser Metadaten-Knoten (oder Metaknoten) teilt diese Information auf Anfrage mit anderen Knoten.
  • Der Metadaten-Knoten bewahrt die Informationen über die Metadaten der Datei auf und hat die Funktion eines intelligenten Cachespeichers zwischen der Platte und all denjenigen Knoten, die auf die Datei zugreifen. Es gibt Situationen, in denen der Metadaten-Knoten (oder Metaknoten) die Ausführung dieser Funktion einstellt. Um einen reibungslosen Betrieb und eine problemlose Wiederherstellung zu ermöglichen, müssen diese Situationen behandelt werden. Knoten, die gewöhnlich auf den Metaknoten zugegriffen haben, müssen auf direktem Weg einen neuen Metaknoten wählen.
  • Wir wählen einen Metaknoten und stellen diese Information allen Knoten bereit. Beim Wahlvorgang werden die Zugriffsmuster auf die Datei berücksichtigt. Es sollte nur einen einzigen Metaknoten pro Datei geben. Zudem sollte das Schema keine Übernahme und keine Wiederherstellung von Metaknoten erlauben und tut dies auch nicht. In unserem System werden Metaknoten ausgewählt, und ihre Informationen sind anderen Knoten bekannt.
  • Wir verwenden ein Token-Verwaltungsteilsystem. Ein Token-Verwaltungsprogramm ist ein verteiltes Teilsystem, das Knoten Token gewährt. Jeder Knoten kann um ein benanntes Token mit einer ganz bestimmten Betriebsart bitten. Das Token-Verwaltungsprogramm gewährt dem Knoten das Token, wenn die Betriebsart nicht einen Konflikt mit Token auslöst, die denselben Namen haben und anderen Knoten gewährt wurden. Für jedes Token gibt es eine Liste der möglichen Betriebsarten und eine Konflikttabelle. Wenn das angeforderte Token mit einem Token in Konflikt gerät, das einem anderen Knoten gewährt wurde, erfolgt ein Widerruf, und der den Konflikt auslösende Knoten stuft seine Token-Betriebsart zurück auf eine Betriebsart, die nicht mit der angeforderten Betriebsart in Konflikt gerät.
  • Der Metadaten-Knoten wird zum ersten Knoten bestimmt, der auf die Datei zugreift. Wenn folglich nur ein Knoten auf die Datei zugreifen muss, sind keine Nachrichten beziehungsweise kein Mehraufwand erforderlich, da der Knoten direkt auf die Metadaten zugreifen kann. Weitere Knoten greifen zum Erhalt von Metadaten auf den Metaknoten zu.
  • Für jede Datei legen wir das "Metaknoten-Token" fest. Es gibt drei Betriebsarten für das Metaknoten-Token: "ro" (read-only (Nur-Lese-Modus)), "ww" (weak-write (schwacher Schreibmodus)) und "xw" (exclusive-write (exklusiver Schreibmodus)). Die Regeln sind wie folgt: Das Token "xw" gerät mit allen Betriebsarten in Konflikt. "ww" gerät mit "xw" und sich selbst in Konflikt. "ro" gerät nur mit "xw" in Konflikt. Es gibt folglich zwei Möglichkeiten: Entweder 0 oder mehr Knoten halten das Token in "ro", und dann kann höchstens ein Knoten das Token in "ww" halten, oder ein einziger Knoten hält das Token in "xw". Das Teilsystem Token Manager (oder kurz TM) ist für die Verwaltung von Token für einen Knoten und für die Sicherstellung zuständig, dass die Token-Betriebsarten mit dieser Festlegung übereinstimmen. Die Konflikte zwischen den verschiedenen Betriebsarten lassen sich in der folgenden Tabelle 5 zusammenfassen: TABELLE 5
    Figure 00960001
  • Für den Metaknoten haben wir den folgenden Algorithmus entwickelt: Wenn ein Knoten eine Datei zum ersten Mal öffnet, versucht er, das Token des Metaknotens in der Betriebsart "ww" zu erhalten. Das Token-Verwaltungsprogramm TM gewährt das Token in "ww", sofern dies möglich ist, d.h., wenn kein anderer Knoten das Token in "ww" oder in "xw" hält. Wenn dies geschieht, übernimmt der Knoten die Funktion des Metaknoten-Verwaltungsprogramms. Wenn jedoch ein anderer Knoten das Token in "ww" hält, gewährt das Token-Verwaltungsprogramm das Token in "ro". Dann weiß der Knoten, dass ein anderer Knoten der Metaknoten ist. Er kann eine Anfrage an das Token-Verwaltungsprogramm richten, um herauszufinden, welcher Knoten der Metaknoten für diese Datei ist.
  • Es gibt Situationen, in denen ein Knoten ein Metaknoten werden muss. In diesem Fall hilft es nicht, um ein "ww"-Token zu bitten, da der alte Metaknoten sein Token nicht zurückstuft. Hier bittet der Knoten, der der Metaknoten werden will, um ein "xw"-Token. Dies bewirkt, dass eine Widerrufnachricht an den aktuellen Metaknoten gesendet wird. Der alte Metaknoten stuft dann sein Token auf "ro" zurück, und das Token- Verwaltungsprogramm schickt ein "ww"-Token an den neuen Metaknoten zurück. Wenn ein Knoten um ein "xw"-Token bittet und überhaupt keine anderen Knoten dieses Token halten, gewährt das Token-Verwaltungsprogramm das Token in dieser Betriebsart.
  • Wenn ein Knoten das Token in "xw" hält, ist er der Metaknoten für diese Datei, aber darüber hinaus ist diese Datei bei keinem anderen Knoten geöffnet. In diesem Fall wird, wenn ein Knoten versucht, das Token in "ww" zu erhalten, eine Widerrufnachricht an den Metaknoten gesendet. Folglich stuft der Knoten sein "xw"-Token auf "ww" zurück, und das Token-Verwaltungsprogramm kann dem neuen Knoten somit ein "ro"-Token gewähren.
  • Verwendung von erweiterten Token-Betriebsarten zur Steuerung der Dateigröße
  • Die entsprechenden Dateisystem-Standards setzen voraus, dass die korrekte Dateigröße auf Anforderung verfügbar ist; jedoch ist die parallel an allen Knoten erfolgende Verwaltung der Dateigröße bei Vorhandensein von mehreren Anwendungen, die Daten an die Datei anfügen, schwierig und leistungsintensiv. Das nächste dieser Reihe von Merkmalen beschreibt unsere Art der Verwaltung der Dateigröße, so dass sie bei Bedarf ohne ständigen Mehraufwand verfügbar ist. Dabei kann ein paralleles Dateisystem, bei dem mehrere Prozessoren auf alle Platten, die das Dateisystem bilden, unabhängig voneinander zugreifen können, mit einer Datei, die von mehreren Prozessoren sowohl für Lese- als auch Schreiboperationen gemeinsam benutzt wird, ohne ständigen Mehraufwand verwendet werden.
  • Die Freigabe von Dateien für Lese- und Schreiboperationen beinhaltet den Zugriff auf die Dateigröße. Jede Lese- und Schreiboperation muss prüfen, ob der Offset der Operation außerhalb der aktuellen Dateigröße liegt, und wenn ja, muss sie eine EOF-Nachricht, die das Ende der Datei (end of file, EOF) anzeigt, zurückschicken. Jede Schreiboperation muss prüfen, ob der Offset der Operation außerhalb des aktuellen Dateiendes liegt, und wenn ja, sollte sie die Datei erweitern. Wenn es mehrere Benutzer gibt, die Daten lesen und schreiben, muss dies alles übereinstimmend stattfinden. Wenn ein Knoten am Offset 1000 Daten schreibt, sollte bei einer Leseoperation durch einen beliebigen Knoten an diesem Speicherplatz folglich keine EOF-Nachricht zurückgeschickt werden.
  • Eine Möglichkeit, einen übereinstimmenden Zustand aufrechtzuerhalten, besteht darin, die Zugriffe auf die Dateigröße zeitlich aufeinander folgend durchzuführen. Dies stellt jedoch für Benutzer, die parallel Schreiboperationen durchführen, einen großen Engpass dar, da jede Schreiboperation (und jede Leseoperation) vor ihrer Durchführung die aktuelle Dateigröße abrufen muss.
  • In unserer bevorzugten Ausführungsform hinterlegen wir in jedem Knoten eine lokale Kopie der Dateigröße. Zusammen mit jeder Kopie wird auch eine Sperre hinterlegt. Ein Sperrenverwaltungsprogramm stellt sicher, dass keine Sperrmodi gleichzeitig vorhanden sein, die miteinander in Konflikt geraten. Ein geeigneter Sperrmodus für jede Lese- und Schreiboperation stellt sicher, dass die lokal zwischengespeicherte Dateigröße genau genug ist, damit diese Operation ein korrektes Ergebnis erzielen kann. Die verschiedenen Betriebsarten sind wie folgt:
    • – "rw" bei Operationen, die innerhalb der lokal zwischengespeicherten Dateigröße Lese-und Schreiboperationen durchführen.
    • – "rf" bei Operationen, die außerhalb der lokal zwischengespeicherten Dateigröße Leseoperationen durchführen.
    • – "wf" bei Operationen, die außerhalb der lokal zwischengespeicherten Dateigröße Schreiboperationen durchführen.
    • – "wa" bei Schreiboperationen, die Daten an die Datei anfügen
    • – "xw" bei Operationen, die die Dateigröße verringern wie zum Beispiel die Operation "kürzen" (truncate) und folglich eine Sperre für eine exklusiven Schreibzugriff benötigen.
  • Die Konflikttabelle der Sperrmodi der Dateigröße schaut folgendermaßen aus: TABELLE 6
    Figure 00990001
    Figure 01000001
  • Immer, wenn ein Knoten seinen Sperrmodus aktualisiert, liest er die neue Dateigröße aus einem speziellen Knoten, der die Dateigröße überwacht (dem Metadaten-Knoten oder kurz Metaknoten). Immer, wenn ein Knoten seinen Sperrmodus zurückstuft, sendet er seine Dateigröße an den Metaknoten. Der Metaknoten selbst behält eine Dateigröße, die den Höchstwert aller von ihm empfangenen Dateigrößen darstellt (außer, wenn ein Knoten die Dateigröße in der Betriebsart "xw" sperrt, was die Verringerung der Dateigröße erlaubt).
  • Manche Betriebsarten lassen nur das Lesen der Dateigröße zu (rw rf). Manche Betriebsarten (wf, wa) gestatten es, dass die Dateigröße zunimmt. Eine Betriebsart (xw) gestattet es, die Dateigröße zu verringern. Die wirkliche Dateigröße ist der Höchstwert aller lokaler Kopien der Dateigrößen, die der Knoten hält.
  • Operationen, die in der lokal zwischengespeicherten Kopie der Dateigröße lesen oder schreiben, benötigen eine "rw"-Sperre auf die Dateigröße. Operationen, die außerhalb der lokal zwischengespeicherten Kopie der Dateigröße lesen, müssen sicherstellen, dass die Dateigröße nicht zugenommen hat, seit sie die Dateigröße das letzte Mal gelesen haben. Sie müssen folglich in den Besitz einer "rf"-Sperre kommen (die mit Betriebsarten, die die Dateigröße erhöhen, in Konflikt geraten).
  • Operationen, die die Dateigröße erhöhen, erwerben entweder eine "wf"-Sperre oder eine "wa"-Sperre. Eine "wf"-Sperre wird benötigt, wenn derjenige, der Daten schreibt, die neue absolute Dateigröße kennt. Eine "wa"-Sperre wird für Anfüge-Operationen (APPEND-Operationen) benötigt. Eine APPEND-Operation schreibt an das aktuelle Dateiende (EOF). Bei mehreren APPEND-Operationen schreibt folglich jede APPEND-Operation jeweils an das Ende der anderen. Somit gerät "wa" in Konflikt mit sich selbst, da eine APPEND-Operation auf andere APPEND-Operationen warten sollte.
  • Die einzige Betriebsart, die eine Verringerung der Dateigröße zulässt, ist "xw". Dabei handelt es sich um eine exklusive Betriebsart, die bewirkt, dass alle anderen Knoten ihre Sperren aufgeben und somit die lokal zwischengespeicherte Dateigröße verlieren. Nachdem also der Knoten, der die "xw"-Sperre erworben hat, seine Operation beendet (beispielsweise eine Dateikürzungsoperation), müssen alle Knoten die neue Dateigröße von dem Metaknoten abrufen.
  • Uns ist kein System bekannt, bei dem verschiedene Dateigrößen an verschiedenen Knoten zwischengespeichert werden, so dass die Freigabe der Datei für parallele Schreiboperationen auf ein Höchstmaß gesteigert wird und das System dennoch allen Benutzen eine übereinstimmende Ansicht der Datei liefert.
  • Die Lösung ermöglicht Benutzern an verschiedenen Knoten, die Datei zu erweitern und auf diese Weise einen sehr hohen Grad der Freigabe von Schreiboperationen zu erreichen. Schreiboperationen brauchen selbst dann nicht zeitlich aufeinander folgend durchgeführt werden, wenn die Benutzer die Dateigröße erweitern.
  • Intelligente Zwischenspeicherung von Bytebereichs-Token mit Hilfe von Dateizugriffsmustern
  • Die nächste unserer Entwicklungen im Rahmen von parallelen Schreiboperationen befasst sich mit den für alle Zugriffe verwendeten Sperrmechanismen, und zwar mit parallelen und mit nichtparallelen. Das Sperren von nur demjenigen Teil der Datei, der sofort benötigt wird, ist aufwändig und würde bei jedem Aufruf der Anwendung Aufrufe des Sperrenverwaltungsprogramms erforderlich machen. Dieser Algorithmus versucht, die Erfordernisse der Anwendung vorwegzunehmen, indem er sorgfältig prüft, welche Abläufe noch in dem System stattfinden, und die Anzahl der Aufrufe des Token-Verwaltungsprogramms so gering wie möglich zu halten.
  • Bei parallelen Lese- und Schreiboperationen in die gleiche Datei wird ein verteilter Sperrmechanismus verwendet, um die Zugriffe auf dieselben Bereiche in einer Datei zeitlich aufeinander folgend durchzuführen. Der Erhalt einer solchen Sperre setzt jedoch gewöhnlich voraus, dass zuerst ein Token abgerufen wird, und dies gilt als eine aufwendige Operation. Folglich wäre es von Vorteil, Tokens an einem Knoten zwischenzuspeichern, indem man die Zugriffsmuster der Datei vorhersieht. Andererseits könnte der Erwerb eines Token, das nicht gebraucht wird, die Leistungsfähigkeit herabsetzen, da dieses Token von einem anderen Knoten gebraucht werden könnte. Diese Beschreibung legt die Algorithmen dar, mit denen ein Knoten ein Token erwirbt, um die Leistungsfähigkeit auf ein Höchstmaß zu steigern, indem man die Zugriffsmuster der Datei vorhersieht.
  • Die zeitlich aufeinanderfolgende Durchführung von Zugriffen auf verschiedene Bereiche in einer Datei, in die Prozesse auf verschiedenen Knoten parallel Daten schreiben, erfolgt durch verteilte Bytebereichssperren. Wenn ein Prozess einen Bytebereich sperren muss, muss er zuerst ein entsprechendes Bytebereichs-Token erwerben. Das Bytebereichs-Token stellt die Zugriffsrechte des Knotens auf einen Teil einer Datei dar. Wenn ein Knoten folglich ein Bytebereichs-Token für den Bereich (100, 200) einer Datei X im Lesemodus hält, bedeutet dies, dass der Knoten diesen Teil der Datei sicher lesen kann. Um jedoch zu verhindern, dass das Token gestohlen wird, muss der Knoten das Token vor der eigentlichen Leseoperation sperren, da ein anderer Knoten, der möglicherweise denselben Bereich beschreiben muss, das Token stehlen könnte. Durch das Sperren des Token wird verhindert, dass es gestohlen wird. Nach erfolgter Leseoperation wird das Token entsperrt.
  • Man kann Token als eine Möglichkeit sehen, Sperren im Cachespeicher "zwischenzuspeichern". Wenn ein Knoten einen Teil einer Datei sperren muss, muss er das Token sperren. Zuerst erwirbt er ein Token und sperrt es. Sobald die Operation abgeschlossen und das Token entsperrt ist, befindet es sich immer noch an dem Knoten. Folglich müssten nachfolgende Operationen in demselben Bereich nicht auf die Token-Instanz zugreifen. Nur wenn das Token gestohlen wird, muss eine neue Anforderung für das Token gestellt werden.
  • Wenn man dies berücksichtigt, mag es von Vorteil sein, ein größeres Token als das, das gesperrt werden muss, anzufordern. Wenn ein Prozess zum Beispiel eine Datei in Folge liest und dabei vom Bereich 1000 bis zum Bereich 2000 liest, kann er, obgleich die nächste Sperre für den Bereich 1000 bis 2000 gilt, ein größeres Token anfordern, zum Beispiel vom Bereich 1000 bis zum Bereich 10000. Dies kann jedoch einen übermäßigem Token-Verkehr auf anderen Knoten erzeugen. Wenn ein anderer Knoten gerade dabei ist, vom Bereich 5000 bis zum Bereich 6000 zu schreiben, kann der Vorgang des Erwerbs des Token die Operation verzögern.
  • Es ist beabsichtigt, beim Erwerb eines Bytebereich-Token zwei Bereiche zu vergeben: einen benötigten Bereich (dies ist der größtmögliche Bereich, der für die Operation benötigt wird) und den gewünschten Bereich (dies ist der größtmögliche Bereich, der von Nutzen sein soll). Das Token-Verwaltungsprogramm stellt sicher, dass ein Token gewährt wird, das den benötigten Bereich abdeckt, aber nicht größer als der gewünschte Bereich ist.
  • Zwei Algorithmen müssen festgelegt werden: (1) wie der gewünschte Bereich und der benötigte Bereich für jede Operation zu berechnen ist; dies gilt für die anfordernde Seite; (2) wie der gewährte Bereich zu berechnen ist; dies gilt für Knoten, die miteinander in Konflikt stehende Token halten.
  • Bei den vorstehenden Algorithmen unterscheiden wir zwischen zwei Dateizugriffsmustern: zufällig und sequenziell. Bei zufälligen Zugriffen kann der Anfangs-Offset der nächsten Operation nicht vorhergesagt werden. Bei in Folge durchgeführten Operationen wird davon ausgegangen, dass sie an dem Punkt starten, an dem die vorherige Operation beendet wurde. Jede Datei kann auf jedem Knoten mehrfach geöffnet sein, und jede solche Instanz kann ein anderes Zugriffsmuster darstellen.
  • Wir bevorzugen den folgenden Algorithmus. Das Hauptziel besteht darin, den Token-Verkehr auf ein Mindestmaß zu verringern.
  • Bei dem Versuch, einen Bytebereich mit einer Sperre zu belegen, fragen wir zuerst das Token-Verwaltungsprogramm ab, um zu sehen, ob es auf dem Knoten ein kompatibles Token gibt. Der Bereich, der geprüft wird, ist der von der Operation benötigte Mindestbereich. Wenn das Token lokal verfügbar ist, wird es gesperrt, und es findet keine weitere Token-Aktivität statt.
  • Wenn das Token jedoch nicht verfügbar ist, wird ein Token angefordert. Der benötigte Bereich wird auf der Grundlage des Offset und der Länge der Dateioperation berechnet. Der gewünschte Bereich beruht auf dem Zugriffsmuster der Datei. Wenn der Zugriff auf die Datei zufällig erfolgt, ist der gewünschte Bereich gleich dem benötigten Bereich, da es wahrscheinlich nicht von Vorteil ist, Token (die wahrscheinlich nicht benötigt würden) von anderen Knoten zu stehlen. Wenn der Zugriff auf die Datei jedoch sequenziell erfolgt, beginnt der gewünschte Bereich am Beginn des benötigten Bereichs, endet aber bei unendlich (es gibt einen speziellen Wert zur Darstellung der Unendlichkeit). Dies geschieht in dem Versuch, zukünftige Token-Anforderungen auf ein Mindestmaß herabzusetzen, da wir die zukünftigen Sperren, die benötigt werden, vorhersagen können.
  • Wenn ein Knoten ein Token hält, das mit einer Anforderung für ein Token auf einem anderen Knoten in Konflikt gerät, erhält er eine Widerrufaufforderung. Die Aufforderung enthält die benötigten und die gewünschten Bereiche des anfordernden Knotens. Hier muss der Knoten eine Entscheidung darüber treffen, welchen Bereich er abtreten kann. Wenn der benötigte Bereich gleich dem gewünschten Bereich ist, ist die Entscheidung leicht, und der Bereich, der gewährt wird, ist der benötigte (und gewünschte) Bereich. Wenn sich der gewünschte Bereich jedoch von dem benötigten Bereich unterscheidet, bedeutet dies, dass der anfordernde Knoten sequenziell auf die Datei zugreift und ein Token haben möchte, das am Anfang des benötigten Bereichs beginnt, aber bei unendlich endet. Der Knoten durchläuft dann alle seine aktiven Prozesse, die auf die Datei zugreifen, und prüft, ob sie sequenziell oder zufällig auf die Datei zugreifen. Wenn alle von ihnen zufällig auf die Datei zugreifen, gewährt der Knoten den gewünschten Bereich. Wenn jedoch einer oder mehrere der Prozesse sequenziell auf die Datei zugreifen, würde das Abtreten des gewünschten Bereichs eine Verschwendung darstellen, da wir mit hoher Wahrscheinlichkeit wissen, welches Token bald angefordert werden wird. In diesem Fall werden die Dateizeiger (d.h. der vorhergesehene Speicherort der nächsten Operation) von allen sequenziellen Operationen geprüft und der Mindest-Offset berechnet. Es kann vorweggenommen werden, dass diese Operationen nicht auf die Dateibereiche zugreifen, die diesen Mindestwert unterschreiten, da es sequenzielle Operationen sind. Folglich wird der gewährte Bereich auf diesen berechneten Mindestwert gestreckt, wenn dieser höher als der benötigte Bereich ist.
  • Uns ist kein System bekannt, bei dem Bytebereich-Token auf der Grundlage des Zugriffsmusters der Datei angefordert werden.
  • Die Lösung ermöglicht es, Token mit Rücksicht auf das Dateizugriffsmuster im Cachespeicher zwischenzuspeichern. Dies erspart den Erwerb an Token, was eine aufwändige Operation ist, und verbessert damit die Gesamtleistungsfähigkeit des Systems.
  • Jedes Parallelverarbeitungssystem, das es gestatten muss, dass Dateien für parallele Schreiboperationen freigegeben werden, muss Zugriffe auf dieselben Bereich in der Datei zeitlich aufeinenderfolgend durchführen.
  • Bytebereich-Token-Schnittstelle
  • Diese Verbesserung in Form von parallelen Schreiboperationen ermöglicht die Verwaltung von Informationen, die Token beschreiben, welche einen Bytebereich-Sperralgorithmus mit einer Bytebereich-Token-Schnittstelle verwenden. Unser paralleles Dateisystem, in dem auf alle Platten, die das Dateisystem bilden, von mehreren Prozessoren unabhängig voneinander zugegriffen werden kann, macht es bei seiner Nutzung erforderlich, dass eine Datei sowohl für Lese- als auch für Schreiboperationen von mehreren Prozessoren gemeinsam benutzt wird. Um eine parallele Schreiboperation zu ermöglichen, während gleichzeitig eine Übereinstimmung der Dateien gewährleistet wird, ist ein Sperrmechanismus für Bereiche in Dateien notwendig. In einer verteilten Umgebung werden manchmal Token verwendet. Dieses Token stellt die Zugriffsrechte eines Knotens auf ein Objekt dar. Ein Knoten könnte jedoch mehrere Prozesse ausführen, die versuchen, auf denselben Bereich einer Datei zuzugreifen; folglich ist ein lokaler Sperrmechanismus auf die Tokens erforderlich. Ferner muss vielleicht ein anderer Knoten auf denselben Bereich zugreifen und versucht möglicherweise, das Token von diesem Knoten zu widerrufen; ein Widerruf sollte also nicht stattfinden, solange ein lokaler Prozess das Token sperrt. Somit sollte irgendeine Art von Sperralgorithmen für diese Tokens verwendet werden, die von unserem Token-Verwaltungsprogramm verwaltet werden, das unsere Verbesserung gegenüber der US-Patentschrift 5 343 108 darstellt, die auf die International Business Machines Corporation übertragen wurde.
  • Um Zugriff auf einen Bereich in einer Datei zu erlangen, muss ein Knoten zuerst das entsprechende Token abrufen, es dann sperren, die Operation durchführen und das Token anschließend entsperren. Es gibt mehrere Probleme in Verbindung mit dem Sperren der Tokens; erstens kann ein Token bereits in dem Knoten zwischengespeichert sein. In diesem Fall brauchen wir es nicht erneut zu erwerben. Zweitens müssen wir sicherstellen, dass Sperren in demselben Knoten nicht miteinander in Konflikt geraten; drittens müssen wir Widerrufanforderungen von anderen Knoten verarbeiten, die ein Token brauchen, das aber mit einem Token in Konflikt steht, das wir gerade halten. Unser hier vorgestellter Sperralgorithmus löst diese Probleme wirksam.
  • Unser Sperralgorithmus liegt in Form von einer Gruppe von APIs vor. Zwei APIs werden zum Sperren und Entsperren eines Bytebereichs verwendet. Eine dritte API ist eine Rückruffunktion, die von dem Token-Verwaltungsprogramm aufgerufen wird. Es wird davon ausgegangen, dass das Token-Verwaltungsprogramm ebenfalls drei APIs bereitstellt. Eine API wird zum Erwerb des Bytebereich-Token benötigt ("Acquire"). Eine zweite API wird benötigt, um zu prüfen, ob ein Bytebereich-Token bereits in dem Knoten zwischengespeichert ist ("Test"). Eine dritte API wird benötigt, wenn ein Token als Reaktion auf einen Widerruf abgetreten wird ("Relinquish"). Zum Zweck des Zugriffs auf Bereiche in Dateien enthält jedes Token ein Spektrum (Anfang, Ende) des Bereichs der Datei, auf den es zugreifen kann.
  • Wir beschreiben nun die APIs des Token-Verwaltungsprogramms näher, bei denen es sich um eine Annahme handelt. Eine Erwerbsfunktion der Form
    Acquire(byte_range),
    die aufgerufen wird, um ein Bereichs-Token zu erwerben,
    und eine Widerruf-Rückruffunktion der Form
    Revoke(byte_range),
    die das Token-Verwaltungsprogramm immer aufruft, wenn ein anderes Token dieses Token braucht.
  • Folglich sollte der Knoten folgendes aufrufen:
    Relinquish(byte_range)
  • Der Algorithmus, den wir ausführen, beruht außerdem auf einer vierten Schnittstelle, die von dem Token-Verwaltungsprogramm bereitgestellt werden muss:
    Test(byte_range),
    die das Token-Verwaltungsprogramm auf das Vorhandensein des Token auf dem Knoten abfragt.
  • Um die Ausführung zu vereinfachen, überwachen wir nicht die Tokens, die wir halten; dies überlassen wir dem Token-Verwaltungsprogramm, und wir verwenden die Schnittstelle "Test", um abzufragen, ob der Erwerb eines Token erforderlich ist. Gewöhnlich müssen beim Erwerb eines Token Aktionen durchgeführt werden. Folglich ist es wünschenswert, zu wissen, ob ein Token bereits gehalten wir, so dass man sich diese Aktionen sparen kann.
  • Der Algorithmus beruht auf einer Sperrentabelle (Bereichssperrentabelle (Range lock table beziehungsweise RLT), die alle vorhandenen Sperren enthält. Die Tabelle wird von einer Mutex geschützt, um atomare Einfüge- und Löschoperationen von Sperren zu ermöglichen. Drei Hauptfunktionen werden dargelegt: LOCK, die einen Bytebereich sperrt; UNLOCK, die einen zuvor gesperrten Bereich entsperrt; und REVOKE, die eine Widerrufaufforderung verarbeitet.
  • Wir zeigen den Pseudocode für diese Schnittstellen auf:
    Figure 01100001
    Figure 01110001
    Figure 01120001
    Figure 01130001
  • Wir haben somit eine Bytebereich-Sperre beschrieben. Uns sind zwar keine Algorithmen für Bytebereich-Sperren bekannt, doch weisen wir darauf hin, dass bisherige Lösungen für Nicht-Bytebereich-Sperren eine Kopie der Token-Zustände außerhalb des Token-Verwaltungsprogramms aufbewahren würden.
  • Hier würden wir anmerken, dass unser verteiltes Token-Verwaltungsprogramm Schnittstellen (Acquire, Revoke, Relinquish und Test) bereitstellt, um Bereiche (d.h. Bytebereiche einer Datei) zu sperren. Ein bestimmter Bereich kann entweder in einer Betriebsart mit freigegebenem Lesezugriff oder mit exklusivem Schreibzugriff angefordert werden kann.
  • Eines der Merkmale unserer Erfindung ist, dass wir eine Token-Anforderung für einen angegebenen Bytebereich prüfen, um die Anforderung mit den vorhandenen in Konflikt stehenden Bereichen in dem gesamten Mehrknoten-System zu vergleichen und den größtmöglichen Bytebereich zu gewähren, der keinen Widerruf eines Token von einem anderen Rechner erforderlich macht. Dadurch wird die Wahrscheinlichkeit verringert, dass die nächste Operation auf dem anfordernden Knoten eine weitere Token-Anforderung notwendig macht. Zähler und Aufrufe von nicht blockierenden Sperren dienen zum Erwerb von Token, während andere Sperren geladen werden. Dieses Verfahren ermöglicht eine wirksamere Serialisierung bei mehreren Anforderungen in einem einzelnen Knoten, was die erforderliche Serialisierung von mehreren Knoten gestattet.
  • Es ist folglich vorgesehen, dass eine Betriebsart sowie zwei Bereiche, ein Bereich "benötigt" und ein Bereich "gewünscht", die Eingabe in die Schnittstelle "Acquire" des Token-Verwaltungsprogramms bilden. Der gewünschte Bereich muss eine Obermenge des benötigten Bereichs sein. Es ist gewährleistet, dass einer Anwendung, die die Schnittstelle "Acquire" aufruft, mindestens der benötigte Bereich gewährt wird. Das Token-Verwaltungsprogramm stellt fest, ob anderen Knoten in Konflikt stehende Bereiche (d.h. Bereiche, die den benötigten Bereich in einer Betriebsart überlappen, die einen Konflikt auslöst) gewährt wurden. Wenn Bereiche gefunden werden, die miteinander in Konflikt stehen, verlangt das Token-Verwaltungsprogramm, dass jeder Knoten, der einen Bereich hat, der mit anderen Bereichen in Konflikt steht, den überlappenden Bereich in eine konfliktfreie Betriebsart zurückstuft.
  • Wir sehen ferner vor, dass die Schnittstelle "Acquire" nach erfolgter Auflösung von Konflikten mit dem benötigten Bereich den größten zusammenhängenden Bereich ermittelt, der den benötigten Bereich vollständig abdeckt und auch eine Untermenge des gewünschten Bereichs darstellt. Dies ist der Bereich, den die Schnittstelle "Acquire" der aufrufenden Anwendung zurückschickt. Tatsächlich gewährt das Token-Verwaltungsprogramm den größtmöglichen Bereich (der durch den Parameter für den gewünschten Bereich begrenzt ist), bei dem keine zusätzliche Verarbeitung aufgrund eines Widerrufs durchgeführt werden muss.
  • Über die Schnittstelle "Revoke" des Token-Verwaltungsprogramms werden einer Anwendung Informationen über eine einen Konflikt auslösende Bereichsanforderung von einem anderen Knoten übermittelt. Wenn eine Anforderung von der Schnittstelle "Acquire" miteinander in Konflikt stehende Bereiche feststellt, die anderen Knoten gewährt wurden, verlangt sie, dass die Anwendung, die auf jedem der in Konflikt stehenden Knoten läuft, die Bereiche zurückstuft, die ihnen gewährt wurden. Zu den Informationen, die über die Schnittstelle "Revoke" übergeben werden, gehören die Betriebsart sowie die benötigten/gewünschten Bereiche, die in dem Aufruf der Schnittstelle "Acquire" genannt wurden.
  • Nach dem Empfang einer Widerrufanforderung ruft eine Anwendung die Schnittstelle Relinquish auf, um miteinander in Konflikt stehende Bereiche zurückzustufen, die sie einer konfliktfreien Betriebsart gewährt hat. Die Anwendung muss zumindest Bereiche, die mit dem "benötigten" Bereich in Konflikt stehen, in eine konfliktfreie Betriebsart zurückstufen, kann aber auch einen größeren Bereich zurückstufen, wenn sie möchte.
  • Das Token-Verwaltungsprogramm stellt auch eine Schnittstelle "Test" bereit, die feststellt, ob dem lokalen Knoten ein bestimmter Bereich gewährt worden ist. Diese kann von einer Anwendung zur Feststellung verwendet werden, ob eine Anforderung von der Schnittstelle "Acquire" für einen bestimmten Bereich eine Datenübertragungsanforderung an den Token-Serverknoten erforderlich macht.
  • Indem bei der Verarbeitung Folgenummern für einen bestimmten Bytebereich verwendet werden, ermöglichen wir eine korrekte Verarbeitung von Erwerb- und Widerrufoperationen in denselben Bytebereichen. Die Schnittstelle "Acquire" des Token-Verwaltungsprogramms verwendet eine Folgenummer als Argument. Bei jedem Token verwaltet das Token-Verwaltungsprogramm eine Folgenummer für jeden Knoten, dem ein Bereich gewährt wurde. Das Token-Verwaltungsprogramm aktualisiert das Feld, das die Folgenummer eines Knotens enthält, bei Abschluss einer Erwerbsoperation "Acquire" mit dem in der Schnittstelle "Acquire" angegebenen Wert. Wenn eine nachfolgende Erwerbsoperation "Acquire" Bereiche von Knoten widerrufen muss, die miteinander in Konflikt stehen, übergibt das Token-Verwaltungsprogramm die Folgenummer der letzten erfolgreichen Erwerbsoperation von diesem Knoten über die Schnittstelle "Revoke" des Token-Verwaltungsprogramms.
  • Im Hinblick auf die Schnittstellen zu dem verteilten Token-Verwaltungsprogramm (Acquire, Revoke, Relinquish, Test) haben wir ein verbessertes Verfahren zur Realisierung von lokalen Sperren auf Bytebereiche in dem verwendeten Code vorgesehen. Mehrere mögliche Schwierigkeiten lassen sich mit Hilfe dieser Programm-Verfahren oder -Algorithmen elegant lösen, während gleichzeitig die Ausführung einiger anspruchsvoller Funktionen ermöglicht wird:
    Wir verarbeiten mehrere Token-Erwerb- und -Widerruffunktionen parallel unter Verwendung der nachstehend beschriebenen Sperrverfahren mit dem Pseudocode in der ursprünglichen Offenlegung. Wir gestatten die parallele Verarbeitung von mehreren Token-Erwerboperationen. Dieser Fall kann zum Beispiel eintreten, wenn mehrere Operationen des Dateisystems versuchen, auf verschiedene Abschnitte einer Datei parallel zuzugreifen.
  • Und wir gestatten es, dass der Widerruf eines Token für einen Teil einer Datei gleichzeitig mit dem Erwerb eines Token stattfinden kann, solange die beiden Operationen nicht miteinander in Konflikt stehen.
  • Man wird als vorteilhaft erkennen, dass wir keine Kopie des lokalen Token-Zustands im Sperrcode des Bytebereichs hinterlegen müssen.
  • Wir schließen eine Situation einer dauerhaft blockierten Aktivität (livelock) aus, bei der ein Token kurz nach seinem Erwerb, aber bevor es gesperrt wird, von einem anderen Knoten widerrufen wird. Der andere Knoten erwirbt das Token, und bevor es gesperrt wird, wird es erneut gestohlen. Durch diesen Ping-Pong-Effekt ist kein Weiterkommen möglich.
  • Als Folge, dass wir keine Kopie des lokalen Token-Zustands in dem Bytebereich-Sperrcode hinterlegen müssen, nimmt nun der Speicherbedarf unseres Programms ab, da diese Information bereits im Token-Verwaltungsprogramm gespeichert ist. Eine API fragt das Token-Verwaltungsprogramm an, um herauszufinden, ob das Token bereits im Cachespeicher abgelegt ist. Nach erfolgter Sperrung des Bytebereichs wird ein spezieller Mechanismus bereitgestellt, der dazu dient, sicherzustellen, dass das Token nicht widerrufen worden ist, nachdem eine Prüfung auf das Vorhandensein des Token stattgefunden hat und bevor es gesperrt wurde. Es ist möglich, dass das Token zwischen diesen beiden Vorgängen widerrufen wurde. In diesem Fall erwerben wir das Token und versuchen es erneut.
  • Derselbe Bytebereich-Sperrcode, der von den Operationen des Dateisystems verwendet wird, wird auch von der Widerruf-Rückruffunktion verwendet. Eine spezielle Markierung hat jedoch erkennen lassen, dass dies eine Sperre für einen Widerruf (lock-for-revoke) ist. Dadurch wird der Code kompakter und erlaubt die Verwendung derselben Sperrentabellen. Die API, die zum Sperren eines Bytebereichs verwendet wird, unterstützt verschiedene Optionen, die zu einer Verbesserung ihres Betriebs beitragen. Non-blocking (blockierungsfrei); Local-lock (lokale Sperre); Test; und Sequential (sequenziell). Die Option "non-blocking" ermöglicht einen blockierungsfreien Betrieb; wenn wir das Token nicht haben oder wenn eine Sperre, die einen Konflikt auslöst, gehalten wird, kehrt der Sperrcode sofort mit einem entsprechenden Rückkehrcode zurück.
  • Die Option "non-blocking" ermöglicht einen blockierungsfreien Betrieb; wenn wir keine globale Sperre, sondern nur eine Sperre innerhalb des Knotens setzen müssen, können wir diese Option verwenden.
  • Die Option "Test" ermöglicht die Feststellung, ob wir den Bytebereich sperren können, ohne jedoch wirklich eine Sperre zu setzen.
  • Die Option "sequential" gibt einen Hinweis darauf, dass wir einen Bytebereich für eine Leseoperation (oder eine Schreiboperation) in eine(r) Datei sperren, auf die sequenziell zugegriffen wird. Dieser Hinweis wird verwendet, wenn ein Token gebraucht wird. In diesem Fall wird ein Token, das größer als das Token ist, das wirklich gebraucht wird, gewünscht (jedoch ist es nicht notwendig).
  • Besondere Vorkehrungen werden getroffen, um die verschiedenen Sperren, die von den Threads gehalten werden, zu überwachen. Ein Dienstprogramm zur Fehlerbehebung gibt die vorhandenen Bytebereich-Sperren und die Nummern der Threads aus, wo sie gehalten werden. Zudem werden Statistiken geführt, damit sich die Dateizugriffsmuster und die Verhaltensmuster von Sperren verstehen lassen.
  • Dadurch, dass für jede erfolgreiche Sperroperation eine Kennung zurückgeliefert wird, ist eine Entsperroperation sehr schnell und erfordert weder eine Such- noch eine Nachschlageoperation.
  • Indem man Zähler für die verschiedenen vorhandenen Sperrbetriebsarten unterhält, ist die Operation, die prüft, ob eine Sperre vorhanden ist, die einen Konflikt auslöst, schnell. Wenn wir beispielsweise einen Zähler für die Anzahl der aktiven Sperren für freigegebene Leseoperationen und der aktiven Sperren für exklusive Schreiboperationen unterhalten, können wir in vielen Fällen wissen, ob wir eine Prüfung auf das Überlappen von Bereichen durchführen müssen. Wenn es zum Beispiel keine Sperren für exklusive Schreiboperationen gibt und wir eine Sperre für eine freigegeben Leseoperation benötigen, wissen wir, dass es keinen Konflikt gibt, und wir müssen lediglich einen freien Platz in der Sperrentabelle finden.
  • Der Sperrcode bietet Unterstützung für eine unbegrenzte Anzahl von Anforderungen für Bytebereich-Sperren. Falls die Sperrentabelle voll wird oder eine Sperre angefordert wird, die einen Konflikt auslöst, wird der Thread, der um die Sperre bittet, in den Ruhemodus versetzt und erst aufgeweckt, wenn eine Sperre entsperrt wird.
  • Bei unserer Lösung werden die Informationen über das Token nicht dupliziert, und sie ist folglich kompakt und leistungsfähig.
  • Wiederherstellung in der Umgebung eines Token-Verwaltungsprogramms
  • Die Schwierigkeiten des parallelen Dateisystems sind gewaltig, da mehrere Prozessoren aus verschiedenen Teile des Dateisystems in jeder Instanz lesen und in sie schreiben. Es stellt sich die Frage, was geschieht, wenn es in dieser Umgebung zu einem Ausfall kommt. Wir ermöglichen eine Wiederherstellung in dieser Umgebung. Der erste Wiederherstellungsmechanismus betrifft die Frage, was geschieht, wenn ein Knoten ausfällt und die Metadaten zum Zeitpunkt des Ausfalls gerade aktualisiert werden. Er beschreibt ein Verfahren, das die Wiederherstellung des Zustands des Token, die Wiedergabe von Aufzeichnungen der Metadaten und eine strenge Festlegung der Reihenfolge von Operationen beinhaltet.
  • Wiederherstellungsmodell für das parallele Dateisystem
  • Unser Wiederherstellungsmodell kann auf unser Dateisystem mit gemeinsam benutzten Platten angewendet werden. Die Platten sind entweder über mehrere Plattenkabel (z.B. SCSI oder SSA) oder in Form eines an das Netzwerk angeschlossenen Speichers angeschlossen. Jeder Prozessor hat unabhängigen Zugriff auf die Platte, und die Übereinstimmung der Daten/Metadaten wird über die Verwendung eines verteilten Sperrenverwaltungsprogramms gewahrt. Aktualisierungen der Metadaten werden von jedem Prozessor unabhängig protokolliert, um bei einem Ausfall ein Absuchen des Dateisystems zu verhindern.
  • Die Schwierigkeit des Problems besteht darin, dass Prozessoren ausfallen können (entweder Software oder Hardware). Diese Ausfälle können in Form eines tatsächlich katastrophalen Ausfalls des Prozessors oder eines Ausfalls der Datenübertragungsfunktion zur Teilnahme an dem Sperrenverwaltungsprotokoll bestehen. Während dieser Ausfälle kann der fehlerhafte Prozessor Sperren halten, die es ihm ermöglichen, bestimmte Bereiche der gemeinsam benutzten Platten zu ändern. In Abhängigkeit von der Topologie des Sperrenverwaltungsprogramms kann er sogar in der Lage sein, weitere Sperren zu erwerben. Der fehlerhafte Prozessor erkennt schließlich seinen Zustand, aber der Zeitpunkt, zu dem dies geschieht, ist extern nicht erkennbar, da dies davon abhängt, was im Innern des fehlerhaften Prozessors geschieht.
  • Die Zielsetzung besteht darin, allen nicht ausgefallenen Prozessoren eine sichere Ausführung ihrer Operationen unter Verwendung der gemeinsam benutzten Platte zu gestatten und es dem fehlerhaften Prozessor zu ermöglichen, ebenfalls Unterstützung zur Nutzung von Anwendungen bereitzustellen, sobald er in einen bekannten Zustand zurückkehren kann.
  • Ein Wiederherstellungsmodell setzt die folgenden Konzepte um:
    • – Einen Gruppenüberwachungsdienst (wie Phoenix-Gruppendienste), der Prozesse auf allen der Prozessoren überwacht und Ausfälle von Prozessoren und Ausfälle der Datenübertragung erkennt. Dieser Dienst wird bereitgestellt, indem "Prozessgruppen" zusammengeführt werden; alle Mitglieder einer Gruppe werden informiert, wenn ein Mitglied ausfällt oder wenn ein neuer Prozess versucht, sich einer Gruppe anzuschließen. Während der Startzeit müssen sich Prozessoren den "Prozessgruppen" anschließen. – Verteiltes Sperren. Alle Plattenzugriffe werden unter den Gruppenmitgliedern über verteiltes Sperren koordiniert: – Ein Mitglied muss eine Sperre abrufen, bevor es einen bestimmten Teil der Daten/Metadaten auf einer gemeinsam benutzten Platte lesen oder ändern kann.
    • – Ein Gruppenmitglied ist ein Sperrenkoordinator; der Sperrenkoordinator weiß, welche Sperren auf welchem Knoten gehalten werden könnten. – Beschlussfähigkeit. Während des Starts und bei Datenübertragungsausfällen ist es möglich, dass sich mehr als eine Gruppe bildet. Dies könnte dazu führen, dass es in verschiedenen Gruppen Sperrkoordinatoren gibt, die Sperrentscheidungen treffen, welche miteinander in Konflikt stehen. Um dies zu verhindern, werden Dateisystem-Operationen untersagt, wenn weniger als die Mehrzahl der Prozessoren, die auf die Platte zugreifen können, Mitglieder einer "Prozessgruppe" sind. – Aufzeichnen. Alle Aktualisierungen der Daten/Metadaten, die nach einem Ausfall zu Unstimmigkeiten führen könnten, werden aufgezeichnet. Jeder Prozessor hat sein eigenes Protokoll, aber die Protokolle werden auf einer gemeinsam benutzten Platte gespeichert, so dass im Falle eines Ausfalls alle Knoten auf die Protokolle zugreifen können. – Abschirmen. Es muss eine Funktion geben, mit der sich der Zugriff eines bestimmten Prozessors auf eine bestimmte Platte blockieren lässt. – Barrieren. Da die Schritte der Wiederherstellung zwangsläufig in Folge durchgeführt werden und bestimmte Schritte der Wiederherstellung auf allen Knoten durchgeführt werden müssen, werden "Barrieren" verwendet, um sicherzustellen, dass ein Schritt auf allen Knoten abgeschlossen ist, bevor irgendwo der nächste Schritt ausgeführt wird.
  • Unser Wiederherstellungsmodell handhabt Knotenausfälle, ohne die Hardware zu sperren. Jede Instanz des Dateisystems funktioniert nur, wenn sie ein aktives Mitglied einer "Prozessgruppe" sein kann. Wenn der Ausfall eines Prozessors festgestellt wird, der einen tatsächlichen Ausfall eines Prozessors oder die Unfähigkeit, seine funktionierenden Zustand zu übermitteln, darstellen kann, werden alle verbleibenden Gruppenmitglieder von dem Gruppenüberwachungsdienst informiert. Die Wiederherstellung des ausgefallenen Prozessors geschieht, indem die nachstehend beschriebenen Wiederherstellungsschritte durchgeführt werden, wobei unter den nicht ausgefallenen Gruppenmitgliedern ein Barrierensynchronisationsprotokoll verwendet wird. Da ein Teil der Wiederherstellungsschritte auf einem Prozessor durchgeführt wird, wird zu ihrer Durchführung ein Dateisystem-Koordinator gewählt.
    • – Alle nicht ausgefallenen Prozessoren beenden den Datenaustausch mit dem ausgefallenen Prozessor.
    • – Der Dateisystem-Koordinator schirmt den ausgefallenen Prozessor ab. Dies bewirkt, dass das Platten-Teilsystem keine weiteren Plattenanforderungen von dem ausgefallenen Prozessor mehr erfüllt. Der ausgefallene Prozessor kann nicht auf die gemeinsam benutzte Platte zugreifen, selbst wenn er den Datenübertragungsausfall noch nicht festgestellt hat.
    • – Die nächste Barriere stellt die Wiederherstellung des Sperrzustands dar, sofern dies notwendig ist. Der Dateisystem-Koordinator informiert den Sperrenkoordinator. Der Sperrenkoordinator stellt die Bewilligung von Sperren ein, die von dem ausgefallenen Prozessor zum Zeitpunkt des Ausfalls gehalten werden. Dadurch wird verhindert, dass andere Knoten auf Daten zugreifen, die von dem ausgefallenen Knoten möglicherweise in einem nicht konsistenten Zustand zurückgelassen wurden. Wenn der ausgefallene Prozessor der Sperrenkoordinator war, wird der neue Sperrzustand von einem alternativen Koordinator berechnet, indem dieser die im Cachespeicher abgelegten Informationen über den Zustand der Sperre von den nicht ausgefallenen Prozessoren erfasst. Wenn diese Phase nicht erforderlich war, können normale Operationen des Dateisystems für Daten, die nicht unter die Sperren fallen, deren Bewilligung ausgesetzt wurde, auf den nicht ausgefallenen Knoten wieder aufgenommen werden.
    • – Die dritte Barriere stellt die Wiedergabe des Protokolls des ausgefallenen Knotens durch den Dateisystem-Koordinator dar. Diese Wiedergabe erfolgt mit dem Wissen, dass der ausgefallene Prozessor von den Platten abgeschirmt ist und die nicht ausgefallenen Prozessoren keine Sperren gewähren, die blockiert sind. Bei Beendigung dieses Schritts stimmen die Daten auf der Platte überein, und die Sperren können freigegeben werden. Eine Befreiung von dieser Barriere schließt eine erfolgreiche Wiederherstellung ein, und der normale Betrieb kann auf allen nicht ausgefallenen Prozessoren wieder aufgenommen werden.
    • – Prozessorausfälle, die während der Wiederherstellung festgestellt werden, werden behandelt, indem wieder ganz von vorne angefangen wird. Die einzelnen Wiederherstellungsschritte werden so durchgeführt, dass sie idempotent sind, so dass es nichts ausmacht, wenn sie mehrmals ausgeführt werden, bis das Wiederherstellungsprotokoll ohne weitere Ausfälle vollständig ausgeführt ist.
  • Die vorstehenden Wiederherstellungsschritte beschreiben die Wiederherstellung bei einem Dateisystem, und wenn mehr als ein Dateisystem installiert ist, werden alle Wiederherstellungsmaßnahmen in jedem Schritt auf alle Dateisysteme angewandt.
  • Zur Durchführung der Wiederherstellung eines Knotens versucht der ausgefallene Prozessor, der Gruppe wieder beizutreten, sobald ihm dies möglich ist. Wenn die Wiederherstellung nach einem Ausfall immer noch im Gang ist, kann er sich der "Prozessgruppe" erst anschließen, wenn das Fehlerbehebungsprotokoll vollständig ausgeführt ist. Zwei Wege sind möglich, entweder der ausgefallene Knoten schließt sich einer vorhandenen Gruppe an oder er schließt sich einer Gruppe an, die auf die Beschlussfähigkeit wartet. Wenn er sich einer Gruppe anschließt, die auf die Beschlussfähigkeit wartet, erfolgt die Wiedergabe des Protokolls, sobald die Beschlussfähigkeit vorhanden ist (es ist dann bekannt, dass keine in Konflikt stehenden Sperren vorhanden sind). Wenn er sich einer vorhandenen Gruppe anschließt, hebt er seine Abschirmung auf und ermöglicht normale Dateisystem-Operationen.
  • Die zweite der Wiederherstellungsfunktionen behandelt den Punkt, an dem sich die Wiederherstellung und die Erfordernis von Metadaten-Knoten überschneiden. Die Metadaten-Knoten befinden sich in einem Zustand, der über einen Ausfall erhalten werden muss.
  • Synchrone und asynchrone Übernahme des Metadaten-Knotens
  • Unser paralleles Dateisystem funktioniert in den Fällen, in denen alle Platten, die das Dateisystem bilden, in einem Datenübertragungsnetzwerk wie zum Beispiel in einem TCP/IP-Netzwerk oder in einer Vermittlungsstelle verteilt sind, das beziehungsweise die es mehreren Prozessoren erlaubt, interaktiv miteinander zu kommunizieren, wie es bei einem massiv parallelen Rechner oder einem Rechnerverbund (Cluster) der Fall ist, und folglich muss und kann der Zugriff auf eine Datei unabhängig durch mehrere Prozessoren erfolgen. Um diese Fähigkeit nutzen zu können, sollte eine Datei von mehreren Prozessoren sowohl für Lese- als auch Schreiboperationen gemeinsam benutzt werden.
  • Die Freigabe von Dateien für Schreiboperationen in einem verteilten Dateisystem ist mit mehreren Problemen verbunden. Eines davon ist der Zugriff und die Aktualisierung der von uns bereitgestellten Metadaten. Unser Metadaten-Knoten ist ein Mechanismus zur Steuerung von Metadaten in einem verteilten Dateisystem. Jeder Knoten, der auf eine Datei zugreift, muss Metadaten-Informationen von dem Metadaten-Knoten (oder Metaknoten) lesen oder auf ihn schreiben.
  • Der Metadaten-Knoten hält die Informationen über die Metadaten der Datei fest und hat die Funktion eines intelligenten Cachespeichers zwischen der Platte und all den Knoten, die auf die Datei zugreifen. Es gibt Situationen, in denen der Metadaten-Knoten (oder Metaknoten) die Ausführung dieser Funktion einstellt. Um einen reibungslosen Betrieb und eine problemlose Wiederherstellung zu ermöglichen, müssen diese Situationen behandelt werden. Knoten, die gewöhnlich auf den Metaknoten zugegriffen haben, müssen auf direktem Weg einen neuen Metaknoten wählen.
  • Wir beschreiben hiermit die Situation, die die Übernahme eines Metaknotens auslösen kann, und das Verfahren, das wir wählen, um eine Übernahme zu ermöglichen.
  • Es gibt drei Situationen, in denen ein Metaknoten seine Funktion als ein Metaknoten einstellt; die ersten beiden sind asynchron, d.h., andere Knoten sind sich dessen nicht sofort bewusst. Die dritte ist synchron, d.h., alle Knoten sind sich der Übernahme bewusst.
    • 1. Der Metaknoten fällt aus (stürzt ab);
    • 2. Der Metaknoten schließt die Datei oder löscht sie aus dem Cachespeicher;
    • 3. Ein anderer Knoten muss der Metaknoten werden.
  • In allen drei Fällen müssen wir sicherstellen, dass eine zuverlässige Übernahme stattfindet. Bei asynchronen Operationen stellt der erste Knoten, der versucht, auf den alten Metaknoten zuzugreifen, einen Fehler fest; entweder ist der Knoten abgestürzt, wobei er in diesem Fall eine Übertragungsfehler-Meldung erhält, oder der alte Knoten hat entschieden, dass er nicht länger als Metaknoten fungiert, wobei der Knoten in diesem Fall eine entsprechende Fehlermeldung von dem alten Metaknoten erhält. In beiden Fällen versucht der Knoten, ein Metaknoten zu werden, indem er ein entsprechendes Token vom Token-Verwaltungsprogramm anfordert. Wenn es keinen anderen Metaknoten gibt (dies ist der Fall, wenn er der erste war, der auf den alten Metaknoten zugriff), wird der Knoten der neue Metaknoten. Andere Knoten, die anschließend versuchen, auf den alten Metaknoten zuzugreifen, durchlaufen ebenso den gleichen Prozess, aber es gelingt ihnen nicht, das entsprechende Token zu erwerben. Eine Anfrage an das Token-Verwaltungsprogramm legt den neuen Metaknoten offen. Folglich findet schließlich jeder Knoten heraus, dass er entweder der neue Metaknoten geworden ist oder dass sich der Metaknoten geändert hat. In beiden Fällen werden geeignete Maßnahmen durchgeführt. Wenn ein Knoten ein Metaknoten wurde, liest er die neuesten Metadaten von der Platte. Wenn sich der Metaknoten eines Knotens geändert hat, sendet der Knoten die Aktualisierungen seiner eigenen Metadaten erneut an den neuen Metaknoten, da es möglich ist, dass der alte Metaknoten ausgefallen ist, bevor er diese Aktualisierungen auf die Platte geschrieben hat. Durch die Verwendung einer Versionsnummer für eine jede dieser Aktualisierungen weiß jeder Knoten, welche Aktualisierungen sich auf der Platte befinden und welche erneut an den neuen Metaknoten gesendet werden müssen.
  • Da ein Knoten abstürzen kann, während er versucht, ein Metaknoten zu werden, ist jede Operation, die den Zugriff auf den Metaknoten beinhaltet, wie folgt aufgebaut:
  • TABELLE 7
  • Wiederholungsversuch:
    Figure 01290001
    Figure 01300001
  • Unser beschriebenes System für eine dynamische Übernahme von Metaknoten ist bisher einmalig, und unsere spezielle Lösung hat den Vorteil, dass sie ein Teilsystem nutzt, das weitere Verwendungsmöglichkeiten (das Token-Verwaltungsprogramm) zur Auswahl eines neuen Metaknotens auf der Grundlage der Dateiaktivität bietet. Da alle Operationen einen naturgegebenen "Wiederholungs"-Mechanismus beinhalten und da jeder Knoten die Funktion eines Metaknotens übernehmen kann, wird schließlich ein Metaknoten gewählt, und folglich ist sichergestellt, dass schlussendlich in dynamischer Weise eine Übernahme stattfinden wird.
  • Die in jedem Knoten hinterlegten Informationen stellen sicher, dass der Wiederherstellungsprozess selbst bei Ausfall eines Metaknotens alle Informationen in einer Weise wiederherstellen wird, dass eine übereinstimmende Ansicht der Datei zur Verfügung steht.
  • Zuteilung von Quoten
  • Als Nächstes erörtern wir unsere Verbesserungen, zu denen die Zuteilung von Quoten in diesem Dateisystem mit gemeinsam benutzten Platten gehört. Die grundlegende Schwierigkeit besteht darin, dass Quoten über eine Gruppe von Knoten strikt eingehalten werden müssen. Zwar könnte man sich vorstellen, dass sie an einem zentralen Server verwaltet werden, doch haben wir festgestellt, dass dies keine brauchbare Lösung ist, da der zentrale Server zu einem Engpass werden würde, da jede neue Schreiboperation von Daten diesen einzigen Server um Erlaubnis bitten müsste, bevor sie die Daten schreiben könnte. Hier beschreiben wir unser Verfahren zur Zuteilung von Quotenanteilen zu Rechnern, die im Namen eines Benutzers, der über eine Quote verfügt, aktiv Daten in ein Dateisystem schreiben. Später befassen wir uns mit Möglichkeiten, einen solchen Anteil im Falle eines Ausfalls wiederherzustellen.
  • In einem parallelen Dateisystem, in dem mehrere Prozessoren auf alle Platten, die das Dateisystem bilden, unabhängig voneinander zugreifen können, um aktiv Daten in die Dateien auf den verschiedenen Platten zu schreiben und sie daraus zu lesen, muss die jeweilige Nummer der Sektoren einer Platte den Dateien auf jedem Prozessor zugeordnet werden, der die Dateien erstellt. Die Sektoren, die Dateien zugeordnet werden, welche einem bestimmten Benutzer gehören, sind durch eine Quote begrenzt, die festlegt, wie viel Plattenbereich dieser Benutzer oder diese Gruppe von Benutzern in Anspruch nehmen darf. Das Problem ist, dass Benutzer auf mehreren Prozessoren gleichzeitig Operationen ausführen können und dieselbe Quote belasten. Die Zentralisierung der Zuordnung von neuen Plattenblöcken verlangsamt die Nutzung unseres massiv parallelen Verarbeitungssystems.
  • Wir haben ein System realisiert, das jedem Knoten Anteile an der Quote zuteilt, sie auf der Grundlage der Nachfrage neu zuteilt und sie bei Ausfällen wiederherstellt. Unsere Lösung besteht in einem Verfahren zur Verwaltung von Inode- und Plattenblockquoten pro Dateisystem in einer massiv parallelen Datenverarbeitungsumgebung oder einer anderen Umgebung mit mehreren Rechnern, die wir beschreiben. Die Arbeit wird zwischen einem Quoten-Server je Dateisystem und einem Quoten-Client je Knoten pro Dateisystem aufgeteilt, der aktiv Operationen an Daten in dem Dateisystem ausführt.
  • Eine Quotengrenze ist ein Schwellenwert, bis zu dem ein Benutzer Inodes oder Speicherplatz in dem Dateisystem zuordnen darf. In dieser Schrift wird die Anzahl der Inodes und der Speicherbereich, der einem Benutzer zugeteilt werden darf, als Quote bezeichnet. Ein lokaler Anteil ist der Speicherbereich, der zugunsten eines Benutzers auf einem Quoten-Client ohne Dialogverkehr mit dem Quoten-Server zugeordnet werden kann.
  • Der Server verwaltet eine auf der Platte befindliche Datei, die den Quotengrenzwert s und die kumulierte Nutzung für alle Benutzer in dem gesamten MPP-System enthält. Diese Werte stehen nur auf dem Server zur Verfügung, der alle Lese- und Aktualisierungsoperationen für diese Datei für alle Prozessoren durchführt. Folglich hat nur der Server einen Gesamtüberblick über die Quotenauslastung und den noch zur Zuordnung verfügbaren Speicherbereich.
  • Alle Maßnahmen in Bezug auf die gesamte Quotenverwaltung werden auf dem Quotenserver durchgeführt. Änderungen an den Grenzwerten, die Zuordnung des lokalen Anteils und die Anzeige des aktuellen Status erfordern einen Dialogverkehr mit dem Quoten-Server. Quoten-Clients nehmen Änderungen an den Dateisystem-Zuordnungen entsprechend ihrem zugebilligten lokalen Anteil vor und aktualisieren den Server in regelmäßigen Abständen auf der Grundlage der Inanspruchnahme dieses Anteils. Der Server kann einen dem Client zugebilligten Anteil widerrufen, um Anforderungen für eine gemeinsame Benutzung von einem anderen Client zu erfüllen.
  • Quoten-Clients beginnen mit einem lokalen Anteil von null. Nur wenn eine Anwendung auf dem Prozessor versucht, neue Dateisystem-Daten zu erzeugen, wird ein lokaler Anteil für den Benutzer angefordert. Nur wenn der Client einen angemessenen lokalen Anteil erhält, ist die Anforderung der Anwendung erfüllt; andernfalls gilt die Anforderung der Anwendung als nicht erfüllt. Der Quoten-Client führt ein Protokoll über den lokalen Anteil und über die Menge, die davon in Anspruch genommen wurde. Anwendungen, die Speicherplatz auf der Platte freigeben, erhöhen den lokalen Anteil für den Benutzer. Der Quoten-Client aktualisiert seinen Nutzungsgrad in regelmäßigen Abständen auf dem Quoten-Server und gibt einen überschüssigen Quotenanteil auf der Grundlage von Anwendungs-Nutzungsmustern frei.
  • Der Quoten-Server gibt lokale Anteile aus, solange er noch über Quoten verfügt, d.h., solange der systemweite Quotengrenzwert nicht überschritten wird. Wenn die gesamte begrenzte Quote in Form von lokalen Anteilen verteilt wurde, widerruft der Quoten-Server lokale Anteile, um neue Anforderungen zu erfüllen. Dies geschieht, indem ein Teil der lokalen Anteile widerrufen wird, wobei der Client den verbleibenden Anteil weiterhin nutzen darf. Diese Anforderungen werden immer fordernder, wobei immer größere Teile des lokalen Anteils widerrufen werden, bis keine Quote mehr verfügbar ist, um Anforderungen zu erfüllen, was dazu führt, dass Anforderungen von Anwendungen abgewiesen werden.
  • Die Schwierigkeit bei diesem Verfahren besteht darin, dass es Ausfälle sowohl von Clients als auch von Servern berücksichtigen muss. Clients können mit teilweise verwendeten lokalen Anteilen ausfallen, und der Server kann gleichzeitig mit einem ausfallenden Client ausfallen. Der Benutzer darf die zugewiesene Quote nie überschreiten, und er geht auch davon aus, dass er diese Speicherkapazität zugeteilt bekommen kann. Dies setzt die Anwendung des "Zweifelhaft"-Verfahrens ("in-doubt") der Quotenzuteilung voraus. Jedes Mal, wenn der Quoten-Server einen lokalen Anteil zuteilt, wird ein Datensatz über die Summe der lokalen Anteile, der "Zweifelhaft"-Wert ("in-doubt value"), auf der wiederherstellbaren Platte gespeichert. Dies stellt die Kapazität des Quoten-Speicherbereichs dar, über den der Server keine genauen Informationen hat. Der zweifelhafte Speicherbereich ("in-doubt space") kann nicht neu zugeordnet werden, ohne dass die Gefahr besteht, dass einem Benutzer gestattet wird, seine Grenzwerte zu überschreiten. Die "Zweifelhaft"-Werte werden von den Clients mit regelmäßig versandten Nachrichten aktualisiert, um ihre Nutzung des lokalen Anteils anzuzeigen. Dieser Speicherbereich ändert seinen Status von "zweifelhaft" in "belegt". Bei Speicherbereich, der von einem Client abgetreten wird, wird der "Zweifelhaft"-Wert auch verringert. Der gesamte Speicherbereich, der zugeordnet werden kann und für einen Benutzer zur Verfügung steht, ist seine Zuteilung abzüglich des Speicherbereichs, der als belegt bekannt ist, abzüglich des Speicherbereichs, der als zweifelhaft gilt. Alle Änderungen an dem "Zweifelhaft"-Wert werden unverzüglich gezwungenermaßen auf die Platte geschrieben, um eine Wiederherstellung auszuführen.
  • Wenn ein Client ausfällt, steht der Speicherbereich, der als zweifelhaft gilt, einem Benutzer erst zur Verfügung, wenn ein Dienstprogramm "Quotenprüfung" ("quota check") ausgeführt wird, das die tatsächliche Belegung des Speichers durch diesen Benutzer prüft. Ein Teil des "Zweifelhaft"-Werts stellt die tatsächliche Nutzung durch den Benutzer dar; aber ein Teil stellt die mögliche Nutzung dar, die vorübergehend verloren geht. Der Algorithmus zur Zuteilung von Anteilen ist empfindlich für die Nutzung von neuem Plattenspeicher am Client und versucht, dem Client aus Gründen der Leistungsfähigkeit das zu geben, was er bald in Anspruch nehmen wird, und einen überschüssigen lokalen Anteil aus Wiederherstellungsgründen zu begrenzen. Dieses Verfahren ermöglicht die Fortsetzung des Betriebs durch den Benutzer in Abhängigkeit von dem Teil seiner Quote, der nicht zweifelhaft ist, bis das Dienstprogramm zur Quotenprüfung ausgeführt wird.
  • Es ermöglicht auch die parallele Zuordnung von Plattenblöcken im Interesse einer höheren Leistungsfähigkeit.
  • Wenn der Quoten-Server ausfällt, wird ein neuer Quoten-Server gewählt. Er verfügt über keine Informationen über Änderungen, die noch nicht auf die Platte geschrieben worden sind. Er erzeugt diese Informationen, indem er alle lokalen Anteile widerruft und zweifelhafte Werte auf der Grundlage der Antworten aktualisiert. Es sei angemerkt, dass Ausfälle von Clients, die gleichzeitig mit dem Ausfall des Servers stattfinden, dazu führen, dass Blöcke verloren gehen, bis das Dienstprogramm zur Quotenprüfung ausgeführt wird. Dieser Algorithmus ermöglicht es, dass Quoten bei nicht zweifelhaften Zuteilungen nach einem Ausfall korrekt und schnell durchgesetzt werden können.
  • Uns ist kein paralleles Dateisystem bekannt, das Plattenblöcke unabhängig auf allen Knoten eines parallelen Systems zuordnet. Das bedeutet, dass kein anderer mit dem Problem konfrontiert wird, bis er es mit an das Netzwerk angeschlossenen Speichersystemen versucht.
  • Aus Gründen der Leistungsfähigkeit ordnen wir parallel Speicher zu. Jede Lösung mit einem Zuordnungsserver hätte Engpässe und Probleme mit der Wiederherstellung. Wir müssen eine Quote haben, da Benutzer die Belegung von Plattenspeicher über das gesamte Parallelverarbeitungssystem steuern möchten. Die Lösung gestattet eine parallele Zuordnung, erzwingt kein dauerndes Sperren einer globalen Quote, was den Betrieb verlangsamen würde, und ermöglicht die rechtzeitige Wiederherstellung nach Verarbeitungsfehlern.
  • Jedes Parallelverarbeitungssystem, das ein Modell mit gemeinsam benutzten Platten verwendet, bei dem die Platten miteinander verbunden sind, kann diese Entwicklung nutzen.
  • Wiederherstellung von lokalen Anteil zur Quotenverwaltung bei der Parallelverarbeitung
  • Dieser Abschnitt beschreibt die Funktionsweise unseres Dienstprogramms zur Quotenprüfung in dieser Umgebung. Die Funktionen der Quotenprüfung sind ähnlich denen von Quotachk, einem Standarddienstprogramm zur Festlegung von Quotendateien nach einem Ausfall in einer Unix-Betriebsumgebung, jedoch kann Quotachk nicht mit mehreren Knoten ausgeführt werden, die Quoten freigeben, wie in der älteren Erfindung beschrieben wurde. Unsere Entwicklung ermöglicht die Ausführung eines "Quotachk"-Programms, ohne dass alle Rechner, die auf die Daten zugreifen, heruntergefahren werden müssen.
  • Dieser Abschnitt beschreibt ein Dienstprogramm/Verfahren, das Anteile widerruft, wenn nach einem Ausfall nicht bekannt ist, ob diese Anteile in Anspruch genommen werden/zugeteilt sind oder ob sie noch verfügbar sind. Das Dienstprogramm arbeitet, ohne dass es Benutzer an der Zuordnung oder der Aufhebung der Zuordnung von Speicherbereich auf der Platte in dem Dateisystem hindert.
  • Zur Verwaltung von Inode- und Plattenblock-Quoten je Dateisystem in einer massiv parallelen Datenverarbeitungsumgebung wird die Arbeit zwischen einem Quoten-Server je Dateisystem und einem Quoten-Client je Knoten pro Dateisystem aufgeteilt, der aktiv Operationen an Daten in dem Dateisystem durchführt.
  • Eine Quotengrenze ist ein Schwellenwert, bis zu dem ein Benutzer Inodes oder Speicherplatz in dem Dateisystem zuordnen darf. In dieser Schrift wird die Anzahl der Inodes und der Speicherbereich, der einem Benutzer zugeteilt werden darf, als Quote bezeichnet. Ein lokaler Anteil ist der Speicherbereich, der zugunsten eines Benutzers auf einem Quoten-Client ohne Dialogverkehr mit dem Quoten-Server zugeordnet werden kann.
  • Der Server verwaltet eine auf der Platte befindliche Datei, die die Quotengrenzwerte und die kumulierte Nutzung sowie den "Zweifelhaft"-Wert für alle Benutzer in dem gesamten MPP-System enthält. Der "Zweifelhaft"-Wert stellt die Kapazität des Quoten-Speicherbereichs dar, über den der Server keine genauen Informationen hat. Der zweifelhafte Speicherbereich ("in-doubt space") kann nicht neu zugeordnet werden, ohne dass die Gefahr besteht, dass einem Benutzer gestattet wird, seine Grenzwerte zu überschreiten. Ein Teil des "Zweifelhaft"-Werts stellt die tatsächliche Nutzung durch den Benutzer dar; aber ein Teil stellt die mögliche Nutzung dar, die vorübergehend verloren geht.
  • Die hier beschriebene Lösung ist ein Verfahren zur Wiederherstellung von lokalen Anteilen von dem zweifelhaften Speicherbereich, so dass die ungenutzte, vorübergehend verlorene Quote wieder zur Verfügung steht. Dieser Mechanismus (der hier die Bezeichnung "quotacheck" (Quotenprüfung)) trägt, arbeitet an einem aktiven Dateisystem, ohne dass er die Zuordnung und die Aufhebung der Zuordnung von Speicherbereich auf der Platte und von Inodes stört.
  • Quotacheck legt auf dem Quoten-Server eine Schattenkopie aller Quoten-Datensätze an und summiert dort die Quoten-Nutzung auf, die es den Inode-Informationen der Dateien entnimmt. Während Quotacheck die Inodes abfragt, werden alle Änderungen an den Zuordnungen und der Aufhebung der Zuordnungen in dem ursprünglichen Quoten-Datensatz und in dem Schattendatensatz auf dem Quoten-Server vermerkt. Aktualisierungen der Quotennutzung vor und nach der aktuellen Position von Quotacheck (d.h. dem gerade gelesenen Inode) müssen unterschiedlich behandelt werden. Änderungen an der Zuordnung nach der aktuellen Position von Quotacheck (bereits geprüfte Inodes) werden in dem ursprünglichen Quoten-Datensatz und in dem Schattendatensatz aktualisiert; Änderungen an der Zuordnung vor der aktuellen Position von Quotacheck (noch nicht geprüfte Inodes) werden nur in dem ursprünglichen Quoten-Datensatz aktualisiert. Der "Zweifelhaft"-Wert in beiden Datensätzen wird auf den gleichen Wert aktualisiert, so dass die Summe der lokalen Anteile auf den Quoten-Clients korrekt ist, nachdem das Programm Quotacheck vollständig ausgeführt worden ist.
  • Die Quoten-Clients werden über die aktuelle Position von Quotacheck informiert und können folglich all diejenigen Quoten in Schatteneinträgen erfassen, die hinter der jeweiligen aktuellen Position von Quotacheck zugeordnet wurden beziehungsweise deren Zuordnung aufgehoben wurde. Quoten-Clients senden ihre erfassten Änderungen für den Quoten-Schattendatensatz an den Quoten-Server, sobald Quotacheck mit der Abfrage der Inodes fertig ist und damit beginnt, die ursprünglichen Quoten-Einträge und die Schatten-Quoteneinträge zusammenzuführen.
  • Der "Zweifelhaft"-Wert des Schattendatensatzes wird zusammen mit dem "Zweifelhaft"-Wert des ursprünglichen Quoten-Datensatzes auf dem Server aktualisiert, nachdem alle Schattendatensätze angelegt und nachdem alle lokalen Anteile von den Clients widerrufen worden sind, jedoch bevor Quotacheck mit der Abfrage von Inodes nach Quoten-Nutzungsinformationen beginnt (d.h, der "Zweifehlhaft"-Schattenwert beginnt bei null, und der reguläre "Zweifelhaft"-Wert zeigt die verlorenen Quoten an). Bei der Zusammenführung von Schatten- und regulären Quoten-Datensätzen am Ende der Ausführung des Programms Quotacheck wird der "Zweifelhaft"-Wert des Schattendatensatzes in den regulären Quoten-Datensatz kopiert.
  • Uns ist kein paralleles Dateisystem bekannt, das Plattenblöcke unabhängig auf allen Knoten eines parallelen Systems zuordnet. Das bedeutet, dass kein anderer mit dem Problem konfrontiert wird, bis er es mit an das Netzwerk angeschlossenen Speichersystemen versucht.
  • Aus Gründen der Leistungsfähigkeit ordnen wir parallel Speicher zu und vermeiden eine Lösung mit einem einzigen Server, der Engpässe und Probleme mit der Wiederherstellung hat. Wir müssen eine Quote haben, da Benutzer die Belegung von Plattenspeicher über das gesamte Parallelverarbeitungssystem steuern möchten. Diese Lösung gestattet eine parallele Zuordnung, erzwingt kein dauerndes Sperren einer globalen Quote, was den Betrieb verlangsamen würde, und ermöglicht die rechtzeitige Wiederherstellung nach Verarbeitungsfehlern.

Claims (10)

  1. Verfahren in einem Rechnersystem, das über ein Dateisystem mit gemeinsam benutzten Platten verfügt, welches auf mehreren Rechnern (1, 2, 3) läuft, von denen jeder seine eigene Instanz eines Betriebssystems hat und zum parallelen und gemeinsamen Zugriff auf Daten mit Dateien verbunden ist, die sich auf gemeinsam benutzten Platten befinden, welche an ein Netzwerk angeschlossen sind, wobei das Verfahren den folgenden Schritt umfasst: Ermöglichen des unabhängigen Zugriffs auf angeschlossene gemeinsam benutzte Platten, wobei jedoch durch die Verwendung eines verteilten Token-Verwaltungsprogramms (11) und eines Sperrmechanismus die Konsistenz bei jeder Datei gewahrt wird, dadurch gekennzeichnet, dass das Verfahren des Weiteren den folgenden Schritt beinhaltet: Bereitstellen von Metadaten für eine Datei in einem Metadaten-Knoten, wobei für jede Datei ein einzelner Knoten ausgewählt wird, der für die Abwicklung der gesamten Eingabe-/Ausgabeaktivität der Metadaten von der Platte und auf die Platte beziehungsweise von den Platten und auf die Platten, auf der beziehungsweise auf denen sich die Metadaten befinden, verantwortlich ist, während Aktualisierungen von Metadaten von jedem Rechnerknoten unabhängig aufgezeichnet werden, wobei jeder Knoten mit Ausnahme des Metadaten-Knotens nur über den Metadaten-Knoten Daten austauscht, um Metadaten-Informationen abzurufen oder zu aktualisieren.
  2. Verfahren nach einem der vorhergehenden Ansprüche, wobei ein Dateisystem-Koordinator aktiviert wird, um den Zugriff von einem bestimmten Knoten auf eine bestimmte Platte nach einem Ausfallereignis zu sperren.
  3. Verfahren nach Anspruch 2, wobei nach dem Start einer Wiederherstellungsprozedur im Anschluss an ein Ausfallereignis Wiederherstellungsschritte auf allen Knoten durchgeführt werden, bevor an irgendeiner Stelle in dem Dateisystem ein nächster Schritt aktiviert wird.
  4. Verfahren nach Anspruch 2 oder Anspruch 3, das Folgendes beinhaltet: Beenden des Datenaustauschs mit einem ausgefallenen Prozessor und Abschirmen des ausgefallenen Prozessors durch den Dateisystem-Koordinator, wobei das System mit den gemeinsam benutzten Platten keine Plattenanforderungen von einem ausgefallenen Knoten mehr erfüllt, indem es den Zugriff auf eine gemeinsam benutzte Platte selbst dann verweigert, wenn der ausgefallene Knoten den Ausfall der Datenübertragung nicht festgestellt hat.
  5. Verfahren nach Anspruch 4, das das Aufheben eines Sperrzustands beinhaltet, indem der Dateisystem-Koordinator ein Token-Verwaltungsprogramm und den Sperrenkoordinator des Sperrmechanismus informiert, der daraufhin die Bewilligung von Sperren einstellt, die von einem ausgefallenen Knoten zum Zeitpunkt des Ausfalls gehalten werden.
  6. Verfahren nach Anspruch 5, das die Wiedergabe eines Metadaten-Protokolls des ausgefallenen Knotens durch den Dateisystem-Koordinator beinhaltet.
  7. Verfahren nach einem der vorhergehenden Ansprüche, das die Wiederherstellung nach einem Ausfall eines Knotens bei jedem Dateisystem des Rechnersystems beinhaltet, indem die Wiederherstellungsprozedur für jedes Dateisystem des Rechnersystems wiederholt wird.
  8. Verfahren nach einem der vorhergehenden Ansprüche, das die Wiederherstellung nach einem Ausfall eines Knotens bei jedem Dateisystem des Rechnersystems beinhaltet.
  9. System, das ein Mittel umfasst, welches zur Durchführung aller Schritte des Verfahrens nach einem der vorhergehenden Verfahrensansprüche ausgelegt ist.
  10. Rechnerprogramm, das Befehle zur Durchführung aller Schritte des Verfahrens nach einem der vorhergehenden Verfahrensansprüche umfasst, wenn das Rechnerprogramm auf einem Rechnersystem ausgeführt wird.
DE69838367T 1997-07-11 1998-06-17 Paralleles Dateiensystem und Verfahren zur unabhängigen Aufzeichnung von Metadaten Expired - Lifetime DE69838367T2 (de)

Applications Claiming Priority (2)

Application Number Priority Date Filing Date Title
US08/893,627 US6021508A (en) 1997-07-11 1997-07-11 Parallel file system and method for independent metadata loggin
US893627 1997-07-11

Publications (2)

Publication Number Publication Date
DE69838367D1 DE69838367D1 (de) 2007-10-18
DE69838367T2 true DE69838367T2 (de) 2008-05-29

Family

ID=25401831

Family Applications (1)

Application Number Title Priority Date Filing Date
DE69838367T Expired - Lifetime DE69838367T2 (de) 1997-07-11 1998-06-17 Paralleles Dateiensystem und Verfahren zur unabhängigen Aufzeichnung von Metadaten

Country Status (3)

Country Link
US (1) US6021508A (de)
EP (1) EP0892347B1 (de)
DE (1) DE69838367T2 (de)

Families Citing this family (76)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
US6633916B2 (en) 1998-06-10 2003-10-14 Hewlett-Packard Development Company, L.P. Method and apparatus for virtual resource handling in a multi-processor computer system
US6332180B1 (en) 1998-06-10 2001-12-18 Compaq Information Technologies Group, L.P. Method and apparatus for communication in a multi-processor computer system
US6199179B1 (en) 1998-06-10 2001-03-06 Compaq Computer Corporation Method and apparatus for failure recovery in a multi-processor computer system
US6542926B2 (en) 1998-06-10 2003-04-01 Compaq Information Technologies Group, L.P. Software partitioned multi-processor system with flexible resource sharing levels
US6260068B1 (en) 1998-06-10 2001-07-10 Compaq Computer Corporation Method and apparatus for migrating resources in a multi-processor computer system
US6381682B2 (en) * 1998-06-10 2002-04-30 Compaq Information Technologies Group, L.P. Method and apparatus for dynamically sharing memory in a multiprocessor system
US6647508B2 (en) 1997-11-04 2003-11-11 Hewlett-Packard Development Company, L.P. Multiprocessor computer architecture with multiple operating system instances and software controlled resource allocation
US6374336B1 (en) 1997-12-24 2002-04-16 Avid Technology, Inc. Computer system and process for transferring multiple high bandwidth streams of data between multiple storage units and multiple applications in a scalable and reliable manner
US6415373B1 (en) 1997-12-24 2002-07-02 Avid Technology, Inc. Computer system and process for transferring multiple high bandwidth streams of data between multiple storage units and multiple applications in a scalable and reliable manner
US6493720B1 (en) * 1998-01-26 2002-12-10 International Business Machines Corporation Method and system for synchronization of metadata in an information catalog
US7725433B1 (en) * 1998-01-26 2010-05-25 International Business Machines Corporation Data navigation system and method employing data transformation lineage model
US7451448B1 (en) 1998-08-28 2008-11-11 Oracle International Corporation Methods for selectively quiescing a computer system
US6457008B1 (en) * 1998-08-28 2002-09-24 Oracle Corporation Pluggable resource scheduling policies
US7526767B1 (en) 1998-08-28 2009-04-28 Oracle International Corporation Methods for automatic group switching according to a resource plan
US7020878B1 (en) 1998-08-28 2006-03-28 Oracle International Corporation System for allocating resource using the weight that represents a limitation on number of allowance active sessions associated with each resource consumer group
US6334196B1 (en) * 1999-02-12 2001-12-25 Unisys Corporation Estimator program for estimating the availability of an application program that runs in a cluster of at least two computers
US6530036B1 (en) * 1999-08-17 2003-03-04 Tricord Systems, Inc. Self-healing computer system storage
US6308184B1 (en) 1999-04-09 2001-10-23 Hewlett-Packard Company Delayed unloading of a dynamically loadable file
GB2356473B (en) * 1999-07-06 2001-11-28 Matsushita Electric Ind Co Ltd Real-time distributed file system
US7596563B1 (en) * 1999-10-28 2009-09-29 Hewlett-Packard Development Company, L.P. Computerized file system and method
US6779080B2 (en) * 2000-01-11 2004-08-17 International Business Machines Corporation Serial data storage system with automatically adjusted data protection to implement worm media with limited overwrite allowing write appending
US6654908B1 (en) * 2000-04-29 2003-11-25 Hewlett-Packard Development Company, L.P. Method for and system producing shared usage of intercommunication fabric error logging registers in a multiprocessor environment
CA2413434A1 (en) * 2000-06-26 2002-01-03 International Business Machines Corporation Data management application programming interface for a parallel file system
US6622259B1 (en) 2000-07-14 2003-09-16 International Business Machines Corporation Non-disruptive migration of coordinator services in a distributed computer system
JP2005530242A (ja) * 2000-09-11 2005-10-06 アガミ システムズ, インコーポレイテッド 区画された移動可能メタデータを有する記憶システム
US6915391B2 (en) * 2000-12-15 2005-07-05 International Business Machines Corporation Support for single-node quorum in a two-node nodeset for a shared disk parallel file system
US6920541B2 (en) * 2000-12-21 2005-07-19 International Business Machines Corporation Trace termination for on-the-fly garbage collection for weakly-consistent computer architecture
US6928578B2 (en) * 2001-05-10 2005-08-09 International Business Machines Corporation System, method, and computer program for selectable or programmable data consistency checking methodology
US6708175B2 (en) 2001-06-06 2004-03-16 International Business Machines Corporation Program support for disk fencing in a shared disk parallel file system across storage area network
CN1463518A (zh) * 2001-06-12 2003-12-24 塞克沃亚宽带公司 用于分布式计算机网络的xml驱动的自动自还原文件传送系统
GB2377051B (en) * 2001-06-30 2005-06-15 Hewlett Packard Co Monitoring applicance for data storage arrays and a method of monitoring usage
DE10143142A1 (de) * 2001-09-04 2003-01-30 Bosch Gmbh Robert Verfahren zum Betreiben einer Schaltungsanordnung, die einen Mikrocontroller und ein EEPROM enthält
WO2003025801A1 (en) * 2001-09-21 2003-03-27 Polyserve, Inc. System and method for implementing journaling in a multi-node environment
US6766482B1 (en) 2001-10-31 2004-07-20 Extreme Networks Ethernet automatic protection switching
US6904448B2 (en) * 2001-12-20 2005-06-07 International Business Machines Corporation Dynamic quorum adjustment
US7010528B2 (en) * 2002-05-23 2006-03-07 International Business Machines Corporation Mechanism for running parallel application programs on metadata controller nodes
US7448077B2 (en) * 2002-05-23 2008-11-04 International Business Machines Corporation File level security for a metadata controller in a storage area network
US20030220943A1 (en) * 2002-05-23 2003-11-27 International Business Machines Corporation Recovery of a single metadata controller failure in a storage area network environment
US8140622B2 (en) 2002-05-23 2012-03-20 International Business Machines Corporation Parallel metadata service in storage area network environment
US6964833B2 (en) * 2002-05-31 2005-11-15 Samsung Electronics Co., Ltd. Linked dihydrazone-based charge transport compounds
US20040019640A1 (en) * 2002-07-25 2004-01-29 Bartram Linda Ruth System and method for distributing shared storage for collaboration across multiple devices
US7529842B2 (en) * 2002-12-17 2009-05-05 International Business Machines Corporation Method, system and program product for detecting an operational risk of a node
US8250202B2 (en) * 2003-01-04 2012-08-21 International Business Machines Corporation Distributed notification and action mechanism for mirroring-related events
US7107293B2 (en) * 2003-04-30 2006-09-12 International Business Machines Corporation Nested recovery scope management for stateless recovery agents
US7437492B2 (en) * 2003-05-14 2008-10-14 Netapp, Inc Method and system for data compression and compression estimation in a virtual tape library environment
US20050039001A1 (en) * 2003-07-30 2005-02-17 Microsoft Corporation Zoned based security administration for data items
US7555504B2 (en) * 2003-09-23 2009-06-30 Emc Corporation Maintenance of a file version set including read-only and read-write snapshot copies of a production file
JP4452064B2 (ja) * 2003-11-18 2010-04-21 株式会社日立製作所 情報処理システム、情報処理装置、情報処理装置の制御方法及びプログラム
US7698289B2 (en) 2003-12-02 2010-04-13 Netapp, Inc. Storage system architecture for striping data container content across volumes of a cluster
US20060074940A1 (en) * 2004-10-05 2006-04-06 International Business Machines Corporation Dynamic management of node clusters to enable data sharing
US7904649B2 (en) 2005-04-29 2011-03-08 Netapp, Inc. System and method for restriping data across a plurality of volumes
US7698334B2 (en) 2005-04-29 2010-04-13 Netapp, Inc. System and method for multi-tiered meta-data caching and distribution in a clustered computer environment
US7373545B2 (en) * 2005-05-06 2008-05-13 Marathon Technologies Corporation Fault tolerant computer system
US20070011136A1 (en) * 2005-07-05 2007-01-11 International Business Machines Corporation Employing an identifier for an account of one domain in another domain to facilitate access of data on shared storage media
EP1949214B1 (de) 2005-10-28 2012-12-19 Network Appliance, Inc. System und verfahren zum optimieren der multi-pathing-unterstützung in einer verteilten speichersystemumgebung
US7552148B2 (en) * 2006-02-28 2009-06-23 Microsoft Corporation Shutdown recovery
US7844584B1 (en) * 2006-06-23 2010-11-30 Netapp, Inc. System and method for persistently storing lock state information
US8150870B1 (en) * 2006-12-22 2012-04-03 Amazon Technologies, Inc. Scalable partitioning in a multilayered data service framework
US8489811B1 (en) 2006-12-29 2013-07-16 Netapp, Inc. System and method for addressing data containers using data set identifiers
US8312046B1 (en) 2007-02-28 2012-11-13 Netapp, Inc. System and method for enabling a data container to appear in a plurality of locations in a super-namespace
US7797489B1 (en) 2007-06-01 2010-09-14 Netapp, Inc. System and method for providing space availability notification in a distributed striped volume set
US7890555B2 (en) * 2007-07-10 2011-02-15 International Business Machines Corporation File system mounting in a clustered file system
US8156164B2 (en) * 2007-07-11 2012-04-10 International Business Machines Corporation Concurrent directory update in a cluster file system
US9880970B2 (en) * 2007-10-03 2018-01-30 William L. Bain Method for implementing highly available data parallel operations on a computational grid
US9946722B2 (en) * 2007-11-30 2018-04-17 Red Hat, Inc. Generating file usage information
US7996607B1 (en) 2008-01-28 2011-08-09 Netapp, Inc. Distributing lookup operations in a striped storage system
CN101539873B (zh) * 2009-04-15 2011-02-09 成都市华为赛门铁克科技有限公司 数据恢复的方法、数据节点及分布式文件系统
US8117388B2 (en) 2009-04-30 2012-02-14 Netapp, Inc. Data distribution through capacity leveling in a striped file system
US9058334B2 (en) * 2010-02-11 2015-06-16 Emc Corporation Parallel file system processing
RU2469388C1 (ru) * 2011-09-19 2012-12-10 Российская Федерация, от имени которой выступает Государственная корпорация по атомной энергии "Росатом" - Госкорпорация "Росатом" Способ обращения к данным, хранимым в параллельной файловой системе, с иерархической организацией памяти
US8954409B1 (en) * 2011-09-22 2015-02-10 Juniper Networks, Inc. Acquisition of multiple synchronization objects within a computing device
US10048990B2 (en) * 2011-11-19 2018-08-14 International Business Machines Corporation Parallel access of partially locked content of input file
US9495293B1 (en) * 2014-05-05 2016-11-15 EMC IP Holding Company, LLC Zone consistency
US11079971B2 (en) * 2017-03-31 2021-08-03 Veritas Technologies Llc Input/output (i/o) fencing without dedicated arbitrators
US10671370B2 (en) * 2018-05-30 2020-06-02 Red Hat, Inc. Distributing file system states
US11650974B2 (en) * 2020-07-01 2023-05-16 Sap Se Cross-system process control framework

Family Cites Families (11)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
GB2023314B (en) * 1978-06-15 1982-10-06 Ibm Digital data processing systems
US5202971A (en) * 1987-02-13 1993-04-13 International Business Machines Corporation System for file and record locking between nodes in a distributed data processing environment maintaining one copy of each file lock
DE68913629T2 (de) * 1988-03-14 1994-06-16 Unisys Corp Satzverriegelungsprozessor für vielfachverarbeitungsdatensystem.
US5043876A (en) * 1988-05-27 1991-08-27 International Business Machines Corporation N-level file shadowing and recovery in a shared file system
US5226159A (en) * 1989-05-15 1993-07-06 International Business Machines Corporation File lock management in a distributed data processing system
JP2532194B2 (ja) * 1992-03-30 1996-09-11 インターナショナル・ビジネス・マシーンズ・コーポレイション プロセッサと結合機能間に対するメッセ―ジ経路指定機能を有するデ―タ処理システム
US5339427A (en) * 1992-03-30 1994-08-16 International Business Machines Corporation Method and apparatus for distributed locking of shared data, employing a central coupling facility
US5454108A (en) * 1994-01-26 1995-09-26 International Business Machines Corporation Distributed lock manager using a passive, state-full control-server
US5490270A (en) * 1994-06-16 1996-02-06 International Business Machines Corporation Simultaneous updates to the modification time attribute of a shared file in a cluster having a server and client nodes
US5566297A (en) * 1994-06-16 1996-10-15 International Business Machines Corporation Non-disruptive recovery from file server failure in a highly available file system for clustered computing environments
US5996075A (en) * 1995-11-02 1999-11-30 Sun Microsystems, Inc. Method and apparatus for reliable disk fencing in a multicomputer system

Also Published As

Publication number Publication date
EP0892347B1 (de) 2007-09-05
DE69838367D1 (de) 2007-10-18
US6021508A (en) 2000-02-01
EP0892347A2 (de) 1999-01-20
EP0892347A3 (de) 2003-12-03

Similar Documents

Publication Publication Date Title
DE69838367T2 (de) Paralleles Dateiensystem und Verfahren zur unabhängigen Aufzeichnung von Metadaten
DE69722962T2 (de) Strukturiertes datenspeichersystem mit global adressierbarem speicher
US5987477A (en) Parallel file system and method for parallel write sharing
US5963963A (en) Parallel file system and buffer management arbitration
EP0890916B1 (de) Paralleles Dateisystem und -Verfahren zur Auswahl eines Metadatenrechners
US5950199A (en) Parallel file system and method for granting byte range tokens
EP0899667B1 (de) Paralleles Dateisystem und Verfahren zur Übernahme der Funktion eines Metadatenservers
US5956734A (en) Parallel file system with a quota check utility
US5946686A (en) Parallel file system and method with quota allocation
US5940841A (en) Parallel file system with extended file attributes
US5893086A (en) Parallel file system and method with extensible hashing
US6032216A (en) Parallel file system with method using tokens for locking modes
US5940838A (en) Parallel file system and method anticipating cache usage patterns
DE69728176T2 (de) Verfahren und gerät das verteilte steuerung von gemeinsamen betriebsmitteln erlaubt
DE60313783T2 (de) Bewegen von daten zwischen speichereinheiten
CA2302981C (en) Online database table reorganization
Mohan Commit_LSN: A Novel and Simple Method for Reducing Locking and Latching in Transaction Processing Systems.
DE602005004166T2 (de) Vorrichtung, system und verfahren zur reinitialisierung einer serialisierung von dateisystemen
US7805568B2 (en) Method and apparatus for data storage using striping specification identification
US6321234B1 (en) Database server system with improved methods for logging transactions
DE112020000749T5 (de) Indexerstellung für sich entwickelnde umfangreiche Datensätze in Hybriden Transaktions- und Analysenverarbeitungssystemen mit mehreren Mastern
DE202014010907U1 (de) Isolierung von Clients verteilter Speichersysteme
DE102017118341B4 (de) Neuaufteilen von Daten in einem verteilten Computersystem
DE602004007925T2 (de) Verwalten einer beziehung zwischen einem zielvolumen und einem quellenvolumen
DE102021127151A1 (de) Verfahren und system für libfabric atomics-basierte lockless cluster-wide shared memory access api in einem verteilten system

Legal Events

Date Code Title Description
8364 No opposition during term of opposition
8320 Willingness to grant licences declared (paragraph 23)