Tworzenie demona w Linuksie

W Linuksie chcę dodać demona, którego nie można zatrzymać i który monitoruje zmiany systemu plików. Jeśli zostaną wykryte jakiekolwiek zmiany, powinien napisać ścieżkę do konsoli, w której został uruchomiony, oraz znak nowej linii.

Mam już prawie gotowy kod zmieniający system plików, ale nie mogę wymyślić, jak utworzyć demona.

Mój kod pochodzi stąd: http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html

Co zrobić po widelcu?
int main (int argc, char **argv) {

  pid_t pID = fork();
  if (pID == 0)  {              // child
          // Code only executed by child process    
      sIdentifier = "Child Process: ";
    }
    else if (pID < 0) {
        cerr << "Failed to fork" << endl;
        exit(1);
       // Throw exception
    }
    else                                   // parent
    {
      // Code only executed by parent process

      sIdentifier = "Parent Process:";
    }       

    return 0;
}
Author: Jonathan Leffler, 2013-07-30

8 answers

W Linuksie chcę dodać demona, którego nie można zatrzymać i który monitoruje zmiany systemu plików. Jeśli zostaną wykryte jakiekolwiek zmiany, powinien napisać ścieżkę do konsoli, w której został uruchomiony + znak nowej linii.

Demony działają w tle i (zazwyczaj...) nie należy do TTY, dlatego nie możesz używać stdout/stderr w sposób, w jaki prawdopodobnie chcesz. Zazwyczaj Demon syslog (syslogd) jest używany do rejestrowania Wiadomości do plików (debug, błąd,...).

Poza tym istnieje kilka wymaganych kroków , aby daemonizować proces.


Jeśli dobrze pamiętam te kroki to:

  • fork wyłącz proces macierzysty i pozwól mu zakończyć się, jeśli rozwidlenie się powiedzie. - >Ponieważ proces macierzysty został zakończony, proces potomny działa teraz w tle.
  • setsid - Utwórz nową sesję. Proces wywołujący staje się liderem nowej sesji i procesu lider grupy nowych procesów. Proces jest teraz oddzielony od swojego terminala sterującego (CTTY).
  • Catch signals - ignorować i / lub obsługiwać sygnały.
  • fork again & pozwól procesowi nadrzędnemu zakończyć się, aby upewnić się, że pozbędziesz się wiodącego procesu sesji. (Tylko liderzy sesji mogą ponownie uzyskać TTY.)
  • chdir - Zmiana katalogu roboczego demona.
  • umask - Zmiana maski trybu Pliku według potrzeby demona.
  • close - zamyka wszystkie otwarte deskryptory plików, które mogą być dziedziczone po procesie nadrzędnym.

Aby dać ci punkt wyjścia: spójrz na ten szkielet kodu, który pokazuje podstawowe kroki:

/*
 * daemonize.c
 * This example daemonizes a process, writes a few log messages,
 * sleeps 20 seconds and terminates afterwards.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>

static void skeleton_daemon()
{
    pid_t pid;

    /* Fork off the parent process */
    pid = fork();

    /* An error occurred */
    if (pid < 0)
        exit(EXIT_FAILURE);

    /* Success: Let the parent terminate */
    if (pid > 0)
        exit(EXIT_SUCCESS);

    /* On success: The child process becomes session leader */
    if (setsid() < 0)
        exit(EXIT_FAILURE);

    /* Catch, ignore and handle signals */
    //TODO: Implement a working signal handler */
    signal(SIGCHLD, SIG_IGN);
    signal(SIGHUP, SIG_IGN);

    /* Fork off for the second time*/
    pid = fork();

    /* An error occurred */
    if (pid < 0)
        exit(EXIT_FAILURE);

    /* Success: Let the parent terminate */
    if (pid > 0)
        exit(EXIT_SUCCESS);

    /* Set new file permissions */
    umask(0);

    /* Change the working directory to the root directory */
    /* or another appropriated directory */
    chdir("/");

    /* Close all open file descriptors */
    int x;
    for (x = sysconf(_SC_OPEN_MAX); x>=0; x--)
    {
        close (x);
    }

    /* Open the log file */
    openlog ("firstdaemon", LOG_PID, LOG_DAEMON);
}
int main()
{
    skeleton_daemon();

    while (1)
    {
        //TODO: Insert daemon code here.
        syslog (LOG_NOTICE, "First daemon started.");
        sleep (20);
        break;
    }

    syslog (LOG_NOTICE, "First daemon terminated.");
    closelog();

    return EXIT_SUCCESS;
}


  • Skompiluj kod: gcc -o firstdaemon daemonize.c
  • Uruchom demona: ./firstdaemon
  • Sprawdź, czy wszystko działa poprawnie: ps -xj | grep firstdaemon

  • Wyjście powinno być podobne do tego jeden:

