ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/docs/ev.pod
Revision: 1.4
Committed: Tue Jan 8 00:50:36 2008 UTC (18 years, 9 months ago) by root
Branch: MAIN
Changes since 1.3: +26 -5 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3 root 1.2 EV - (noch) ein Event-Modell für Perl
4 root 1.1
5     =head1 Was?
6    
7     EV ist ein Perl-Modul, das Ereignisgesteuertes Programmieren
8 root 1.2 unterstützt. Darin unterscheidet es sich erst einmal nicht von anderen
9 root 1.1 dedizierten Modulen wie "Event" oder die in jedes Toolkit (Gtk, Tk etc.)
10     eingebaute Ereignisverarbeitung.
11    
12     EV ist des weiteren eine Schnittstelle zur "Libev"-Bibliothek, eine Art
13     Libevent-Klon, und erbt deshalb deren Features (worin sich EV nun von
14     einigen anderen Modulen unterscheidet):
15    
16     =over 4
17    
18 root 1.3 =item Korrektheit
19 root 1.1
20     Die meisten Ereignis-Bibliotheken funktionieren gut, solange nichts
21     unvorhergesehenes passiert.
22    
23 root 1.2 Die meisten versagen kläglich, wenn die Systemuhr verstellt wird
24     (Zeitsprünge): Aus einem 10s-Timeout können bei Tk (Event, Glib etc.)
25     problemlos mal Jahre werden, aus einem Tag wenige Sekunden. EV unterstützt
26 root 1.1 sowohl relative Timer ("wecke mich in 10 Minuten auf, egal ob ntpd die
27 root 1.2 Uhr ändert oder nicht) als auch absolute Timer ("wecke mich 2008-10-10
28 root 1.1 12:17:23.445 auf, egal wie lange das wirklich dauert" oder gar "wecke
29 root 1.2 mich zur nächsten Mitternacht auf"), was kein anderes mir bekanntes
30     Event-Modell unterstützt.
31 root 1.1
32     Registriert man bei Tk (Event::Lib, ...) zwei Watcher auf denselben
33     Filehandle, so kann einer davon verloren gehen (nicht notwendigerweise
34 root 1.2 immer der ältere oder neuere).
35 root 1.1
36 root 1.2 Stellt man bei Event die "richtigen" Prioritäten ein, kriegt man eine
37 root 1.1 stetig wachsende Event-Queue die nicht kleiner wird (was allerdings selten
38     in Out-Of-Memory-Situationen endet, denn Event wird vorher zu langsam).
39    
40 root 1.2 Das alles sind Kleinigkeiten - bis das Programm deshalb mal "hängt", 100%
41 root 1.1 Rechenzeit verbraucht oder gar sporadisch versagt.
42    
43 root 1.2 Außerdem haben viele Event-Modelle (Event, Glib, Tk...) ein von Perl
44 root 1.1 abweichendes Objektmodell: verliert man beim Event oder Glib die Referenz
45     auf den Watcher, so hat man bestenfalls ein Memory-Leak. Die EV-Watcher
46     bestehen genau so lange, wie Referenzen darauf zeigen wie jedes andere
47     Perl-Objekt auch.
48    
49     Fork ist ein ganz spezielles Problem: wenige Event-Bibliotheken
50 root 1.2 unterstützen es direkt, vor allem diejenigen, die Epoll, Kqueue
51     o.ä. benutzen, da diese Schnittstellen sehr fork-feindlich sind. EV
52     unterstützt fork indem es den Kernel-Zustand im Kindprozess automatisch
53 root 1.1 wiederherstellt, sofern es benutzt wird.
54    
55 root 1.3 =item Geschwindigkeit
56 root 1.1
57 root 1.2 EV benutzt "die richtigen" Algorithmen zur Verwaltung: Heaps für die
58     Verwaltung der Timer (keine lineare Liste oder ähnliches wie alle
59 root 1.1 anderen), Arrays, wenn es um viele Watcher geht, Listen, wenn es nur
60     wenige (meist einen) geht. Auch geht Libev nicht vor jedem Pollen alle
61     seine Watcher (Event, Glib, ...) durch um zu sehen ob sie noch alle da
62     sind.
63    
64     Dadurch skaliert EV nicht nur hervorragend in die Bereiche von
65     hunderttausenden Filehandles und Timern (falls man das braucht), sondern
66     ist auch mit wenigen davon sehr schnell (was mindstens genauso wichtig
67     ist).
68    
69 root 1.2 Auch die Perl-Schnittstelle selbst wurde geschrieben, um unnötige
70 root 1.1 Laufzeitkosten zu vermeiden: EV erzeugt einen Watcher immerhin 40 mal
71     schneller als Event und fast viermal so schnell wie Glib, womit EV +
72     AnyEvent immer noch schneller sind als "nacktes" Glib.
73    
74     Auch der Speicherverbrauch ist sehr gering: EV-Watcher sind schlichte
75     geblesste Strings die den Libev-Watcher enthalten. Diese wiederum sind im
76     Vergleich mit anderen Bibliotheken recht klein (weniger als 100 Bytes).
77    
78 root 1.3 =item Skalierbarkeit
79 root 1.1
80     Nicht nur die Algorithmen, auch die Schnittstelle zum Betriebssystem muss
81     skalierbar sein, und das bedeutet leider auch: nicht portabel. Jedes
82     Betriebssystem braucht sein eigenes Rezept und daher auch sein eigenes
83 root 1.2 Backend: Libev unterstützt Epoll, Kqueue und Solaris' Event Ports.
84 root 1.1
85     Einige Backends sind sehr begrenzt (Kqueue z.B. ist auf fast allen System
86     kaputt und supported bestenfalls Sockets) aber in ein anderen Backend
87     integrierbar, so kann man z.B. ein Kqueue-Backend (supported nur Sockets
88 root 1.2 zuverlässig auf FreeBSD) in ein "herkömmliches" Poll-basiertes Backend
89 root 1.1 integrieren.
90    
91 root 1.3 =item Watcher-Typen
92 root 1.1
93 root 1.2 Neben den üblichen I/O Watchern und Timern (derer es zwei grundsätzlich
94 root 1.1 verschiedene Typen in EV gibt), bietet EV noch speziellere
95 root 1.2 Watchertypen für POSIX-Signale, Prozess-Watcher, Idle-Watcher (für
96 root 1.1 "Hintergrundprozesse"), Prepare/Check-Watcher, mit denen man EV-fremde
97     Event-Modelle oder -Benutzer integrieren kann und Stat-Watcher, mit denen
98 root 1.2 man Dateisystemobjekte auf Veränderungen überwachen kann. Ganz spezielle
99 root 1.1 Watcher, um C<fork()> zu erkennen oder andere Backends zu integrieren gibt
100 root 1.2 es natürlich auch.
101 root 1.1
102 root 1.3 =item Kompatibilität
103    
104     EV wird von AnyEvent voll unterstützt, so daß Module oder Programme, die
105     AnyEvent benutzen, ohne Quellcodeänderungen sofort mit EV benutzt werden
106     können.
107    
108     =item C-Schnittstelle
109 root 1.1
110     Das EV-Modul exportiert die komplette Libev-API auch auf XS-Ebene, was
111     so nur noch Event bietet (bei anderen Modulen muss man auf positive
112     Eigenschaften von Shared-Libraries vertrauen).
113    
114     =back
115    
116    
117     =head1 Warum?
118    
119     EV ist, wie die zugrunde liegende Libev, sehr jung (Ende 2007 erschien
120     die erste Version), und die Entstehung war mehr Zufall als Absicht: Seit
121 root 1.2 ca. 1998 benutze ich regelmäßig und viel das Event-Modul, welches
122     problemlos als das ausgereifteste der verfügbaren Event-Module gelten
123 root 1.1 durfte.
124    
125 root 1.2 Im Laufe der Jahre stieß ich auf eine Menge kleinerer Probleme mit anderen
126 root 1.1 Event-Modellen (siehe weiter oben), doch die Anstrengung, eine eigene
127     Bibliothek zu schreiben, wollte ich nicht aufbringen.
128    
129 root 1.2 Speicherverbrauch war eigentlich nie ein Problem, die Beschränkung auf
130 root 1.1 C<select> und C<poll> schon eher, aber man hat ja schnelle Rechner die
131     das kompensieren (und wenn man nicht drauf achtet, was die Rechenzeit
132 root 1.2 verbraucht, stört es ja sowieso nie).
133 root 1.1
134 root 1.2 An die echten Fehler in z.B. Event gewöhnt man sich mit der Zeit, bzw.
135 root 1.1 findet mehr oder weniger akzeptable Umleitungen um das Problem, oder
136 root 1.2 vermeidet den Einsatz in solchen Fällen ganz.
137 root 1.1
138     Eines Tages kam jedoch die Notwendigkeit, wirklich viele Filehandles
139 root 1.2 effizient zu verwalten, und da Libevent sowas wie der Standard für
140     Performance zu sein schien und es dafür keine Perl-Schnittstelle gab
141     (das ist falsch, es gab eine, nämlich C<Event::Lib>, aber ich habe sie
142 root 1.1 trotz suchens auf CPAN nicht wahrgenommen), fing' ich an, eine eigene
143     Schnittstelle zu Libevent zu schreiben.
144    
145 root 1.2 Wenig erfreut war ich, als ich herausfand, daß Libevent die gleichen
146 root 1.1 Probleme wie Tk hat (nicht mehrere Watcher auf einem Filehandle) und auch
147 root 1.2 nicht wenige Schwächen in der API. Das Erlebnis war derart frustrierend,
148     daß ich mich entschieden habe, endgültig mein eigenes Ding' zu machen. Wie
149 root 1.1 erwartet, war es mehr Arbeit, als ich eigentlich investieren wollte,
150 root 1.2 aber nach einigen Monaten hatte ich etwas gebrauchsfähiges, daß sogar
151     bei Benchmarks gegenüber dem "Standard" (nämlich Libevent) hervorragend
152 root 1.1 abschneidet.
153    
154     Da die meisten meiner Module AnyEvent benutzen, und die API von EV (im
155 root 1.2 Geiste...) der von Event ähnelt, war die Umstellung aller meiner wichtigen
156 root 1.1 Projekte (gvpe, rxvt-unicode, Deliantra, mein Webserver sowie mehrere
157 root 1.2 kommerzielle Anwendungen) kein Problem, was natürlich ein sehr guter Test
158     für das Modul war (und ist).
159 root 1.1
160     EV ist somit das Ergebnis von knapp 10 Jahren Erfahrung mit
161 root 1.2 anderen Event-Modellen, deren Stärken und Schwächen und deren
162 root 1.1 Perl-Schnittstelle: alle mir bekannten Probleme wurden adresssiert.
163    
164    
165     =head1 Wie?
166    
167 root 1.3 Nach so viel Blabla wird's Zeit für Code-Beispiele.
168 root 1.1
169     Einen langweiligen Timer realisiert man so.
170    
171     use EV;
172    
173     my $timer = EV::timer 1, 2, sub {
174 root 1.3 warn "zeitstempel: " . EV::now;
175 root 1.1 warn "man rief' mich auf nach 1s dann alle 2s. yeah.";
176     };
177    
178     EV::loop;
179    
180     Das gibt nach einer Sekunde ein warn, und danach alle zwei Sekunden, bis
181     in alle Ewigkeit/Reset/SIGINT etc.
182    
183 root 1.2 EV::loop kehrt automatisch zurück, wenn es keine aktiven Watcher mehr gibt
184 root 1.1 (das nennt man "Deadlock"):
185    
186     my $delay = EV::timer 1, 0, sub {
187     warn "nur einmal nach 1s werde ich aufgerufen.\n";
188     };
189    
190     EV::loop;
191    
192 root 1.2 Das läuft nur für eine Sekunde. Wichtig ist, daß man das Watcher-Objekt,
193 root 1.1 das der Konstruktor liefert, auch aufbewahrt, denn wenn man es vergisst,
194     wird der Watcher automatisch deaktiviert. Hier kehrt C<EV::loop> sofort
195 root 1.2 zurück, wenn keine sonstigen Watcher aktiv sind, denn der Timer wird
196     erzeugt, gestartet und sofort wieder gelöscht:
197 root 1.1
198     EV::timer 10, 10, sub { };
199     EV::loop;
200    
201     I/O-Watcher gehen genauso einfach. Das folgende Beispiel "loop"ed so
202     lange, bis man auf STDIN eine Zeile die mit C<q> beginnt, eingibt (z.b.
203     "quit"):
204    
205     my $stdin_watcher = EV::io *STDIN, EV::READ, sub {
206     my $line = <STDIN>;
207    
208 root 1.2 # $_[0] enthält das watcher-objekt
209 root 1.1 $_[0]->stop if $line =~ /^q/;
210     };
211    
212     EV::loop;
213    
214 root 1.3 Alternativ kann man den Watcher auch "einfach" C<undef>'en (die
215     Sichtbarkeit von my-Variablen in Perl macht das ein bißchen eklig):
216 root 1.1
217     my $stdin_watcher;
218     $stdin_watcher = EV::io *STDIN, EV::READ, sub {
219     my $line = <STDIN>;
220    
221     undef $stdin_watcher if $line =~ /^q/;
222     };
223    
224     EV::loop;
225    
226 root 1.2 =head2 Spezialitäten
227 root 1.1
228 root 1.3 Nach dem Standardkram, hier ein paar Spezialitäten, die so wahrscheinlich
229     nur mit EV gehen.
230    
231     =over 4
232    
233     =item Ein Watcher, der jeden Tag um Mitternacht lokaler Zeit seinen
234     Callback aufruft (genauer: zur nächsten Mitternacht nach 23:59:59 des
235     aktuellen Tages. Für Details muß ich auf die Manpage verweisen):
236    
237     use EV;
238     use Time::Local;
239    
240     my $geisterstunde = EV::periodic 0, 0, sub {
241     my ($w, $now) = @_;
242    
243     # berechne den nächsten Zeitpunkt des "Feuerns":
244     my ($y, $m, $d) = (localtime $now)[5, 4, 3];
245     1 + timelocal 59, 59, 23, $d, $m, $y
246     }, sub {
247     warn "buh!";
248     };
249    
250     Der erste Callback berechnet auf Zuruf von EV die nächste Mitternacht.
251     Sollte sich die Systemzeit verändern (z.B. ein paar Jahre zurückfallen),
252     ruft EV den Callback automatisch erneut auf.
253    
254     =item Überwache F</var/log/messages> auf Veränderungen (die Größe
255     sollte sich verändern, wenn man nur die mtime überwachen will, wird es
256     schwerer, wie in der Manpage beschrieben):
257    
258     my $logwatcher = EV::stat "/var/log/messages", 10, sub {
259     warn "/var/log/messages size is now ", ($_[0]->attr)[7];
260     };
261    
262     =item Überwachung einer Perl-Variable auf Veränderung zwischen zwei
263     Iterationen der EV-Ereignis-Schleife:
264    
265     my $var;
266    
267     my $oldval = $var;
268     my $checkvar = EV::prepare sub {
269     if ($oldval ne $var) {
270     warn "variable has changed from $oldval to $var\n";
271     $oldval = $var;
272     }
273     };
274    
275     =item Einbetten von Kqueue in ein anderes Backend (z.B. für FreeBSD, Mac
276     OS X), damit man wenigstens Sockets effizient benutzen kann:
277    
278     my $socket_loop;
279    
280     # Erzeuge eine Kqueue-loop WENN Kqueue unterstützt wird UND NICHT
281     # schon für die Standard-Schleife verwendet wird:
282     if (
283     (EV::backend & (EV::BACKEND_POLL | EV::BACKEND_SELECT))
284     && (EV::supported_backends & EV::embeddable_backends & EV::BACKEND_KQUEUE)
285     ) {
286     $socket_loop = new EV::Loop EV::BACKEND_KQUEUE | EV::FLAG_NOENV;
287     }
288    
289     # Ansonsten benutze die Standard-Schleife:
290     $socket_loop ||= EV::default_loop;
291    
292     # Erzeuge einen Socket-Watcher:
293     my $watcher = $socket->io ($socket_fh, EV::READ, sub { ... });
294    
295     Statt die globalen Konstruktoren in EV zu verwenden kann man sie auch
296     über Methodenaufrufe eines EV::Loop-Objektes erreichen.
297    
298     =item Benutzung von EV von XS aus.
299    
300     Im Makefile.PL:
301    
302     use EV::MakeMaker;
303    
304     WriteMakefile (EV::MakeMaker::ev_args (
305     ... normale MakeMaker-Argumente
306     ));
307    
308     Im .XS-File:
309    
310     #include "EVAPI.h"
311    
312     static ev_timer mytimer;
313    
314     static void
315     mycb (ev_timer *w, int revents)
316     {
317     // zuerst nach 1s, dann alle 2s
318     }
319    
320     ...
321    
322     BOOT:
323     I_EV_API ("<modulname>");
324     ev_timer_init (&mytimer, mycb, 1., 2.);
325     ev_timer_start (EV_DEFAULT, &mywatcher);
326    
327     =back
328    
329 root 1.1
330     =head1 Und wie noch?
331    
332 root 1.3 Auf CPAN gibt es einige Module, die sich direkt oder indirekt mit EV beschäftigen.
333    
334     =head2 Module, die direkt mit EV in Bezug stehen
335    
336     =over 4
337    
338     =item EV::ADNS
339    
340     Eine Schnittstelle zu libadns, einer asynchronen DNS-Resolver-Bibliothek,
341     die sich in EV integriert. Ein gutes Beispiel, wie man eine "EV-fremde"
342     C-Bibliothek in libev/EV integriert (auf XS-Ebene).
343    
344     =item Net::SNMP::EV
345    
346     Ein Hack, der die Non-Blocking-Aufrufe des Net::SNMP-Moduls in EV
347     integriert: Net::SNMP unterstützt parallel Anfragen, benutzt aber sein
348     eigenes, internes, Event-Modell. Dieses Modul bildet es auf EV ab.
349    
350     Einfaches Laden genügt und schon benutzt Net::SNMP für I/O und Timer das
351     EV-Modul.
352    
353     Das Modul ist ein gutes Beispiel dafür, wie man "EV-fremde" Perl-Module
354     in EV integrieren kann (auf Perl-Ebene).
355    
356     =item Glib::EV
357    
358     Dieses Modul ersetzt das "backend" (normalerweise poll oder select) von
359     Glib (bzw. libglib) durch EV. Dies ist üblicherweise (z.B. wenn man
360     epoll verwendet) etwas langsamer als Glib selbst, wenn man jedoch wenige
361     Glib-Watcher (z.B. nur die von Gtk+) und viele (also mehr als 1-2 :)
362     EV-Watcher verwendet, ergibt dies einen Nettogewinn, vor allem, weil Glib
363     das ineffizienteste Event-Modell ist, daß ich je gesehen habe.
364    
365     Laden des Moduls genügt, man muss jedoch die Hauptschleife von Glib
366     oder Gtk+ benutzen, C<EV::loop> hilft nicht viel, da Glib die Kontrolle
367     behalten muss.
368    
369     Arbeitet ähnlich wie EV::ADNS, ist aber komplexer.
370    
371     =item EV::Glib
372    
373     Mit diesem Modul kann man EV-Watcher in bestehenden Glib-Programmen
374     benutzen.
375    
376     Fast identisch mit Glib::EV, jedoch wird hier der "MainContext" von Glib
377     in EV integriert, d.h. die Watcher von Glib werden bei Iterationen von EV
378     mitgeprüft. Dies ist genauso ineffizient wie C<Glib::EV>, es amortisiert
379     sich aber sobald man einige EV-Watcher benutzt.
380    
381     Laden des Moduls genügt, man muss jedoch die Hauptschleife von EV
382     aufrufen, was dazu führt, daß sowohl die EV-eigenen als auch die
383     Glib-eigenen Watcher betreut/abgefragt werden.
384    
385     Mit diesem Modul kann man Glib (doer Gtk) in einem EV-Programm benutzen.
386    
387 root 1.4 =item POE::Loop::EV
388    
389     Offenbar ein Backend zu EV von POE. Hab's nicht ausprobiert, die Kerle
390     sollten aber AnyEvent benutzen, damit kriegen sie automatisch EV und alles
391     andere was AnyEvent unterstützt.
392    
393 root 1.3 =back
394    
395     =head2 Module, die EV benutzen können
396    
397     =over 4
398    
399     =item AnyEvent
400    
401     AnyEvent unterstützt C<EV> (und C<Coro::EV>) explizit, d.h. bestehende
402     Anwender von AnyEvent kommen sofort in den Genuß von EV.
403    
404     AnyEvent + EV ist sogar schneller als die meisten (bzw. alle von mir
405     getesteten) Event-Module, und das, obwohl AnyEvent vergleichsweise viel
406     Aufwand leisten muss.
407    
408     =item Coro, Coro::EV
409    
410     Die Filehandle- und Timer-Abstraktionen von C<Coro> (C<Coro::Handle>,
411     C<Coro::Socket>, C<Coro::Timer>) unterstützen EV direkt (über
412     C<Coro::EV>). EV ist das effizienteste direkt unterstützte Event-Modul in
413 root 1.4 C<Coro::Coro>.
414    
415     C<Coro::EV> hat neben der Unterstützung von C<Coro::Handle> noch
416     Hilfsfunktionen, um einmalige Verzögerungen u.ä. in Coroutinen zu
417     benutzen.
418 root 1.3
419 root 1.4 Sollte C<EV::ADNS> installiert sein, so wird es von C<Coro::Util> (und
420     damit auch C<Coro::Socket>) für DNS-Anfragen benutzt, statt einen
421     separaten Prozess zu forken (was sehr langsam sein kann).
422    
423     =item IO::AIO / Coro::AIO
424    
425     Haben direkt nichts mit EV zu tun (außer, daß sie mit EV gut
426     funktionieren), aber für jedes ereignisgesteuerte Programm ein Muss, da
427     man damit Ereignisgesteuert (und asynchron) auf das Dateisystem zugreifen
428     kann, mit Coro::AIO sogar ähnlich einfach wie mit den eingebauten
429     Perl-Funktionen.
430    
431     =item BDB / Coro::BDB
432 root 1.3
433 root 1.4 Was IO::AIO für's Dateisystem ist, ist BDB für die Berkeley DB, nämlich
434     ein Modul, mit dem man Ereignisgesteuert Berkeley DB benutzen kann.
435 root 1.3
436     =back
437    
438     =head2 Werbung
439    
440     Ok, pure Werbung, aber es gibt noch andere Programme, die Libev benutzen.
441    
442     =over 4
443    
444     =item GVPE, das GNU Virtual Private Ethernet
445    
446     In der nächsten (2.2) release wird es Libev benutzen. Bringt total viel :->
447    
448     http://savannah.gnu.org/projects/gvpe
449    
450     =item Rxvt-Unicode
451    
452     Das allerbeste Terminal überhaupt, vor allem, weil es einen
453     Perl-Interpreter embeddet. Und dazu benutzt es (ab version 8.4 oder so)
454     auch Libev. Leider ohne EV, sondern mit eigener Perl-Schnittstelle, aber
455     na gut, man kann nicht alles haben.
456    
457     http://software.schmorp.de/pkg/rxvt-unicode.html
458    
459     =item Deliantra
460    
461     Das allergeilste Online-RPG wo gibt, vor allem, weil es Perl embeddet,
462     einen ganzen vollen Interpreter. Nicht nur, es benutzt auch Coro, IO::AIO,
463     BDB uvm., und der OpenGL-supergeile-Grafik-mach-Client ist ebenfalls
464     in Perl geschrieben und benutzt EV, BDB... den Rest kann man sich
465     vorstellen. Ein Must-See-Must-Have, vor allem wegen des super Retro-Stils!
466    
467     =item OpenMPI
468    
469     Wird, nach eigener Aussage, in der nächsten Release ebenfalls Libev
470     benutzen. Kein embeddetes Perl in Sicht.
471    
472     =back
473    
474    
475 root 1.2 =head1 Hä?
476 root 1.1
477 root 1.3 Ich kann (bzw. wollte) hier nur eine kurze Übersicht geben bzw. einen
478     Geschmack vermitteln und eventuell neugierig machen.
479    
480     Mehr Informationen gibt es auf der libev-Manpage:
481    
482     http://libev.schmorp.de/
483    
484     Von dort gibt es auch einen Link zu EV (der Perl-Schnittstelle), dessen
485     Doku, und einem Benchmark mit Libevent:
486    
487     http://pod.tst.eu/http://cvs.schmorp.de/EV/EV.pm
488     http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod
489     http://libev.schmorp.de/bench.html
490    
491 root 1.1 =back
492    
493 root 1.3 =head1 Autor
494    
495     Marc Lehmann <schmorp@schmorp.de>.
496