Cron di Linux: sejarah, penggunaan dan perangkat

Cron di Linux: sejarah, penggunaan dan perangkat

Klasik menulis bahwa happy hour tidak ditonton. Di masa-masa sulit itu, tidak ada pemrogram atau Unix, tetapi saat ini pemrogram tahu pasti: cron akan melacak waktu, bukan mereka.

Utilitas baris perintah merupakan kelemahan sekaligus tugas bagi saya. sed, awk, wc, cut dan program lama lainnya dijalankan oleh skrip di server kami setiap hari. Banyak di antaranya dirancang sebagai tugas untuk cron, penjadwal yang berasal dari tahun 70an.

Untuk waktu yang lama saya menggunakan cron secara dangkal, tanpa menjelaskan secara detail, tetapi suatu hari, ketika saya menemukan kesalahan saat menjalankan skrip, saya memutuskan untuk memeriksanya secara menyeluruh. Beginilah artikel ini muncul, saat menulisnya saya menjadi akrab dengan crontab POSIX, opsi cron utama dalam distribusi Linux populer dan struktur beberapa di antaranya.

Apakah Anda menggunakan Linux dan menjalankan tugas cron? Apakah Anda tertarik dengan arsitektur aplikasi sistem di Unix? Lalu kita berangkat!

kadar

Asal spesies

Eksekusi berkala terhadap program pengguna atau sistem merupakan kebutuhan yang jelas di semua sistem operasi. Oleh karena itu, pemrogram telah lama menyadari kebutuhan akan layanan yang memungkinkan mereka merencanakan dan melaksanakan tugas secara terpusat.

Sistem operasi mirip Unix menelusuri asal-usulnya kembali ke Unix Versi 7, yang dikembangkan pada tahun 70-an abad terakhir di Bell Labs, termasuk oleh Ken Thompson yang terkenal. Versi 7 Unix juga menyertakan cron, layanan untuk menjalankan tugas pengguna super secara rutin.

Cron modern pada umumnya adalah program yang sederhana, tetapi algoritme pengoperasian versi aslinya bahkan lebih sederhana: layanan diaktifkan satu menit sekali, membaca tabel dengan tugas dari satu file (/etc/lib/crontab) dan dijalankan untuk pengguna super tugas-tugas yang seharusnya dilakukan saat ini.

Selanjutnya, versi perbaikan dari layanan sederhana dan berguna ini disertakan dengan semua sistem operasi mirip Unix.

Deskripsi umum format crontab dan prinsip dasar pengoperasian utilitas dimasukkan dalam standar utama sistem operasi mirip Unix - POSIX - pada tahun 1992, dan dengan demikian cron dari standar de facto menjadi standar de jure.

Pada tahun 1987, Paul Vixie, setelah mensurvei pengguna Unix tentang keinginan mereka terhadap cron, merilis versi daemon lain yang memperbaiki beberapa masalah cron tradisional dan memperluas sintaks file tabel.

Pada versi ketiga Vixie cron mulai memenuhi persyaratan POSIX, selain itu program ini memiliki lisensi liberal, atau lebih tepatnya tidak ada lisensi sama sekali, kecuali keinginan di README: penulis tidak memberikan jaminan, nama penulis tidak dapat dihapus, dan program hanya dapat dijual bersama dengan kode sumbernya. Persyaratan ini ternyata sesuai dengan prinsip-prinsip perangkat lunak bebas yang mulai populer pada tahun-tahun itu, sehingga beberapa distribusi Linux utama yang muncul di awal tahun 90an menggunakan Vixie cron sebagai sistem mereka dan masih mengembangkannya hingga saat ini.

Secara khusus, Red Hat dan SUSE mengembangkan fork Vixie cron - cronie, dan Debian serta Ubuntu menggunakan Vixie cron edisi asli dengan banyak patch.

Pertama-tama mari kita kenali crontab utilitas pengguna yang dijelaskan di POSIX, setelah itu kita akan melihat ekstensi sintaksis yang disediakan di Vixie cron dan penggunaan variasi Vixie cron di distribusi Linux populer. Dan yang terakhir, yang menarik adalah analisis perangkat daemon cron.

crontab POSIX

Jika cron asli selalu bekerja untuk pengguna super, penjadwal modern sering kali menangani tugas-tugas pengguna biasa, yang lebih aman dan nyaman.

