GNU/Linux >> Belajar Linux >  >> Linux

Membuat daemon di Linux

man 7 daemon menjelaskan cara membuat daemon dengan sangat detail. Jawaban saya hanyalah kutipan dari manual ini.

Setidaknya ada dua jenis daemon:

  1. daemon SysV tradisional (gaya lama),
  2. daemon systemd (gaya baru).

Daemon SysV

Jika Anda tertarik dengan daemon SysV tradisional, Anda harus menerapkan langkah-langkah berikut:

  1. Tutup semua deskriptor file yang terbuka kecuali input standar , keluaran , dan kesalahan (yaitu tiga deskriptor file pertama 0, 1, 2). Ini memastikan bahwa tidak ada deskriptor file yang dilewatkan secara tidak sengaja tetap ada dalam proses daemon. Di Linux, ini paling baik diterapkan dengan iterasi melalui /proc/self/fd , dengan fallback iterasi dari file descriptor 3 ke nilai yang dikembalikan oleh getrlimit() untuk RLIMIT_NOFILE .
  2. Setel ulang semua penangan sinyal ke defaultnya. Hal ini paling baik dilakukan dengan melakukan iterasi melalui sinyal yang tersedia hingga batas _NSIG dan meresetnya ke SIG_DFL .
  3. Setel ulang topeng sinyal menggunakan sigprocmask() .
  4. Sanitasi blok lingkungan, hapus atau setel ulang variabel lingkungan yang mungkin berdampak negatif pada waktu proses daemon.
  5. Hubungi fork() , untuk membuat proses latar belakang.
  6. Pada anak, panggil setsid() untuk melepaskan dari terminal mana pun dan membuat sesi independen.
  7. Pada anak, panggil fork() sekali lagi, untuk memastikan bahwa daemon tidak akan pernah bisa mendapatkan kembali terminal lagi.
  8. Hubungi exit() pada anak pertama, sehingga hanya anak kedua (proses daemon yang sebenarnya) yang tetap ada. Hal ini memastikan bahwa proses daemon diasuh ulang ke init/PID 1, sebagaimana seharusnya semua daemon.
  9. Dalam proses daemon, hubungkan /dev/null ke masukan standar , keluaran , dan kesalahan .
  10. Dalam proses daemon, setel ulang umask ke 0, sehingga mode file diteruskan ke open() , mkdir() dan sejenisnya secara langsung mengontrol mode akses dari file dan direktori yang dibuat.
  11. Dalam proses daemon, ubah direktori saat ini ke direktori root (/ ), untuk menghindari daemon secara tidak sengaja memblokir titik pemasangan agar tidak dilepas.
  12. Dalam proses daemon, tulis PID daemon (seperti yang dikembalikan oleh getpid() ) ke file PID, misalnya /run/foobar.pid (untuk daemon hipotetis "foobar") untuk memastikan bahwa daemon tidak dapat dijalankan lebih dari sekali. Ini harus diterapkan dengan cara bebas ras sehingga file PID hanya diperbarui ketika diverifikasi pada saat yang sama dengan PID yang sebelumnya disimpan dalam file PID tidak lagi ada atau milik proses asing.
  13. Dalam proses daemon, hilangkan hak istimewa, jika memungkinkan dan berlaku.
  14. Dari proses daemon, beri tahu proses asli dimulai bahwa inisialisasi selesai. Ini dapat diterapkan melalui pipa tanpa nama atau saluran komunikasi serupa yang dibuat sebelum fork() pertama dan karenanya tersedia dalam proses asli dan daemon.
  15. Hubungi exit() dalam proses aslinya. Proses yang memanggil daemon harus dapat mengandalkan exit() ini terjadi setelah inisialisasi selesai dan semua saluran komunikasi eksternal dibuat dan dapat diakses.

Perhatikan peringatan ini:

BSD daemon() fungsi seharusnya tidak digunakan, karena hanya mengimplementasikan subset dari langkah-langkah ini.

Daemon yang perlu menyediakan kompatibilitas dengan sistem SysV harus mengimplementasikan skema yang disebutkan di atas. Namun, sebaiknya buat perilaku ini opsional dan dapat dikonfigurasi melalui argumen baris perintah untuk memudahkan proses debug serta menyederhanakan integrasi ke dalam sistem menggunakan systemd.

Perhatikan bahwa daemon() tidak sesuai dengan POSIX.

Daemon Gaya Baru