+------+------+------+------+-----+-------+------+------+------+-----+
| PPID | PID  | PGID | SID  | TTY | TPGID | STAT | UID  | TIME | CMD |
+------+------+------+------+-----+-------+------+------+------+-----+
|    1 | 3387 | 3386 | 3386 | ?   |    -1 | S    | 1000 | 0:00 | ./  |
+------+------+------+------+-----+-------+------+------+------+-----+

To co powinieneś zobaczyć to:

  • demon nie ma kontrolującego terminala ( TTY = ?)
  • identyfikator procesu nadrzędnego (PPID) jest 1 (proces init)
  • PID ! Oznacza to, że nasz proces nie jest liderem sesji.]} (z powodu drugiego widelca ())
  • ponieważ PID != SID nasz proces nie może przejąć kontroli nad TTY again

Czytanie sysloga:

  • Zlokalizuj swój plik syslog. Mój jest tutaj: /var/log/syslog
  • Do a: grep firstdaemon /var/log/syslog

  • Wyjście powinno być podobne do tego:

  firstdaemon[3387]: First daemon started.
  firstdaemon[3387]: First daemon terminated.


uwaga: W rzeczywistości chciałbyś również zaimplementować obsługę sygnału i poprawnie skonfigurować rejestrowanie (pliki, poziomy dziennika...).

Czytaj dalej:

 164
Author: Pascal Werkl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-29 06:49:55

man 7 daemon opisuje szczegółowo, jak tworzyć demona. Moja odpowiedź to tylko fragment tego podręcznika.

Istnieją co najmniej dwa rodzaje demonów:

  1. traditional sysv daemons (old-style),
  2. systemd daemons (new-style ).

Demony SysV

Jeśli interesuje Cię tradycyjnyDemon SysV , powinieneś zaimplementować następujący kroki :

  1. Zamknij wszystkie otwarte deskryptory plików z wyjątkiem standardowego wejścia , wyjście i błąd (tj. pierwsze trzy deskryptory plików 0, 1, 2). Gwarantuje to, że żaden przypadkowo przekazany deskryptor pliku nie pozostanie w procesie demona. W Linuksie najlepiej jest to zaimplementować poprzez iterację przez /proc/self/fd, z powrotem iteracji z deskryptora pliku 3 do wartości zwracanej przez getrlimit() dla RLIMIT_NOFILE.
  2. Reset wszystkie obsługa sygnałów do ich domyślnych. Najlepiej jest to zrobić poprzez iterację dostępnych sygnałów do limitu _NSIG i zresetowanie ich do SIG_DFL.
  3. Zresetuj maskę sygnału za pomocą sigprocmask().
  4. wyczyścić blok środowiska, usuwając lub resetując zmienne środowiskowe, które mogą negatywnie wpłynąć na środowisko uruchomieniowe demona.
  5. Call fork(), aby utworzyć proces w tle.
  6. u dziecka wywołanie setsid() aby odłączyć się od dowolnego terminala i Utwórz niezależną sesję .
  7. u dziecka wywołanie fork() ponownie, aby upewnić się, że demon nie może ponownie zdobyć terminala.
  8. Call exit() W pierwszym potomku, tak, że tylko drugie dziecko (rzeczywisty proces demona) pozostaje w pobliżu. Gwarantuje to, że proces daemona zostanie przekierowany do init / PID 1, tak jak powinny być wszystkie demony.
  9. w procesie demona połącz /dev/null do standardowego wejścia , wyjście i błąd .
  10. w procesie demona zresetuj umask do 0, tak aby tryby pliku przeszły do open(), mkdir() i takjak bezpośrednio kontrolować tryb dostępu do utworzonych plików i katalogów.
  11. w procesie demona, Zmień bieżący katalog na katalog główny (/), aby uniknąć tego, że Demon mimowolnie blokuje punkty montowania przed odmontowaniem.
  12. W proces demona, napisz demona PID (jako zwracane przez getpid()) do pliku PID, na przykład /run/foobar.pid (dla hipotetycznego demona "foobar"), aby upewnić się, że demon nie może być uruchomiony więcej niż jeden raz. Musi to być realizowane w sposób wolny od rasy tak, że plik PID jest aktualizowany tylko wtedy, gdy jest zweryfikowane w tym samym czasie, że PID wcześniej zapisane w pliku PID nie istnieje lub należy do zagranicznego procesu.
  13. w procesie demona, upuść uprawnienia, jeśli możliwe i stosowne.
  14. Z procesu demona, powiadom rozpoczęty oryginalny proces o zakończeniu inicjalizacji. Można to zaimplementować za pomocą nienazwanego potoku lub podobnego kanału komunikacyjnego, który jest tworzony przed pierwszym fork() i stąd dostępne zarówno w oryginale, jak i w procesie demona.
  15. Call exit() w pierwotnym procesie. Proces, który wywołał demona, musi być w stanie polegać na tym, że to exit() happens po inicjalizacja jest zakończona i wszystkie zewnętrzne kanały komunikacji są ustanowione i dostępne.

