Tutorial
[Linux] Deamon Programmieren
von ThePuppetMaster | Seite 1 von 1 |
Dieses Tutorial wird sich mit der Entwicklung eines sogenannten "Deamon" für Linux Betriebssysteme beschäftigen.
Zuerst einmal, Was ist eigentlich ein "Deamon"?
Nun, als Deamon bezeichnet man unter Linux Programme, die selbstständig arbeiten und nicht auf eine Konsole angeweisen sind.
Ein Kurzes Beispiel hierzu. Startet man in einer Konsole unter Linux ein Programm, z.B. mit
./test
und dieses Programm arbeitet eine Endlosschleie an, dann kann man das Programm entweder dadurch beenden als wir es via "kill" befehl stopen oder wir beenden die Konsole, von der aus das Programm bestartet wurde.
Diese Konsole ist somit als Arbeitsfläche für dieses Programm anzusehen.
Wird diese Konsole beendet, kann das Programm ebenfalsl nicht weiter ausgeführt werden, da seine Arbeitsfläche nicht mehr existiert.
Dadurch wird das Programm ebenfalls beenden.
Um dieses Programm unabhängig laufen zu lassen, ohne das eine Konsole zu deren Existenz nötig ist, reicht es aus, wenn nach dem Programmname ein "&" angehängt wird.
./text&
Hierdurch wird der Konsole mitgeteilt, das dieses Programm unabhängig von der Konsole gestartet werden soll.
Um dieses Programm nun zu beenden gibt es nur noch die möglichkeit es via "kill" befehl zu terminieren. Siehe hierzu "kill --help"
Aber schreiben wir uns erstmal ein kleines "test" Programm
Do
Print "Ich bin ein Deamon!"
sleep 5000, 1
Loop
End 0
Dieses kleine Programm gibt alle 5 Sekunden einen Text aus.
Starten wir dieses Programm auf der Konsole mit dem oben genannten Befehl.
./test
(ohne &)
dann läuft dieses Programm endlos lang. Wärend dieser Zeit können wir keine weiteren Eingaben mehr auf der Konsole ausführen.
Um dieses Programm zu beenden reicht ein STRG+C. Alternativ könnte man eine 2te Konsole öffnen und per
PS aux | grep test
Die PID des Programmes anzeigen lassen.
tpm@workboard-0:~$ ps aux | grep test
tpm 5268 0.0 0.0 10288 804 pts/2 Sl+ 08:46 0:00 ./test
tpm 5271 0.0 0.0 2872 752 pts/5 R+ 08:46 0:00 grep test
tpm@workboard-0:~$
Die PID ist der erste Zahlenwert pro Zeile in der auflistung von "PS". Wir suchen also am ende nach dem Programm "./test" und finden vorne die PID: 5268.
Der erste String in der Auflistung ist übrigens der Benutzername des Benutzer, welche dieses Programm ursprünglich gestartet hat.
Um das Programm jetzt zu beenden reicht ein
kill 5268
Woraufhin sich das Programm ohne weitere Meldungen beenden, und in der Auflistung nicht mehr finden lässt.
Starten wir das Programm jedoch mit dem "Deamon" zusatz "&", ist die Konsole wieder für und frei, nachdem das Programm erfolgreich inizialisiert wurde.
./test&
Hierbei ändert sich in der "PS" liste nichts, bis auf die Tatsache, das bei den Flag's anstat eines "Sl+" ein "S" zu sehen ist. Dies deutet das "Deamon" an.
Nachdem das Programm gestartet wurde sollte die Konsole folgende dinge ausgeben.
tpm@workboard-0:~/data/work/code/fb/sonstiges/tut_deamon$ ./test&
[1] 5286
tpm@workboard-0:~/data/work/code/fb/sonstiges/tut_deamon$ Ich bin ein Deamon!
Ich bin ein Deamon!
Ich bin ein Deamon!
Hierbei wird nach dem Return die PID ausgegeben (in Klammern), gefolgt vom Promt.
Da das Programm alle 5 Sekunden seinen Text ausgibt, sehen wir diesen Text jetzt.
Die Arbeit mit der Konsole ist regulär möglich. Das einzigste, was ständig, alle 5 Sek. auftaucht, ist der Text, welcher sich ständig in unsere Eingaben oder einfach so, wiederzufinden ist.
Hier hilft jetzt auch kein STRG+C mehr, um solche Meldungen abzuschalten.
Das Programm wurde beim start automatisch mit der Konsolen Ausgabe verknüpft, und kann durch den "Print" Befehl text auf der Konsole ausgeben.
Aber ACHTUNG! Der "Input" Befehl funktioniert NICHT mehr! EInzigst die Ausgabe wird auf der Konsole ausgegeben. Jedoch auch nur auf der Konsole, von dem aus das Programm gestartet wurde.
Wird diese Konsole geschlossen / beendet, ist es nicht emrh möglich die Ausgaben auf diese Konsole zurück zu hohlen. Zumindest nicht ohne etwas Aufwand.
Das Programm kann jetzt nur noch via "kill" Befehl beendet werden.
Hierbei gibt die Konsole (sofern die Original Konsole noch existiert, in der das Programm gestartet wurde), das Ende des Programmes aus.
tpm@workboard-0:~/data/work/code/fb/sonstiges/tut_deamon$
[1]+ Beendet ./test
tpm@workboard-0:~/data/work/code/fb/sonstiges/tut_deamon$
Diese Vorgehensweise ist für uns natürlich nicht sonderlich schön. Ein Deamon via Kill zu beenden ist nicht nur unschön, sondern auch ungeeignet! Gehen wir einmal davon aus, das unser Programm vor seinen Ende noch aufgaben auszuführen hat, dann würden diese niemals durchgeführt.
Ein "Kill" auf ein Programm führt unweigerlich zu seinem Ende. Einzigste möglichkeit dies zu verhindern, bzw. (korreckt gesprochen) dies zu verlangsamen, ist das Abfangen des Kill's über die sogenannten "Signale".
Ein Signal ist unter Linux mit einem Callback zu realisieren, das aufgerufen wird, sobald bestimmte Kill-Ereignisse auftreten.
Unter Linux existieren derzeit rund 30 offizielle Signalvarianten.
Signal-Name | Dezimalwert | Beschreibung |
---|---|---|
SIGHUP | 1 | Hangup (POSIX) |
SIGINT | 2 | Terminal interrupt (ANSI) |
SIGQUIT | 3 | Terminal quit (POSIX) |
SIGILL | 4 | Illegal instruction (ANSI) |
SIGTRAP | 5 | Trace trap (POSIX) |
SIGIOT | 6 | IOT Trap (4.2 BSD) |
SIGBUS | 7 | BUS error (4.2 BSD) |
SIGFPE | 8 | Floating point exception (ANSI) |
SIGKILL | 9 | Kill(can't be caught or ignored) (POSIX) |
SIGUSR1 | 10 | User defined signal 1 (POSIX) |
SIGSEGV | 11 | Invalid memory segment access (ANSI) |
SIGUSR2 | 12 | User defined signal 2 (POSIX) |
SIGPIPE | 13 | Write on a pipe with no reader, Broken pipe (POSIX) |
SIGALRM | 14 | Alarm clock (POSIX) |
SIGTERM | 15 | Termination (ANSI) |
SIGSTKFLT | 16 | Stack fault |
SIGCHLD | 17 | Child process has stopped or exited, changed (POSIX) |
SIGCONT | 18 | Continue executing, if stopped (POSIX) |
SIGSTOP | 19 | Stop executing(can't be caught or ignored) (POSIX) |
SIGTSTP | 20 | Terminal stop signal (POSIX) |
SIGTTIN | 21 | Background process trying to read, from TTY (POSIX) |
SIGTTOU | 22 | Background process trying to write, to TTY (POSIX) |
SIGURG | 23 | Urgent condition on socket (4.2 BSD) |
SIGXCPU | 24 | CPU limit exceeded (4.2 BSD) |
SIGXFSZ | 25 | File size limit exceeded (4.2 BSD) |
SIGVTALRM | 26 | Virtual alarm clock (4.2 BSD) |
SIGPROF | 27 | Profiling alarm clock (4.2 BSD) |
SIGWINCH | 28 | Window size change (4.3 BSD, Sun) |
SIGIO | 29 | I/O now possible (4.2 BSD) |
SIGPWR | 30 | Power failure restart (System V) |
Jedes Signal hat "theoretisch" unterschiedliche Aufgaben zu vollführen. Wenn man sich (was man unbedingt machen sollte) an die Ereignisse hält, dann ist es möglich ein Programm so zu gestalten, dass es nicht nur auf das "Kill" reagiert, sondern auch auf alle anderen eintretenden Ereignisse, welche den Regulären Ablauf behindern könnten.
Um diese Signale zu empfangen, müssen diese dem Linux-System mitgeteilt werden. Hierfür existiert eine Function in der C-Bibliothek, und kann einfach im Programm Deklariert werden.
Declare Function Signal cdecl lib "c" alias "signal" (ByVal V_Signal As Integer, V_Function As Any Ptr) as Integer
Anschliessend müssen nur noch Callback Routinen hinzugefügt werde, um dem Programm eine Schnittstelle zu bieten und Signale Empfangen zu können.
Sub Signal_SIGINT cdecl()
Print "STRG+C wurde gedrückt!"
end sub
Im oberen Code sieht man die Callback Routine für das "Terminal-Interupt" Signal.
Zu guter letzt registrieren wir dieses Callback mithilfe der Deklarierten Funktion am Linux-System.
Signal(SIGINT, @Signal_SIGINT)
Erweitern wir unser Programm nun um diese Signal-Funktionen.
'Zuerst die Function deklarieren, welche uns ermöglicht das Signal von diesem Programm zu setzen.
Declare Function Signal cdecl lib "c" alias "signal" (ByVal V_Signal As Integer, V_Function As Any Ptr) as Integer
'Anschliessend Die Konstanten für die Signalvarianten
Const SIGHUP = 1 'Hangup (POSIX)
Const SIGINT = 2 'Terminal interrupt (ANSI)
Const SIGQUIT = 3 'Terminal quit (POSIX)
Const SIGILL = 4 'Illegal instruction (ANSI)
Const SIGTRAP = 5 'Trace trap (POSIX)
Const SIGIOT = 6 'IOT Trap (4.2 BSD)
Const SIGBUS = 7 'BUS error (4.2 BSD)
Const SIGFPE = 8 'Floating point exception (ANSI)
Const SIGKILL = 9 'Kill (can't be caught or ignored) (POSIX)
Const SIGUSR1 = 10 'User defined signal 1 (POSIX)
Const SIGSEGV = 11 'Invalid memory segment access (ANSI)
Const SIGUSR2 = 12 'User defined signal 2 (POSIX)
Const SIGPIPE = 13 'Write on a pipe with no reader, Broken pipe (POSIX)
Const SIGALRM = 14 'Alarm clock (POSIX)
Const SIGTERM = 15 'Termination (ANSI)
Const SIGSTKFLT = 16 'Stack fault
Const SIGCHLD = 17 'Child process has stopped or exited, changed (POSIX)
Const SIGCONT = 18 'Continue executing, if stopped (POSIX)
Const SIGSTOP = 19 'Stop executing (can't be caught or ignored) (POSIX)
Const SIGTSTP = 20 'Terminal stop signal (POSIX)
Const SIGTTIN = 21 'Background process trying to read, from TTY (POSIX)
Const SIGTTOU = 22 'Background process trying to write, to TTY (POSIX)
Const SIGURG = 23 'Urgent condition on socket (4.2 BSD)
Const SIGXCPU = 24 'CPU limit exceeded (4.2 BSD)
Const SIGXFSZ = 25 'File size limit exceeded (4.2 BSD)
Const SIGVTALRM = 26 'Virtual alarm clock (4.2 BSD)
Const SIGPROF = 27 'Profiling alarm clock (4.2 BSD)
Const SIGWINCH = 28 'Window size change (4.3 BSD, Sun)
Const SIGIO = 29 'I/O now possible (4.2 BSD)
Const SIGPWR = 30 'Power failure restart (System V)
'Signal für das 'Terminal-Interrupt' (STRG+C)
Sub Signal_SIGINT cdecl()
Print "STRG+C wurde gedrückt!"
end sub
'Signal für das 'Terminal-Terminate' (Beenden des Terminals)
Sub Signal_SIGQUIT cdecl()
Print "Terminal wurde beenden!"
end sub
'...
Sub Signal_SIGKILL cdecl ()
Print "KILL wurde ausgeführt!"
end sub
'...
Sub Signal_SIGTERM cdecl ()
Print "Programm soll beendet werden! (Vermutlich von start-stop-deamon)"
end sub
Sub Signal_SIGCHLD cdecl ()
Print "Arbeitsfläche wurde beenden! Eigenes Programm muss ebenfalls beendet werden!"
end sub
Sub Signal_SIGSTOP cdecl ()
Print "Programmausführung muss gestopt werden!"
end sub
'Die Signal-Callbacks dem Linux-System mitteilen
Signal(SIGINT, @Signal_SIGINT)
Signal(SIGQUIT, @Signal_SIGQUIT)
Signal(SIGKILL, @Signal_SIGKILL)
Signal(SIGTERM, @Signal_SIGTERM)
Signal(SIGCHLD, @Signal_SIGCHLD)
Signal(SIGSTOP, @Signal_SIGSTOP)
'Programm wie bisher bekannt
Print "Deamon-Start! Commandline: >"; Command(); "<"
Do
Print "Ich bin ein Deamon!"
sleep 5000, 1
Loop
Print "Deamon-Ende!"
End 0
Nach dem Kompilieren und starten im NICHT Deamon-Modus, läuft unser Programm nun regulär, und wie gewohnt ab.
Drücken wir jetzt STRG+C, wird das Programm nicht mehr beendet! Statdessen erhalten wir eine Meldung von unserem Programm, dass es das Signal empfangen hat.
Wir müssen folglich das Beenden von Hand durchführen und ein "End" in die entsprechende Routine einfügen.
Sub Signal_SIGINT cdecl()
Print "STRG+C wurde gedrückt!"
Print "Programm wird beenden!"
End 0
end sub
Das gleiche gilt übrigens auch für die meisten anderen Signale. Eine Ausnahme ist z.B. SIGILL (Illegal Instruction), SIGTRAP, ..., welches nicht nur das Signal an unser Programm sendet, sondern dieses auch gleich Beendet.
Eine weitere Ausnahme ist das SIGKILL Signal. Dieses Signal nimmt eine art Sonderstellung ein. Hierbei wird das Programm sofort beendet, ohne das dies dem Programm mitgeteilt wird.
Zum testen aller vom Programmierer verwendeten Signale, kann man den "Kill" Befehl nutzen. Hiebrei gibt man die option "-n <signalnummer>" an.
Kill -n 9 1234
Kill sendet daraufhin an die PID:1234 das Signal:9 und, je nach Signalnummer, terminiert es das Programm zusätzlich.
Und damit wäre die Grundlage für ein "Deamon" geschaffen.
Es kann ein Normales Programm geschrieben werden, das als Deamon läuft und auf Signale von ausen Reagieren kann. Hierbei muss nur beachtet werden, das bestimmte Signale kein Beenden ausführen, und wir dies von Hand in der entsprechenden Routine durchführen müssen.
MfG + HF
TPM
Zusätzliche Informationen und Funktionen | |||||||
---|---|---|---|---|---|---|---|
|
|