Untuk daemon gaya baru, langkah-langkah berikut disarankan:

  1. Jika SIGTERM diterima, matikan daemon dan keluar dengan bersih.
  2. Jika SIGHUP diterima, muat ulang file konfigurasi, jika ini berlaku.
  3. Berikan kode keluar yang benar dari proses daemon utama, karena ini digunakan oleh sistem init untuk mendeteksi kesalahan dan masalah layanan. Direkomendasikan untuk mengikuti skema kode keluar seperti yang ditentukan dalam rekomendasi LSB untuk skrip init SysV.
  4. Jika memungkinkan dan berlaku, tampilkan antarmuka kontrol daemon melalui sistem IPC D-Bus dan ambil nama bus sebagai langkah terakhir inisialisasi.
  5. Untuk integrasi di systemd, berikan file unit .service yang membawa informasi tentang memulai, menghentikan, dan memelihara daemon. Lihat systemd.service(5) untuk detail.
  6. Sebisa mungkin, andalkan fungsionalitas sistem init untuk membatasi akses daemon ke file, layanan, dan sumber daya lainnya, misalnya dalam kasus systemd, andalkan kontrol batas sumber daya sistemd alih-alih mengimplementasikannya sendiri, andalkan hak istimewa systemd menjatuhkan kode alih-alih mengimplementasikannya di daemon, dan serupa. Lihat systemd.exec(5) untuk kontrol yang tersedia.
  7. Jika D-Bus digunakan, buat daemon bus Anda dapat diaktifkan dengan menyediakan file konfigurasi aktivasi layanan D-Bus. Ini memiliki banyak keuntungan:daemon Anda dapat dimulai dengan lambat sesuai permintaan; itu dapat dimulai secara paralel dengan daemon lain yang membutuhkannya — yang memaksimalkan paralelisasi dan kecepatan boot-up; daemon Anda dapat dimulai ulang jika gagal tanpa kehilangan permintaan bus apa pun, karena bus mengantri meminta layanan yang dapat diaktifkan. Lihat di bawah untuk detailnya.
  8. Jika daemon Anda menyediakan layanan ke proses lokal lain atau klien jarak jauh melalui soket, itu harus dibuat dapat diaktifkan soket mengikuti skema yang ditunjukkan di bawah ini. Seperti aktivasi D-Bus, ini memungkinkan permulaan layanan sesuai permintaan serta memungkinkan paralelisasi yang lebih baik dari permulaan layanan. Selain itu, untuk protokol tanpa status (seperti syslog, DNS), daemon yang mengimplementasikan aktivasi berbasis soket dapat dimulai ulang tanpa kehilangan satu permintaan pun. Lihat di bawah untuk detailnya.
  9. Jika berlaku, daemon harus memberi tahu sistem init tentang penyelesaian startup atau pembaruan status melalui sd_notify(3) antarmuka.
  10. Alih-alih menggunakan syslog() panggilan untuk masuk langsung ke layanan syslog sistem, daemon gaya baru dapat memilih untuk masuk ke kesalahan standar melalui fprintf() , yang kemudian diteruskan ke syslog oleh sistem init. Jika level log diperlukan, ini dapat dikodekan dengan mengawali setiap baris log dengan string seperti "<4>" (untuk log level 4 "WARNING" dalam skema prioritas syslog), mengikuti gaya yang mirip dengan printk() sistem tingkat. Untuk detailnya, lihat sd-daemon(3) dan systemd.exec(5) .

Untuk mempelajari lebih lanjut, baca seluruh man 7 daemon .


Di Linux saya ingin menambahkan daemon yang tidak dapat dihentikan dan yang memantau perubahan sistem file. Jika ada perubahan yang terdeteksi, itu harus menulis jalur ke konsol tempat dimulainya + baris baru.

Daemon bekerja di latar belakang dan (biasanya...) bukan milik TTY itu sebabnya Anda tidak dapat menggunakan stdout/stderr dengan cara yang mungkin Anda inginkan. Biasanya daemon syslog (syslogd ) digunakan untuk mencatat pesan ke file (debug, error,...).

Selain itu, ada beberapa langkah yang diperlukan untuk melakukan daemonisasi suatu proses.

Jika saya ingat dengan benar, langkah-langkah ini adalah:

  • garpu matikan proses induk &biarkan berhenti jika forking berhasil. -> Karena proses induk telah dihentikan, proses anak sekarang berjalan di latar belakang.
  • setsid - Buat sesi baru. Proses pemanggilan menjadi pemimpin sesi baru dan pemimpin grup proses dari grup proses baru. Proses sekarang terlepas dari terminal pengontrolnya (CTTY).
  • Menangkap sinyal - Abaikan dan/atau tangani sinyal.
  • garpu lagi &biarkan proses induk berhenti untuk memastikan bahwa Anda menyingkirkan proses memimpin sesi. (Hanya pemimpin sesi yang bisa mendapatkan TTY lagi.)
  • chdir - Ubah direktori kerja daemon.
  • umask - Ubah topeng mode file sesuai dengan kebutuhan daemon.
  • tutup - Tutup semua deskriptor file terbuka yang mungkin diwariskan dari proses induk.

Untuk memberi Anda titik awal:Lihat kode kerangka ini yang menunjukkan langkah-langkah dasar. Kode ini sekarang juga dapat di-fork di GitHub:Kerangka dasar daemon linux

/*
 * 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;
}


  • Kompilasi kode:gcc -o firstdaemon daemonize.c
  • Mulai daemon:./firstdaemon
  • Periksa apakah semuanya berfungsi dengan baik:ps -xj | grep firstdaemon

  • Outputnya harus mirip dengan yang ini:

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

Yang harus Anda lihat di sini adalah:

  • Daemon tidak memiliki terminal pengendali (TTY =? )
  • ID proses induk (PPID ) adalah 1 (Proses init)
  • PID !=SID yang berarti bahwa proses kami BUKAN pemimpin sesi
    (karena fork kedua())
  • Karena PID !=SID, proses kami tidak dapat mengontrol TTY lagi

Membaca syslog:

  • Temukan file syslog Anda. Punyaku ada di sini:/var/log/syslog
  • Lakukan:grep firstdaemon /var/log/syslog

  • Outputnya harus mirip dengan yang ini:

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


Catatan: Pada kenyataannya Anda juga ingin mengimplementasikan penangan sinyal dan menyiapkan logging dengan benar (File, level log...).

Bacaan lebih lanjut:

  • Linux-UNIX-Programmierung - Jerman
  • Pemrograman Server Daemon Unix

Linux
  1. Linux – Blokir Akses Jaringan Suatu Proses?

  2. Linux – Proses “subreaper”?

  3. Pengantar Utas Linux – Bagian I

  1. bunuh Contoh Perintah di Linux

  2. Status Proses Linux

  3. Proses Pembuatan Linux?

  1. Cara mematikan proses zombie di Linux

  2. Cara menginstal vtop di Linux

  3. Linux:proses menjadi layanan