signal(7) | Miscellaneous Information Manual | signal(7) |
signal - Überblick über Signale (Software-Interrupts)
Linux unterstützt sowohl nach POSIX zuverlässige Signale (im Folgenden: »Standard-Signale«) und POSIX-Echtzeit-Signale.
Jedes Signal hat eine aktuelle Zuordnung. Sie legt fest, wie sich der Prozess verhält, wenn er das Signal erhält.
Die Einträge in der »Aktion«-Spalte in der folgenden Tabelle legen die Standardzuordnung für jedes Signal fest:
Ein Prozess kann die Zuordnung eines Signals mit Hilfe von sigaction(2) oder signal(2) ändern. (Letzteres ist schlechter portierbar bei der Realisierung von Signal-Handlern; siehe signal(2) für Details.) Mit diesen Systemaufrufen kann ein Prozess eine der folgenden Verhaltensweisen bei Erhalt eines Signals auswählen: die Standardaktion ausführen, das Signal ignorieren oder das Signal mit einem Signal-Handler abfangen. Ein Signal-Handler ist eine vom Programmierer definierte Funktion. Sie wird automatisch aufgerufen, wenn das Signal eintrifft.
Standardmäßig wird ein Signal-Handler auf dem normalen Prozess-Stack aufgerufen. Man kann es einrichten, dass der Signal-Handler einen alternativen Stack benutzt; vgl. sigaltstack(2) für eine Erörterung, wie das gemacht wird und wann es nützlich sein könnte.
Die Signalzuordnung ist ein prozessbezogenes Attribut; in einer Multithread-Anwendung ist die Zuordnung eines bestimmten Signales für alle Threads gleich.
Ein mittels fork(2) erstellter Kindprozess erbt eine Kopie der Signalzuordnungen seines Elternprozesses. Während eines execve(2) werden die Zuordnungen von verwalteten Signalen auf die Vorgabe zurückgesetzt; die Zuordnung ignorierter Signale werden unverändert gelassen.
Die folgenden Systemaufrufe und Bibliotheksfunktionen ermöglichen dem aufrufenden Programm den Versand eines Signals:
Die folgenden Systemaufrufe setzen die Ausführung des aufrufenden Threads aus, bis ein Signal abgefangen wird (oder ein nicht abgefangenes Signal den Prozess beendet):
Anstatt ein Signal asynchron mit einem Signal-Handler abzufangen, kann ein Signal auch synchron akzeptiert werden. Das heißt, die Ausführung wird blockiert, bis das Signal gesendet wird. Dann liefert der Kernel Informationen über das Signal an den Aufrufenden. Es gibt zwei allgemeine Möglichkeiten, das zu tun:
Ein Signal kann blockiert werden. Das bedeutet, dass es erst dann gesendet wird, nachdem es (später/verzögert) freigegeben wurde. Zwischen dem Zeitpunkt seiner Erzeugung und dem Zeitpunkt seines Versands wird es anstehend (pending) genannt.
Jeder Thread in einem Prozess hat eine unabhängige Signalauswahl-Maske (signal mask). Sie legt den Satz von Signalen fest, den der Thread derzeit blockiert. Ein Thread kann seine Signalauswahl-Maske mit pthread_sigmask(3) manipulieren. In einer traditionellen Single-Threaded-Anwendung kann sigprocmask(2) verwendet werden, um die Signalmaske zu manipulieren.
Ein mittels fork(2) erstellter Kindprozess erbt eine Kopie der Signalmaske des Elternprozeses; die Signalmaske wird über execve(2) hinweg erhalten.
Ein Signal kann Prozess-orientiert oder Thread-orientiert sein. Ein Prozess-orientiertes Signal ist eines, das auf einen Prozess als gesamtes zielt (und daher daran anhängig ist). Ein Signal kann Prozess-orientiert sein, da es vom Kernel für einen Grund außer einer Hardware-Ausnahmebehandlung erzeugt wurde oder da es mittels kill(2) oder sigqueue(3) gesandt wurde. Ein Thread-orientiertes Signal ist eines, das auf einen bestimmten Thread abzielt. Ein Signal kann Thread-orientiert sein, da es als Konsequenz einer Ausführung einer bestimmten Anweisung in Maschinensprache erstellt wurde, die eine Hardware-Ausnahmebehandlung auslöste (z.B. SIGSEGV für einen ungültigen Speicherzugriff oder SIGFPE für einen mathematischen Fehler) oder da es mit Schnittstellen wie tgkill(2) oder pthread_kill(3) auf einen bestimmten Thread zielte.
Ein Prozess-orientiertes Signal kann an jeden der Threads ausgeliefert werden, der derzeit keine Signale blockiert. Falls mehr als ein Thread Signale nicht blockiert, dann wählt der Kernel einen beliebigen Thread aus, an den er das Signal ausliefert.
Ein Thread kann die aktuell für ihn anstehenden Gruppe von Signale mit sigpending(2) ermitteln. Das sind einerseits die für diesen Thread und andererseits die für seinen Prozess bestimmten Signale.
Ein mittels fork(2) erstellter Kindprozess hat anfänglich eine leere anhängende Signalgruppe; die anhängende Signalgruppe wird über execve(2) hinweg erhalten.
Immer wenn es einen Übergang von der Kernelmodus-Ausführung zu der Anwendungsraum-Ausführung gibt (z.B bei der Rückkehr aus einem Systemaufruf oder Einplanung eines Threads auf einer CPU), prüft der Kernel, ob es ein anhängendes, nicht blockiertes Signal gibt, für das der Prozess einen Signal-Handler etabliert hat. Falls es ein solches anhängendes Signal gibt, passieren die folgenden Schritte:
Beachten Sie, dass der abschließende Schritt nicht ausgeführt wird, falls der Signal-Handler nicht zurückkehrt (z.B. weil die Steuerung mittels siglongjmp(3) aus dem Handler herausverlegt wurde oder der Handler mittels execve(2) ein neues Programm ausführt). In solchen Szenarien ist es insbesondere die Verantwortung des Programmierers, den Zustand der Signalmaske (mittels sigprocmask(2)) wiederherzustellen, falls gewünscht wird, die Blockierung der Signale aufzuheben, die beim Eintritt in den Signal-Handler blockiert wurden. (Beachten Sie, dass siglongjmp(3) die Signal-Maske wiederherstellen könnte oder auch nicht, abhängig vom Wert savesigs, der beim entsprechenden Aufruf von sigsetjmp(3) festgelegt wurde.)
Vom Standpunkt des Kernels aus ist die Ausführung des Signal-Handler-Codes genau das gleiche wie die Ausführung jedes anderen Codes im Anwendungsraum. Dies bedeutet, dass der Kernel keinerlei besondere Zustandsinformationen aufzeichnet, die anzeigen, dass der Thread sich derzeit in der Ausführung eines Signal-Handlers befindet. Alle notwendigen Zustandsinformationen werden in Anwendungsraum-Registern und im Anwendungsraum-Stack verwaltet. Die Tiefe, zu der verschachtelte Signal-Handler aufgerufen werden können, wird daher durch den Anwendungsraum-Stack begrenzt (und unterliegt daher dem Design der Software).
Linux untersützt die nachfolgend aufgeführten Standard-Signale. Die zweite Spalte der Tabelle zeigt an, welcher Standard (falls vorhanden) das Signal festlegt: »P1990« zeigt an, dass das Signal in dem ursprünglichen Standard POSIX.1-1990 beschrieben wurde; »P2001« zeigt an, dass das Signal in SUSv2 und POSIX.1-2001 hinzugefügt wurde.
Signal | Standard | Aktion | Kommentar |
SIGABRT | P1990 | Core | Abbruchsignal von abort(3) |
SIGALRM | P1990 | Term | Timersignal von alarm(2) |
SIGBUS | P2001 | Core | Bus-Fehler (Speicherzugriffsfehler) |
SIGCHLD | P1990 | Ign | Kindprozess angehalten oder beendet |
SIGCLD | - | Ign | ein Synonym für SIGCHLD |
SIGCONT | P1990 | Cont | fortsetzen, wenn angehalten |
SIGEMT | - | Term | Emulator-Ausnahmebehandlung |
SIGFPE | P1990 | Core | Fließkomma-Ausnahmefehler |
SIGHUP | P1990 | Term | Verbindung am steuernden Terminal beendet |
(aufgehängt) oder der steuernde Prozess wurde beendet | |||
SIGILL | P1990 | Core | ungültiger Befehl |
SIGINFO | - | ein Synonym für SIGPWR | |
SIGINT | P1990 | Term | Unterbrechung von der Tastatur |
SIGIO | - | Term | E/A jetzt möglich (4.2BSD) |
SIGIOT | - | Core | IOT-Ausnahmebehandlung; ein Synonym für SIGABRT |
SIGKILL | P1990 | Term | Kill-Signal |
SIGLOST | - | Term | Dateisperre verloren/aufgehoben (nicht verwandt) |
SIGPIPE | P1990 | Term | defekte Pipe: Schreiben in eine Pipe ohne |
Leser; siehe pipe(7) | |||
SIGPOLL | P2001 | Term | abfragbares Ereignis (Sys V) |
Synonym für SIGIO | |||
SIGPROF | P2001 | Term | Profiling-Timer abgelaufen |
SIGPWR | - | Term | Stromausfall (System V) |
SIGQUIT | P1990 | Core | Abbruch von der Tastatur |
SIGSEGV | P1990 | Core | ungültige Speicherreferenz |
SIGSTKFLT | - | Term | Stack-Ausnahmebehandlung am Koprozessor (nicht verwendet) |
SIGSTOP | P1990 | Stop | Stop process |
SIGTSTP | P1990 | Stop | Stop am Terminal eingegeben |
SIGSYS | P2001 | Core | Ungültiger Systemaufruf (SVr4); |
siehe auch seccomp(2) | |||
SIGTERM | P1990 | Term | Beendigungssignal (termination signal) |
SIGTRAP | P2001 | Core | Trace-/Haltepunkt-Ausnahmebehandlung |
SIGTTIN | P1990 | Stop | Terminal-Eingabe für Hintergrundprozess |
SIGTTOU | P1990 | Stop | Terminal-Ausgabe für Hintergrundprozess |
SIGUNUSED | - | Core | synonym mit SIGSYS |
SIGURG | P2001 | Ign | dringende Gegebenheit an Socket (4.2BSD) |
SIGUSR1 | P1990 | Term | benutzerdefiniertes Signal 1 |
SIGUSR2 | P1990 | Term | benutzerdefiniertes Signal 2 |
SIGVTALRM | P2001 | Term | virtueller Wecker (4.2BSD) |
SIGXCPU | P2001 | Core | CPU-Zeitbegrenzung überschritten (4.2BSD) |
siehe setrlimit(2) | |||
SIGXFSZ | P2001 | Core | Dateigrößenbegrenzung überschritten (4.2BSD) |
siehe setrlimit(2) | |||
SIGWINCH | - | Ign | Änderung der Fenstergröße (4.3BSD, Sun) |
Die Signale SIGKILL und SIGSTOP können nicht abgefangen, blockiert oder ignoriert werden.
Bis einschließlich Linux 2.2 war das Standardverhalten für SIGSYS, SIGXCPU, SIGXFSZ und (auf anderen Architekturen als SPARC und MIPS) SIGBUS den Prozess (ohne einen Speicherauszug zu erzeugen) zu beenden. (Auf einigen anderen UNIX-Systemen ist die Standardaktion für SIGXCPUund SIGXFSZ, den Prozess ohne einen Speicherauszug zu beenden.) Linux 2.4 entspricht den Anforderungen von POSIX.1-2001 an diese Signale und beendet den Prozess mit einem Speicherauszug.
SIGEMT ist nicht in POSIX.1-2001 angegeben, erscheint aber trotzdem auf den meisten anderen UNIX-Systemen. Dort ist die Standardaktion in der Regel die Beendigung des Prozesses mit einem Speicherauszug.
SIGPWR (nicht in POSIX.1-2001 beschrieben) wird bei seinem Eintreten von diesen anderen UNIX-Systemen ignoriert.
SIGIO (nicht in POSIX.1-2001 beschrieben) wird standardmäßig auf verschiedenen anderen UNIX-Systemen ignoriert.
Falls für einen Prozess mehrere Standard-Signale anhängig sind, ist die Reihenfolge, in der diese Signale ausgeliefert werden, nicht spezifiziert.
Standard-Signale kennen keine Warteschlange. Falls mehrere Instanzen eines Standard-Signals erstellt werden, während dieses Signal blockiert ist, wird nur eine Instanz des Signals als anhängig markiert (und das Signal wird ausgeliefert, genau wenn die Blockade aufgehoben wird). Im Fall, bei dem ein Standard-Signal bereits anhängig ist, wird die dem Signal zugehörige Struktur siginfo_t (siehe sigaction(2)) nicht bei der Ankunft nachfolgender Instanzen des gleichen Signals überschrieben. Daher wird der Prozess die Informationen, die zu der ersten Instanz des Signals gehören, erhalten.
Der numerische Wert für jedes Signal wird in der nachfolgenden Tabelle angegeben. Wie in der Tabelle gezeigt, haben viele Signale verschiedene numerische Werte auf verschiedenen Architekturen. Der erste numerische Wert in jeder Zeile zeigt die Signalnummer auf X86, ARM und den meisten anderen Architekturen; der zweite Wert ist für Alpha und SPARC; der dritte für MIPS und der letzte für PARISC. Ein Bindestrich (-) zeigt an, dass ein Signal auf der entsprechenden Architektur nicht vorhanden ist.
Signal | x86/ARM | Alpha/ | MIPS | PARISC | Hinweise |
die meisten anderen | SPARC | ||||
SIGHUP | 1 | 1 | 1 | 1 | |
SIGINT | 2 | 2 | 2 | 2 | |
SIGQUIT | 3 | 3 | 3 | 3 | |
SIGILL | 4 | 4 | 4 | 4 | |
SIGTRAP | 5 | 5 | 5 | 5 | |
SIGABRT | 6 | 6 | 6 | 6 | |
SIGIOT | 6 | 6 | 6 | 6 | |
SIGBUS | 7 | 10 | 10 | 10 | |
SIGEMT | - | 7 | 7 | - | |
SIGFPE | 8 | 8 | 8 | 8 | |
SIGKILL | 9 | 9 | 9 | 9 | |
SIGUSR1 | 10 | 30 | 16 | 16 | |
SIGSEGV | 11 | 11 | 11 | 11 | |
SIGUSR2 | 12 | 31 | 17 | 17 | |
SIGPIPE | 13 | 13 | 13 | 13 | |
SIGALRM | 14 | 14 | 14 | 14 | |
SIGTERM | 15 | 15 | 15 | 15 | |
SIGSTKFLT | 16 | - | - | 7 | |
SIGCHLD | 17 | 20 | 18 | 18 | |
SIGCLD | - | - | 18 | - | |
SIGCONT | 18 | 19 | 25 | 26 | |
SIGSTOP | 19 | 17 | 23 | 24 | |
SIGTSTP | 20 | 18 | 24 | 25 | |
SIGTTIN | 21 | 21 | 26 | 27 | |
SIGTTOU | 22 | 22 | 27 | 28 | |
SIGURG | 23 | 16 | 21 | 29 | |
SIGXCPU | 24 | 24 | 30 | 12 | |
SIGXFSZ | 25 | 25 | 31 | 30 | |
SIGVTALRM | 26 | 26 | 28 | 20 | |
SIGPROF | 27 | 27 | 29 | 21 | |
SIGWINCH | 28 | 28 | 20 | 23 | |
SIGIO | 29 | 23 | 22 | 22 | |
SIGPOLL | identisch zu SIGIO | ||||
SIGPWR | 30 | 29/- | 19 | 19 | |
SIGINFO | - | 29/- | - | - | |
SIGLOST | - | -/29 | - | - | |
SIGSYS | 31 | 12 | 12 | 31 | |
SIGUNUSED | 31 | - | - | 31 |
Beachten Sie Folgendes:
Beginnend mit Linux 2.2 unterstützt Linux Echtzeit-Signale, wie sie ursprünglich in den POSIX.1b-Echtzeit-Erweiterungen definiert wurden (und jetzt in POSIX.1-2001 enthalten sind). Die Bereich der unterstützten Echtzeit-Signale wird von den Makros SIGRTMIN und SIGRTMAX definiert. POSIX.1-2001 verlangt, dass eine Umsetzung mindestens _POSIX_RTSIG_MAX (8) Echtzeit-Signale unterstützt.
Der Linux-Kernel unterstützt eine Reihe von 33 verschiedenen Echtzeit-Signalen, nummeriert von 32 bis 64. Doch die Glibc-Umsetzung der POSIX-Threads verwendet intern zwei (für NPTL) oder drei (für LinuxThreads) Echtzeit-Signale (siehe pthreads (7)) und stellt den Wert von SIGRTMIN passend (auf 34 oder 35 ein). Da die Zahl der verfügbaren Echtzeit-Signale je nach Glibc-Threading-Implementierung variiert und diese Variation (entsprechend dem verfügbaren Kernel und der Glibc) zur Laufzeit auftreten kann und tatsächlich die verfügbaren Echtzeitsignale je nach UNIX-System variieren, sollten Programme niemals mit eincodierten Zahlen auf Echtzeit-Signale verweisen. Stattdessen sollte auf Echtzeit-Signale immer mit der Notation SIGRTMIN+n verwiesen werden und zur Laufzeit überprüft werden, ob (SIGRTMIN+n) SIGRTMAX nicht übersteigt.
Im Gegensatz zu Standard-Signalen haben Echtzeit-Signale keine vordefinierten Bedeutungen: der gesamte Satz von Echtzeit-Signalen kann für anwendungsspezifische Zwecke genutzt werden.
Die Standardaktion für ein nicht abgefangenes Echtzeit-Signal ist der Abbruch des Prozesses.
Echtzeit-Signale zeichnen sich durch folgende Merkmale aus:
Wenn sowohl Standard- als auch Echtzeit-Signale für einen Prozess anstehen, macht POSIX keine Angabe dazu, welche Signale zuerst zugestellt werden. Linux gibt wie auch viele andere Implementierungen den Standard-Signalen den Vorzug.
Nach POSIX sollte eine Umsetzung mindestens _POSIX_SIGQUEUE_MAX (32) Echtzeit-Signale in der Warteschlange eines Prozesses ermöglichen. Allerdings macht Linux das anders. Bis einschließlich Linux 2.6.7 legt Linux eine systemweite Obergrenze für die Anzahl wartender Echtzeit-Signale für alle Prozesse fest. Diese Grenze kann eingesehen und (mit entsprechenden Rechten) durch die Datei /proc/sys/kernel/rtsig-max geändert werden. Aus der verwandten Datei /proc/sys/kernel/rtsig-nr kann die Anzahl der aktuell anstehenden Signale ermittelt werden. In Linux 2.6.8 wurden diese /proc-Schnittstellen durch die Ressource RLIMIT_SIGPENDING, die einen benutzerspezifischen Grenzwert für anstehende Signale in der Warteschlange festlegt, ersetzt (siehe setrlimit(2)).
Die Ergänzung um Echtzeitsignale erforderte die Verbreiterung der Signalmengenstruktur (sigset_t) von 32 auf 64 Bit. Konsequenterweise wurden viele Systemaufrufe durch neue Systemaufrufe abgelöst, die die größeren Signalmengen unterstützten. Die alten und neuen Systemaufrufe sind wie folgt:
Linux 2.0 und älter | Linux 2.2 und neuer |
sigaction(2) | rt_sigaction(2) |
sigpending(2) | rt_sigpending(2) |
sigprocmask(2) | rt_sigprocmask(2) |
sigreturn(2) | rt_sigreturn(2) |
sigsuspend(2) | rt_sigsuspend(2) |
sigtimedwait(2) | rt_sigtimedwait(2) |
Wenn ein Signal-Handler aufgerufen wird, während ein Systemaufruf oder Bibliotheksfunktionsaufruf blockiert ist, wird entweder:
Welche dieser beiden Verhaltensweisen eintritt, hängt von der Schnittstelle und der Verwendung oder Nichtverwendung des Schalters SA_RESTART ab (siehe sigaction(2)). Die Einzelheiten unterscheiden sich zwischen UNIX-Systemen. Im Folgenden werden die Linux-Spezifika erörtert.
Wenn ein blockierter Aufruf einer der folgenden Schnittstellen von einem Signal-Handler unterbrochen wird, wird der Aufruf nach der Rückkehr aus dem Signal-Handler erneut gestartet, wenn der Schalter SA_RESTART verwendet wurde; anderenfalls schlägt der Aufruf mit dem Fehler EINTR fehl:
Folgende Schnittstellen werden nach einer Unterbrechung durch einen Signal-Handler, unabhängig von der Verwendung von SA_RESTART nie erneut gestartet; sie schlagen immer mit dem Fehler EINTR fehl:
Die Funktion sleep(3) wird ebenfalls niemals neu gestartet, wenn sie durch einen Handler unterbrochen wurde, wird aber erfolgreich verlassen: Der Rückgabewert ist die Zeit, die noch geschlafen werden sollte.
Unter bestimmten Umständen kann die Benachrichtigungsfunktionalität im Benutzerraum von seccomp(2) zum Neustart von Systemaufrufen führen, die andernfalls niemals durch SA_RESTART neugestartet würden; für Details siehe seccomp_unotify(2).
Auf Linux können sogar ohne Signal-Handler bestimmte sperrende Systemaufrufe mit dem Fehler EINTR fehlschlagen, nachdem der Prozess von einem der Stop-Signale gestoppt wird und dann mittels SIGCONT wieder fortgesetzt. Dieses Verhalten wird von POSIX.1 nicht gebiligt und tritt nicht auf anderen Systemen auf.
Die folgenden Linux-Schnittstellen zeigen dieses Verhalten:
POSIX.1, mit den beschriebenen Ausnahmen
Für eine Diskussion asynchron-Signal-sicherer Funktionen, siehe signal-safety(7).
Die Datei /proc/[pid]/task/[TID]/status enthält verschiedene Felder, die die Signale, die ein Thread blockiert (SigBlk), abfängt (SigCgt) oder ignoriert (SigIgn) zeigt. (Die Gruppe der abgefangenen oder ignorierten Signale wird für alle Threads eines Prozesses identisch sein.) Andere Felder zeigen die Gruppe der anhängenden Signale, die für den Thread bestimmt sind (SigPnd) sowie die Gruppe der anhängenden Signale, die für den Prozess als ganzes bestimmt sind (ShdPnd). Die entsprechenden Felder in /proc/[PID]/status zeigen die Informationen für den Haupt-Thread. Siehe proc(5) für weitere Details.
Es gibt sechs Signale, die als Konsequenz aus einer Hardware-Ausnahmebehandlung ausgeliefert werden können: SIGBUS, SIGEMT, SIGFPE, SIGILL, SIGSEGV und SIGTRAP. Welches dieser Signale für eine bestimmte Hardware-Ausnahmebehandlung ausgeliefert wird, ist nicht dokumentiert und ergibt nicht immer Sinn.
Zum Beispiel kann ein ungültiger Speicherzugriff, der die Auslieferung von SIGSEGV auf einer CPU-Architektur hervorruft, die Auslieferung von SIGBUS auf einer anderen Architektur (oder andersherum) hervorrufen.
Als weiteres Beispiel löst die Verwendung der X86-int-Anweisung mit einem verbotenen Argument (jeder Zahl außer 3 und 128) die Auslieferung von SIGSEGV aus, obwohl SIGILL mehr Sinn ergäbe, aufgrund der Art, wie die CPU die verbotene Operation an den Kernel berichtet.
kill(1), clone(2), getrlimit(2), kill(2), pidfd_send_signal(2), restart_syscall(2), rt_sigqueueinfo(2), setitimer(2), setrlimit(2), sgetmask(2), sigaction(2), sigaltstack(2), signal(2), signalfd(2), sigpending(2), sigprocmask(2), sigreturn(2), sigsuspend(2), sigwaitinfo(2), abort(3), bsd_signal(3), killpg(3), longjmp(3), pthread_sigqueue(3), raise(3), sigqueue(3), sigset(3), sigsetops(3), sigvec(3), sigwait(3), strsignal(3), swapcontext(3), sysv_signal(3), core(5), proc(5), nptl(7), pthreads(7), sigevent(7)
Die deutsche Übersetzung dieser Handbuchseite wurde von Martin Eberhard Schauer <Martin.E.Schauer@gmx.de>, Dr. Tobias Quathamer <toddy@debian.org> und Helge Kreutzmann <debian@helgefjell.de> erstellt.
Diese Übersetzung ist Freie Dokumentation; lesen Sie die GNU General Public License Version 3 oder neuer bezüglich der Copyright-Bedingungen. Es wird KEINE HAFTUNG übernommen.
Wenn Sie Fehler in der Übersetzung dieser Handbuchseite finden, schicken Sie bitte eine E-Mail an die Mailingliste der Übersetzer.
5. Februar 2023 | Linux man-pages 6.03 |