Zwróć uwagę na to Ostrzeżenie:

BSD daemon() funkcja nie powinna być używana, ponieważ implementuje tylko podzbiór tych kroków.

Demon, który musi zapewnić kompatybilność z systemami SysV powinien zaimplementować schemat wskazany powyżej. Jednak zaleca się, aby to zachowanie opcjonalne i konfigurowalne za pomocą argumentu wiersza poleceń w celu ułatwienia debugowania, jak również w celu uproszczenia integracji z systemami przy użyciu systemd.

Zauważ, że daemon() nie jest zgodny z POSIX .


Demony W Nowym Stylu

Dla demonów nowego stylu zaleca się następujące kroki: ]}

  1. jeśli odebrano SIGTERM, wyłącz demona i zakończ go czysto.
  2. If SIGHUP is received, reload the pliki konfiguracyjne, jeśli ma to zastosowanie.
  3. podaj poprawny kod wyjścia z głównego procesu demona, ponieważ jest on używany przez system init do wykrywania błędów i problemów z usługą. Zaleca się stosowanie schematu kodu wyjścia zdefiniowanego w zaleceniach LSB dla skryptów init SysV .
  4. jeśli to możliwe i stosowne, wystaw interfejs sterowania demona za pomocą systemu D-Bus IPC i pobierz nazwę magistrali jako ostatni krok inicjalizacji.
  5. Dla integracja w systemd, podaj .serwis unit plik, który zawiera informacje o uruchamianiu, zatrzymywaniu i utrzymywaniu demona w inny sposób. Zobacz też systemd.service(5) Po szczegóły.
  6. w miarę możliwości, polegaj na funkcjonalności systemu init, aby ograniczyć dostęp demona do plików, usług i innych zasobów, tj. w przypadku systemd, polegaj na kontroli limitu zasobów systemd zamiast implementować własne, polegaj na systemd upuszczanie kodu zamiast implementowania go w Demonie i tym podobne. Zobacz też systemd.exec(5) Dla dostępnych elementów sterujących.
  7. jeśli D-Busjest używany, Uaktywnij magistralę demona, dostarczając plik konfiguracyjny aktywacji usługi D-Bus . Ma to wiele zalet: Demon może być uruchamiany leniwie na żądanie; może być uruchamiany równolegle do innych demonów, które go wymagają - co maksymalizuje równoległość i prędkość rozruchu ; Twój Demon może zostać uruchomiony ponownie w przypadku awarii bez utraty żądań autobusowych, ponieważ kolejki autobusowe żądają aktywowanych usług. Zobacz poniżej Po szczegóły.
  8. jeśli twój Demon świadczy usługi innym lokalnym procesom lub zdalnym klientom za pośrednictwem gniazda, powinien on być aktywowany zgodnie ze schematem wskazanym poniżej . Podobnie jak aktywacja D-Bus, umożliwia to uruchamianie usług na żądanie, a także umożliwia lepszą równoległość usług start-up. Ponadto, w przypadku protokołów bez stanu (takich jak syslog, DNS), Demon implementujący aktywację opartą na gniazdach może zostać ponownie uruchomiony bez utraty ani jednego żądania. Zobacz poniżej Po szczegóły.
  9. jeśli ma to zastosowanie, Demon powinien powiadomić system init o zakończeniu uruchamiania lub aktualizacji statusu za pomocą sd_notify(3) interfejs.
  10. zamiast używać syslog() wywołanie logowania bezpośrednio do usługi syslog systemu, daemon w Nowym Stylu może wybrać po prostu logowanie do błąd standardowy poprzez fprintf(), który jest następnie przekazywany do syslog przez system init. Jeśli konieczne są poziomy logów, mogą one być zakodowane przez prefiks pojedynczych linii logów z łańcuchami takimi jak " "(dla poziomu log 4 "WARNING" w schemacie priorytetu syslog), zgodnie z podobnym stylem jak jądro Linuksaprintk() system poziomów. Szczegółowe informacje można znaleźć na stronie sd-daemon(3) oraz systemd.exec(5).