Cron disediakan sebagai satu set dua program: daemon cron yang terus berjalan dan utilitas crontab yang tersedia untuk pengguna. Yang terakhir memungkinkan Anda untuk mengedit tabel tugas khusus untuk setiap pengguna dalam sistem, sementara daemon meluncurkan tugas dari tabel pengguna dan sistem.

В standar POSIX perilaku daemon tidak dijelaskan dengan cara apa pun dan hanya program pengguna yang diformalkan crontab. Keberadaan mekanisme untuk meluncurkan tugas-tugas pengguna, tentu saja, tersirat, namun tidak dijelaskan secara rinci.

Dengan memanggil utilitas crontab, Anda dapat melakukan empat hal: mengedit tabel tugas pengguna di editor, memuat tabel dari file, menampilkan tabel tugas saat ini, dan menghapus tabel tugas. Contoh cara kerja utilitas crontab:

crontab -e # редактировать таблицу задач
crontab -l # показать таблицу задач
crontab -r # удалить таблицу задач
crontab path/to/file.crontab # загрузить таблицу задач из файла

Saat dipanggil crontab -e editor yang ditentukan dalam variabel lingkungan standar akan digunakan EDITOR.

Tugasnya sendiri dijelaskan dalam format berikut:

# строки-комментарии игнорируются
#
# задача, выполняемая ежеминутно
* * * * * /path/to/exec -a -b -c
# задача, выполняемая на 10-й минуте каждого часа
10 * * * * /path/to/exec -a -b -c
# задача, выполняемая на 10-й минуте второго часа каждого дня и использующая перенаправление стандартного потока вывода
10 2 * * * /path/to/exec -a -b -c > /tmp/cron-job-output.log

Lima bidang catatan pertama: menit [1..60], jam [0..23], hari dalam sebulan [1..31], bulan [1..12], hari dalam seminggu [0. .6], dimana 0 adalah hari Minggu. Bidang terakhir, keenam, adalah baris yang akan dieksekusi oleh penerjemah perintah standar.

Di lima bidang pertama, nilai dapat dicantumkan dipisahkan dengan koma:

# задача, выполняемая в первую и десятую минуты каждого часа
1,10 * * * * /path/to/exec -a -b -c

Atau dengan tanda hubung:

# задача, выполняемая в каждую из первых десяти минут каждого часа
0-9 * * * * /path/to/exec -a -b -c

Akses pengguna ke penjadwalan tugas diatur di POSIX oleh file cron.allow dan cron.deny, yang masing-masing mencantumkan pengguna dengan akses ke crontab dan pengguna tanpa akses ke program. Standar tidak mengatur lokasi file-file ini dengan cara apa pun.

Menurut standar, setidaknya empat variabel lingkungan harus diteruskan ke program yang diluncurkan:

  1. HOME - direktori home pengguna.
  2. LOGNAME — login pengguna.
  3. PATH adalah jalur di mana Anda dapat menemukan utilitas sistem standar.
  4. SHELL — jalur ke penerjemah perintah yang digunakan.

Khususnya, POSIX tidak menjelaskan apa pun tentang dari mana nilai variabel ini berasal.

Penjual terbaik - Vixie cron 3.0pl1

Nenek moyang varian cron yang populer adalah Vixie cron 3.0pl1, yang diperkenalkan di milis comp.sources.unix pada tahun 1992. Kami akan mempertimbangkan fitur utama versi ini secara lebih rinci.

Vixie cron hadir dalam dua program (cron dan crontab). Seperti biasa, daemon bertanggung jawab untuk membaca dan menjalankan tugas dari tabel tugas sistem dan tabel tugas pengguna individual, dan utilitas crontab bertanggung jawab untuk mengedit tabel pengguna.

Tabel tugas dan file konfigurasi

Tabel tugas pengguna super terletak di /etc/crontab. Sintaks tabel sistem sesuai dengan sintaks Vixie cron, dengan pengecualian bahwa kolom keenam di dalamnya menunjukkan nama pengguna yang atas namanya tugas diluncurkan:

# Запускается ежеминутно от пользователя vlad
* * * * * vlad /path/to/exec

Tabel tugas pengguna reguler terletak di /var/cron/tabs/nama pengguna dan menggunakan sintaks yang sama. Saat Anda menjalankan utilitas crontab sebagai pengguna, file inilah yang diedit.

