Buchempfehlung
Mikrocomputertechnik mit Controllern der Atmel AVR-RISC-Familie
Mikrocomputertechnik mit Controllern der Atmel AVR-RISC-Familie
Umfassend, aber leicht verständlich führt dieses Buch in die Programmierung von ATMEL AVR Mikrocontrollern ein. [Mehr Infos...]
FreeBASIC-Chat
Es sind Benutzer im FreeBASIC-Chat online.
(Stand:  )
FreeBASIC bei Twitter
Twitter FreeBASIC-Nachrichten jetzt auch über Twitter erhalten. Follow us!

Tutorial

[Linux] Deamon Programmieren

von MitgliedThePuppetMasterSeite 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-NameDezimalwertBeschreibung
SIGHUP1Hangup (POSIX)
SIGINT2Terminal interrupt (ANSI)
SIGQUIT3Terminal quit (POSIX)
SIGILL4Illegal instruction (ANSI)
SIGTRAP5Trace trap (POSIX)
SIGIOT6IOT Trap (4.2 BSD)
SIGBUS7BUS error (4.2 BSD)
SIGFPE8Floating point exception (ANSI)
SIGKILL9Kill(can't be caught or ignored) (POSIX)
SIGUSR110User defined signal 1 (POSIX)
SIGSEGV11Invalid memory segment access (ANSI)
SIGUSR212User defined signal 2 (POSIX)
SIGPIPE13Write on a pipe with no reader, Broken pipe (POSIX)
SIGALRM14Alarm clock (POSIX)
SIGTERM15Termination (ANSI)
SIGSTKFLT16Stack fault
SIGCHLD17Child process has stopped or exited, changed (POSIX)
SIGCONT18Continue executing, if stopped (POSIX)
SIGSTOP19Stop executing(can't be caught or ignored) (POSIX)
SIGTSTP20Terminal stop signal (POSIX)
SIGTTIN21Background process trying to read, from TTY (POSIX)
SIGTTOU22Background process trying to write, to TTY (POSIX)
SIGURG23Urgent condition on socket (4.2 BSD)
SIGXCPU24CPU limit exceeded (4.2 BSD)
SIGXFSZ25File size limit exceeded (4.2 BSD)
SIGVTALRM26Virtual alarm clock (4.2 BSD)
SIGPROF27Profiling alarm clock (4.2 BSD)
SIGWINCH28Window size change (4.3 BSD, Sun)
SIGIO29I/O now possible (4.2 BSD)
SIGPWR30Power 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
  • Das Tutorial wurde am 11.09.2009 von MitgliedThePuppetMaster angelegt.
  • Die aktuellste Version wurde am 14.03.2010 von Mitgliedfrebas gespeichert.
  Bearbeiten Bearbeiten  

  Versionen Versionen