Aby dowiedzieć się więcej czytaj całość man 7 daemon.

 25
Author: patryk.beza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-08 10:09:22

I can stop at the first requirement " a daemon which cannot be stopped ..."

Nie jest to możliwe mój przyjacielu; jednak możesz osiągnąć to samo za pomocą znacznie lepszego narzędzia, modułu jądra.

Http://www.infoq.com/articles/inotify-linux-file-system-event-monitoring

Wszystkie demony można zatrzymać. Niektóre są łatwiejsze do powstrzymania niż inne. Nawet para demonów z partnerem w przytrzymaniu, odradzanie partnera w przypadku utraty, może zostać zatrzymana. Po prostu musisz trochę ciężej.

 5
Author: Edwin Buck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-30 19:20:24

Nie można utworzyć procesu w Linuksie, którego nie można zabić. Użytkownik root (UID=0) może wysłać sygnał do procesu i są dwa sygnały, których nie można przechwycić, SIGKILL=9, SIGSTOP=19. I inne sygnały (gdy nie są przesyłane) mogą również powodować zakończenie procesu.

Możesz potrzebować bardziej ogólnej funkcji demonizacji, w której możesz podać nazwę swojego programu / demona oraz ścieżkę do uruchomienia programu (być może "/" lub "/tmp"). Możesz również podać pliki dla stderr i stdout (i ewentualnie ścieżka kontrolna przy użyciu standardowego wejścia).

Oto niezbędne elementy:

#include <stdio.h>    //printf(3)
#include <stdlib.h>   //exit(3)
#include <unistd.h>   //fork(3), chdir(3), sysconf(3)
#include <signal.h>   //signal(3)
#include <sys/stat.h> //umask(3)
#include <syslog.h>   //syslog(3), openlog(3), closelog(3)

A oto bardziej ogólna Funkcja,

int
daemonize(char* name, char* path, char* outfile, char* errfile, char* infile )
{
    if(!path) { path="/"; }
    if(!name) { name="medaemon"; }
    if(!infile) { infile="/dev/null"; }
    if(!outfile) { outfile="/dev/null"; }
    if(!errfile) { errfile="/dev/null"; }
    //printf("%s %s %s %s\n",name,path,outfile,infile);
    pid_t child;
    //fork, detach from process group leader
    if( (child=fork())<0 ) { //failed fork
        fprintf(stderr,"error: failed fork\n");
        exit(EXIT_FAILURE);
    }
    if (child>0) { //parent
        exit(EXIT_SUCCESS);
    }
    if( setsid()<0 ) { //failed to become session leader
        fprintf(stderr,"error: failed setsid\n");
        exit(EXIT_FAILURE);
    }

    //catch/ignore signals
    signal(SIGCHLD,SIG_IGN);
    signal(SIGHUP,SIG_IGN);

    //fork second time
    if ( (child=fork())<0) { //failed fork
        fprintf(stderr,"error: failed fork\n");
        exit(EXIT_FAILURE);
    }
    if( child>0 ) { //parent
        exit(EXIT_SUCCESS);
    }

    //new file permissions
    umask(0);
    //change to path directory
    chdir(path);

    //Close all open file descriptors
    int fd;
    for( fd=sysconf(_SC_OPEN_MAX); fd>0; --fd )
    {
        close(fd);
    }

    //reopen stdin, stdout, stderr
    stdin=fopen(infile,"r");   //fd=0
    stdout=fopen(outfile,"w+");  //fd=1
    stderr=fopen(errfile,"w+");  //fd=2

    //open syslog
    openlog(name,LOG_PID,LOG_DAEMON);
    return(0);
}