Daftar pengguna yang memiliki akses ke crontab dikelola di file /var/cron/allow dan /var/cron/deny, di mana Anda hanya perlu memasukkan nama pengguna di baris terpisah.

Sintaks yang diperluas

Dibandingkan dengan crontab POSIX, solusi Paul Vixey berisi beberapa modifikasi yang sangat berguna pada sintaks tabel tugas utilitas.

Sintaks tabel baru telah tersedia: misalnya, Anda dapat menentukan hari dalam seminggu atau bulan berdasarkan nama (Senin, Selasa, dan seterusnya):

# Запускается ежеминутно по понедельникам и вторникам в январе
* * * Jan Mon,Tue /path/to/exec

Anda dapat menentukan langkah peluncuran tugas:

# Запускается с шагом в две минуты
*/2 * * * Mon,Tue /path/to/exec

Langkah dan interval dapat dicampur:

# Запускается с шагом в две минуты в первых десять минут каждого часа
0-10/2 * * * * /path/to/exec

Alternatif intuitif untuk sintaks biasa didukung (reboot, tahunan, tahunan, bulanan, mingguan, harian, tengah malam, setiap jam):

# Запускается после перезагрузки системы
@reboot /exec/on/reboot
# Запускается раз в день
@daily /exec/daily
# Запускается раз в час
@hourly /exec/daily

Lingkungan pelaksanaan tugas

Vixie cron memungkinkan Anda mengubah lingkungan aplikasi yang sedang berjalan.

Variabel lingkungan USER, LOGNAME dan HOME tidak hanya disediakan oleh daemon, tetapi diambil dari sebuah file passwd. Variabel PATH disetel ke "/usr/bin:/bin" dan variabel SHELL disetel ke "/bin/sh". Nilai semua variabel kecuali LOGNAME dapat diubah di tabel pengguna.

Beberapa variabel lingkungan (terutama SHELL dan HOME) digunakan oleh cron sendiri untuk menjalankan tugas. Berikut tampilan penggunaan bash alih-alih sh standar untuk menjalankan tugas khusus:

SHELL=/bin/bash
HOME=/tmp/
# exec будет запущен bash-ем в /tmp/
* * * * * /path/to/exec

Pada akhirnya, semua variabel lingkungan yang ditentukan dalam tabel (digunakan oleh cron atau dibutuhkan oleh proses) akan diteruskan ke tugas yang sedang berjalan.

Untuk mengedit file, crontab menggunakan editor yang ditentukan dalam variabel lingkungan VISUAL atau EDITOR. Jika lingkungan tempat crontab dijalankan tidak memiliki variabel yang ditentukan, maka "/usr/ucb/vi" digunakan (ucb mungkin adalah Universitas California, Berkeley).

cron di Debian dan Ubuntu

Pengembang Debian dan distribusi turunannya telah merilis versi yang sangat dimodifikasi Vixie cron versi 3.0pl1. Tidak ada perbedaan dalam sintaks file tabel, bagi pengguna itu adalah cron Vixie yang sama. Fitur Baru Terbesar: Dukungan syslog, SELinux и PAM.

Perubahan yang kurang terlihat namun nyata mencakup lokasi file konfigurasi dan tabel tugas.

Tabel pengguna di Debian terletak di direktori /var/spool/cron/crontab, tabel sistem masih ada - di /etc/crontab. Tabel tugas khusus paket Debian ditempatkan di /etc/cron.d, tempat daemon cron secara otomatis membacanya. Kontrol akses pengguna dikendalikan oleh file /etc/cron.allow dan /etc/cron.deny.

Shell defaultnya masih /bin/sh, yang di Debian merupakan shell kecil yang mendukung POSIX berlari, diluncurkan tanpa membaca konfigurasi apa pun (dalam mode non-interaktif).

Cron sendiri pada Debian versi terbaru diluncurkan melalui systemd, dan konfigurasi peluncurannya dapat dilihat di /lib/systemd/system/cron.service. Tidak ada yang istimewa dalam konfigurasi layanan; manajemen tugas yang lebih halus dapat dilakukan melalui variabel lingkungan yang dideklarasikan langsung di crontab setiap pengguna.

kroni di RedHat, Fedora dan CentOS

cronie — fork dari Vixie cron versi 4.1. Seperti di Debian, sintaksisnya tidak berubah, tetapi dukungan untuk PAM dan SELinux, bekerja dalam cluster, melacak file menggunakan inotify dan fitur lainnya telah ditambahkan.

Konfigurasi default ada di tempat biasa: tabel sistem ada di /etc/crontab, paket meletakkan tabelnya di /etc/cron.d, tabel pengguna ada di /var/spool/cron/crontab.

Daemon berjalan di bawah kendali systemd, konfigurasi layanannya adalah /lib/systemd/system/crond.service.

Pada distribusi mirip Red Hat, /bin/sh digunakan secara default saat startup, yang merupakan bash standar. Perlu dicatat bahwa ketika menjalankan pekerjaan cron melalui /bin/sh, bash shell dimulai dalam mode yang sesuai dengan POSIX dan tidak membaca konfigurasi tambahan apa pun, berjalan dalam mode non-interaktif.

kroni di SLES dan openSUSE

Distribusi Jerman SLES dan turunannya openSUSE menggunakan kroni yang sama. Daemon di sini juga diluncurkan di bawah systemd, konfigurasi layanan terletak di /usr/lib/systemd/system/cron.service. Konfigurasi: /etc/crontab, /etc/cron.d, /var/spool/cron/tabs. /bin/sh adalah bash yang sama yang berjalan dalam mode non-interaktif yang sesuai dengan POSIX.

Perangkat vixie cron

Keturunan cron modern tidak berubah secara radikal dibandingkan dengan Vixie cron, tetapi masih memperoleh fitur-fitur baru yang tidak memerlukan pemahaman tentang prinsip-prinsip program. Banyak dari ekstensi ini dirancang dengan buruk dan membingungkan kodenya. Kode sumber cron asli oleh Paul Vixey menyenangkan untuk dibaca.

Oleh karena itu, saya memutuskan untuk menganalisis perangkat cron menggunakan contoh program cron yang umum untuk kedua cabang pengembangan - Vixie cron 3.0pl1. Saya akan menyederhanakan contoh dengan menghapus ifdef yang mempersulit pembacaan dan menghilangkan detail kecil.

Pekerjaan iblis dapat dibagi menjadi beberapa tahap:

  1. Inisialisasi program.
  2. Mengumpulkan dan memperbarui daftar tugas yang akan dijalankan.
  3. Loop cron utama berjalan.
  4. Mulai tugas.

Mari kita lihat secara berurutan.

Inisialisasi

Ketika dimulai, setelah memeriksa argumen proses, cron menginstal penangan sinyal SIGCHLD dan SIGHUP. Yang pertama membuat entri log tentang penghentian proses anak, yang kedua menutup deskriptor file dari file log:

signal(SIGCHLD, sigchld_handler);
signal(SIGHUP, sighup_handler);

Daemon cron selalu berjalan sendiri di sistem, hanya sebagai pengguna super dan dari direktori cron utama. Panggilan berikut membuat file kunci dengan PID proses daemon, pastikan pengguna benar dan ubah direktori saat ini ke direktori utama:

acquire_daemonlock(0);
set_cron_uid();
set_cron_cwd();

Jalur default telah ditetapkan, yang akan digunakan saat memulai proses:

setenv("PATH", _PATH_DEFPATH, 1);

Kemudian proses tersebut “di-daemonisasi”: ia membuat salinan anak dari proses tersebut dengan memanggil fork dan sesi baru dalam proses anak (memanggil setid). Proses induk tidak lagi diperlukan, dan keluar:

switch (fork()) {
case -1:
    /* критическая ошибка и завершение работы */
    exit(0);
break;
case 0:
    /* дочерний процесс */
    (void) setsid();
break;
default:
    /* родительский процесс завершает работу */
    _exit(0);
}

Penghentian proses induk melepaskan kunci pada file kunci. Selain itu, diperlukan pembaruan PID pada file ke anak. Setelah ini, database tugas diisi:

/* повторный захват лока */
acquire_daemonlock(0);

/* Заполнение БД  */
database.head = NULL;
database.tail = NULL;
database.mtime = (time_t) 0;
load_database(&database);

Kemudian cron berpindah ke siklus kerja utama. Namun sebelum itu, ada baiknya melihat memuat daftar tugas.

Mengumpulkan dan memperbarui daftar tugas