Oto przykładowy program, który staje się demonem, wisi wokół, a następnie odchodzi.

int
main()
{
    int res;
    int ttl=120;
    int delay=5;
    if( (res=daemonize("mydaemon","/tmp",NULL,NULL,NULL)) != 0 ) {
        fprintf(stderr,"error: daemonize failed\n");
        exit(EXIT_FAILURE);
    }
    while( ttl>0 ) {
        //daemon code here
        syslog(LOG_NOTICE,"daemon ttl %d",ttl);
        sleep(delay);
        ttl-=delay;
    }
    syslog(LOG_NOTICE,"daemon ttl expired");
    closelog();
    return(EXIT_SUCCESS);
}

Zauważ, że SIG_IGN oznacza przechwytywanie i ignorowanie sygnału. Można zbudować funkcję obsługi sygnału, która może rejestrować odbiór sygnału i ustawiać flagi(takie jak flaga wskazująca wdzięczne zamknięcie).

 5
Author: ChuckCottrill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-15 19:43:11

Spróbuj użyć funkcji daemon:

#include <unistd.h>

int daemon(int nochdir, int noclose);

Ze strony man :

Funkcja daemon() jest przeznaczona dla programów, które chcą się odłączyć z terminala sterującego i uruchom w tle jako system demony.

Jeśli nochdir jest równy zero, daemon () zmienia bieżący proces wywołujący working directory to the root directory ( " / " ); w przeciwnym razie, bieżący katalog roboczy pozostaje bez zmian.

Jeśli noclose jest równe zero, DAEMON () przekierowuje standardowe wejście, standardowe wyjście i standardowy błąd do /dev / null; w przeciwnym razie żadne zmiany nie są do tych deskryptorów plików.

 4
Author: weiyin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-13 00:13:29

Jeśli Twoja aplikacja jest jedną z:

{
  ".sh": "bash",
  ".py": "python",
  ".rb": "ruby",
  ".coffee" : "coffee",
  ".php": "php",
  ".pl" : "perl",
  ".js" : "node"
}

I nie masz nic przeciwko zależności od NodeJS to zainstaluj NodeJS a potem:

npm install -g pm2

pm2 start yourapp.yourext --name "fred" # where .yourext is one of the above

pm2 start yourapp.yourext -i 0 --name "fred" # run your app on all cores

pm2 list

Aby wszystkie aplikacje były uruchomione przy ponownym uruchomieniu (i demonizowaniu pm2):

pm2 startup

pm2 save

Teraz możesz:

service pm2 stop|restart|start|status
Pozwala również łatwo obserwować zmiany kodu w katalogu aplikacji i automatycznie restartować proces aplikacji, gdy nastąpi zmiana kodu)
 4
Author: danday74,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-29 03:17:57

Wywołując fork() utworzyłeś proces potomny. Jeśli fork powiedzie się (Fork zwrócił niezerowy PID) wykonywanie będzie kontynuowane od tego momentu z wewnątrz procesu potomnego. W tym przypadku chcemy z wdziękiem wyjść z procesu rodzica, a następnie kontynuować naszą pracę w procesie dziecka.

Może to pomoże: http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html

 1
Author: Doug Morrow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-30 18:32:54

Demon jest tylko procesem w tle. Jeśli chcesz uruchomić program po uruchomieniu systemu operacyjnego, na Linuksie Dodaj swoje polecenie start do /etc / rc.d / rc.local (Uruchom po wszystkich innych skryptach) lub /etc/startup.sh

W systemie windows tworzysz usługę, rejestrujesz usługę, a następnie ustawiasz ją tak, aby uruchamiała się automatycznie przy starcie w panelu Administracja - > Usługi.

 0
Author: Magn3s1um,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-30 18:22:57