Fungsi load_database bertanggung jawab untuk memuat daftar tugas. Ia memeriksa crontab sistem utama dan direktori dengan file pengguna. Jika file dan direktori tidak berubah, daftar tugas tidak dibaca ulang. Jika tidak, daftar tugas baru akan mulai terbentuk.

Memuat file sistem dengan nama file dan tabel khusus:

/* если файл системной таблицы изменился, перечитываем */
if (syscron_stat.st_mtime) {
    process_crontab("root", "*system*",
    SYSCRONTAB, &syscron_stat,
    &new_db, old_db);
}

Memuat tabel pengguna dalam satu lingkaran:

while (NULL != (dp = readdir(dir))) {
    char    fname[MAXNAMLEN+1],
            tabname[MAXNAMLEN+1];
    /* читать файлы с точкой не надо*/
    if (dp->d_name[0] == '.')
            continue;
    (void) strcpy(fname, dp->d_name);
    sprintf(tabname, CRON_TAB(fname));
    process_crontab(fname, fname, tabname,
                    &statbuf, &new_db, old_db);
}

Setelah itu database lama diganti dengan yang baru.

Dalam contoh di atas, pemanggilan fungsi process_crontab memverifikasi bahwa ada pengguna yang cocok dengan nama file tabel (kecuali pengguna super) dan kemudian memanggil load_user. Yang terakhir sudah membaca file itu sendiri baris demi baris:

while ((status = load_env(envstr, file)) >= OK) {
    switch (status) {
    case ERR:
        free_user(u);
        u = NULL;
        goto done;
    case FALSE:
        e = load_entry(file, NULL, pw, envp);
        if (e) {
            e->next = u->crontab;
            u->crontab = e;
        }
        break;
    case TRUE:
        envp = env_set(envp, envstr);
        break;
    }
}

Di sini, variabel lingkungan disetel (baris dalam bentuk VAR=value) menggunakan fungsi load_env / env_set, atau deskripsi tugas dibaca (* * * * * /path/to/exec) menggunakan fungsi load_entry.

Entitas entri yang dikembalikan load_entry adalah tugas kita, yang ditempatkan di daftar umum tugas. Fungsi itu sendiri melakukan penguraian format waktu secara verbose, tetapi kami lebih tertarik pada pembentukan variabel lingkungan dan parameter peluncuran tugas:

/* пользователь и группа для запуска задачи берутся из passwd*/
e->uid = pw->pw_uid;
e->gid = pw->pw_gid;

/* шелл по умолчанию (/bin/sh), если пользователь не указал другое */
e->envp = env_copy(envp);
if (!env_get("SHELL", e->envp)) {
    sprintf(envstr, "SHELL=%s", _PATH_BSHELL);
    e->envp = env_set(e->envp, envstr);
}
/* домашняя директория */
if (!env_get("HOME", e->envp)) {
    sprintf(envstr, "HOME=%s", pw->pw_dir);
    e->envp = env_set(e->envp, envstr);
}
/* путь для поиска программ */
if (!env_get("PATH", e->envp)) {
    sprintf(envstr, "PATH=%s", _PATH_DEFPATH);
    e->envp = env_set(e->envp, envstr);
}
/* имя пользовтеля всегда из passwd */
sprintf(envstr, "%s=%s", "LOGNAME", pw->pw_name);
e->envp = env_set(e->envp, envstr);

Loop utama berfungsi dengan daftar tugas saat ini.

Putaran Utama

Cron asli dari Versi 7 Unix bekerja cukup sederhana: ia membaca ulang konfigurasi dalam satu lingkaran, meluncurkan tugas-tugas saat ini sebagai pengguna super, dan tidur hingga awal menit berikutnya. Pendekatan sederhana pada mesin lama ini memerlukan terlalu banyak sumber daya.

Versi alternatif diusulkan di SysV, di mana daemon tertidur hingga menit terdekat saat tugas ditentukan, atau selama 30 menit. Lebih sedikit sumber daya yang digunakan untuk membaca ulang konfigurasi dan memeriksa tugas dalam mode ini, tetapi memperbarui daftar tugas dengan cepat menjadi merepotkan.

Vixie cron kembali memeriksa daftar tugas satu menit sekali, untungnya pada akhir tahun 80an terdapat lebih banyak sumber daya pada mesin Unix standar:

/* первичная загрузка задач */
load_database(&database);
/* запустить задачи, поставленные к выполнению после перезагрузки системы */
run_reboot_jobs(&database);
/* сделать TargetTime началом ближайшей минуты */
cron_sync();
while (TRUE) {
    /* выполнить задачи, после чего спать до TargetTime с поправкой на время, потраченное на задачи */
    cron_sleep();

    /* перечитать конфигурацию */
    load_database(&database);

    /* собрать задачи для данной минуты */
    cron_tick(&database);

    /* перевести TargetTime на начало следующей минуты */
    TargetTime += 60;
}

Fungsi cron_sleep terlibat langsung dalam pelaksanaan tugas, memanggil fungsi job_runqueue (menghitung dan menjalankan tugas) dan do_command (menjalankan setiap tugas individual). Fungsi terakhir patut dikaji lebih detail.

Menjalankan tugas

Fungsi do_command dijalankan dengan gaya Unix yang baik, yaitu melakukan fork untuk melakukan tugas secara asinkron. Proses induk terus meluncurkan tugas, proses anak menyiapkan proses tugas:

switch (fork()) {
case -1:
    /*не смогли выполнить fork */
    break;
case 0:
    /* дочерний процесс: на всякий случай еще раз пробуем захватить главный лок */
    acquire_daemonlock(1);
    /* переходим к формированию процесса задачи */
    child_process(e, u);
    /* по завершению дочерний процесс заканчивает работу */
    _exit(OK_EXIT);
    break;
default:
    /* родительский процесс продолжает работу */
    break;
}

Ada cukup banyak logika dalam child_process: dibutuhkan keluaran standar dan aliran kesalahan ke dirinya sendiri, untuk kemudian mengirimkannya ke email (jika variabel lingkungan MAILTO ditentukan dalam tabel tugas), dan, akhirnya, menunggu yang utama proses tugas untuk diselesaikan.

Proses tugas dibentuk oleh cabang lain:

switch (vfork()) {
case -1:
    /* при ошибки сразу завершается работа */
    exit(ERROR_EXIT);
case 0:
    /* процесс-внук формирует новую сессию, терминал и т.д.
     */
    (void) setsid();

    /*
     * дальше многословная настройка вывода процесса, опустим для краткости
     */

    /* смена директории, пользователя и группы пользователя,
     * то есть процесс больше не суперпользовательский
     */
    setgid(e->gid);
    setuid(e->uid);
    chdir(env_get("HOME", e->envp));

    /* запуск самой команды
     */
    {
        /* переменная окружения SHELL указывает на интерпретатор для запуска */
        char    *shell = env_get("SHELL", e->envp);

        /* процесс запускается без передачи окружения родительского процесса,
         * то есть именно так, как описано в таблице задач пользователя  */
        execle(shell, shell, "-c", e->cmd, (char *)0, e->envp);

        /* ошибка — и процесс на запустился? завершение работы */
        perror("execl");
        _exit(ERROR_EXIT);
    }
    break;
default:
    /* сам процесс продолжает работу: ждет завершения работы и вывода */
    break;
}

Pada dasarnya itulah cron. Saya menghilangkan beberapa detail menarik, misalnya, akuntansi untuk pengguna jarak jauh, tetapi saya menguraikan hal utama.

penutup

Cron adalah program yang sangat sederhana dan berguna, dibuat dengan tradisi terbaik dunia Unix. Dia tidak melakukan apa pun ekstra, tapi dia telah melakukan pekerjaannya dengan luar biasa selama beberapa dekade sekarang. Menyelesaikan kode untuk versi yang disertakan dengan Ubuntu membutuhkan waktu tidak lebih dari satu jam, dan saya sangat bersenang-senang! Saya harap saya bisa membaginya dengan Anda.

Saya tidak tahu tentang Anda, tapi saya agak sedih menyadari bahwa pemrograman modern, dengan kecenderungannya yang terlalu rumit dan terlalu abstrak, sudah lama tidak mendukung kesederhanaan seperti itu.

Ada banyak alternatif modern untuk cron: systemd-timer memungkinkan Anda mengatur sistem yang kompleks dengan dependensi, fcron memungkinkan Anda mengatur konsumsi sumber daya berdasarkan tugas dengan lebih fleksibel. Namun secara pribadi, crontab yang paling sederhana selalu cukup bagi saya.

Singkatnya, cintai Unix, gunakan program sederhana dan jangan lupa membaca mana untuk platform Anda!

Sumber: www.habr.com

Tambah komentar