
Klasik menulis bahwa happy hour tidak diamati. Di masa-masa liar itu, tidak ada programmer atau Unix, tetapi saat ini programmer tahu betul: bukannya mereka, cron akan mengikuti waktu.
Utilitas baris perintah bagi saya adalah kelemahan dan rutin. sed, awk, wc, cut dan program lama lainnya dijalankan oleh skrip di server kami setiap hari. Banyak dari mereka dirancang sebagai tugas untuk cron, penjadwal dari tahun 70-an.
Untuk waktu yang lama saya menggunakan cron secara dangkal, tanpa masuk ke detail, tetapi sekali, setelah mengalami kesalahan saat menjalankan skrip, saya memutuskan untuk mencari tahu secara menyeluruh. Jadi artikel ini muncul, ketika menulis yang saya kenali dengan POSIX crontab, varian cron utama dalam distribusi Linux yang populer dan perangkat beberapa di antaranya.
Menggunakan Linux dan menjalankan tugas di cron? Tertarik dengan arsitektur aplikasi sistem Unix? Lalu kita berada di jalan!
Isi
Asal usul spesies
Eksekusi berkala dari pengguna atau program sistem adalah kebutuhan yang jelas untuk semua sistem operasi. Oleh karena itu, kebutuhan akan layanan yang memungkinkan perencanaan terpusat dan pelaksanaan tugas, programmer telah menyadari waktu yang sangat lama.
Sistem operasi mirip Unix menurunkan silsilahnya dari Versi 7 Unix, yang dikembangkan pada 1970-an oleh Bell Labs termasuk Ken Thompson yang terkenal. Bersama dengan Versi 7 Unix, cron, layanan untuk pelaksanaan tugas superuser secara teratur, juga disediakan.
Cron modern yang khas adalah program sederhana, tetapi algoritme versi aslinya bahkan lebih sederhana: layanan bangun satu menit sekali, membaca pelat tugas dari satu file (/ etc / lib / crontab) dan melakukan tugas yang harus dilakukan untuk superuser pada menit saat ini. .
Selanjutnya, opsi lanjutan untuk layanan sederhana dan bermanfaat datang dengan semua sistem operasi mirip Unix.
Deskripsi umum dari format crontab dan prinsip-prinsip dasar utilitas pada tahun 1992 dimasukkan dalam standar utama sistem operasi mirip Unix - POSIX - dan dengan demikian cron dari standar de facto menjadi standar de jure.
Pada tahun 1987, Paul Vixie, setelah mewawancarai pengguna Unix untuk saran cron, merilis versi lain daemon yang memperbaiki beberapa masalah cron tradisional dan memperluas sintaksis file tabel.
Pada versi ketiga, Vixie cron mulai memenuhi persyaratan POSIX, di samping itu, program tersebut memiliki lisensi liberal, atau lebih tepatnya tidak ada lisensi sama sekali, kecuali untuk keinginan di README: penulis tidak memberikan jaminan, Anda tidak dapat menghapus nama penulis, dan Anda hanya dapat menjual program dengan kode sumber. Persyaratan ini ternyata kompatibel dengan prinsip-prinsip perangkat lunak bebas, yang mulai populer pada tahun-tahun itu, sehingga beberapa distribusi Linux utama yang muncul pada awal 90-an menggunakan Vixie cron sebagai distribusi sistem dan masih mengembangkannya.
Secara khusus, Red Hat dan SUSE mengembangkan garpu Vixie cron - cronie, sementara Debian dan Ubuntu menggunakan cron Vixie asli dengan banyak tambalan.
Pertama, mari berkenalan dengan utilitas crontab yang ditentukan pengguna yang dijelaskan dalam POSIX, setelah itu kita akan menganalisis ekstensi sintaks yang diperkenalkan di Vixie cron dan penggunaan variasi Vixie cron dalam distribusi Linux yang populer. Dan akhirnya, ceri pada kue adalah penguraian perangkat cron daemon.
Posix crontab
Jika cron asli selalu bekerja untuk superuser, maka penjadwal modern sering berurusan dengan tugas-tugas pengguna biasa, yang lebih aman dan lebih nyaman.
Cron dikirimkan dengan satu set dua program: daemon cron yang terus berjalan dan utilitas crontab tersedia untuk pengguna. Yang terakhir memungkinkan Anda untuk mengedit tabel tugas khusus untuk setiap pengguna dalam sistem, sementara daemon memulai tugas dari pengguna dan tabel sistem.
Standar POSIX tidak menggambarkan perilaku daemon, dan hanya program pengguna crontab yang diformalkan. Keberadaan mekanisme untuk meluncurkan tugas pengguna, tentu saja, tersirat, tetapi tidak dijelaskan secara rinci.
Ada empat hal yang dapat Anda lakukan dengan utilitas crontab: mengedit tabel tugas pengguna di editor, memuat tabel dari file, menampilkan tabel tugas saat ini, dan menghapus tabel tugas. Contoh utilitas crontab:
crontab -e # crontab -l # crontab -r # crontab path/to/file.crontab #
Saat memanggil crontab -e
, editor yang ditentukan dalam EDITOR
lingkungan EDITOR
standar akan digunakan.
Tugas itu 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 rekaman pertama: menit [1..60], jam [0..23], hari dalam bulan [1..31], bulan [1..12], hari dalam minggu [0..6], di mana 0 - minggu Bidang terakhir, keenam, adalah string yang akan dieksekusi oleh penerjemah perintah standar.
Di lima bidang pertama, nilai-nilai bisa dicantumkan dengan koma:
# , 1,10 * * * * /path/to/exec -a -b -c
Atau melalui tanda hubung:
# , 0-9 * * * * /path/to/exec -a -b -c
Akses pengguna ke penjadwalan tugas diatur dalam file POSIX cron.allow dan cron.deny yang masing-masing daftar, pengguna dengan akses ke crontab dan pengguna tanpa akses ke program. Standar tidak mengatur lokasi file-file ini.
Menjalankan program, sesuai standar, harus lulus setidaknya empat variabel lingkungan:
- HOME adalah direktori home pengguna.
- LOGNAME - login pengguna.
- PATH adalah jalur yang digunakan untuk menemukan utilitas sistem standar.
- SHELL adalah jalur menuju shell yang digunakan.
Perlu dicatat bahwa POSIX tidak mengatakan apa-apa tentang dari mana nilai-nilai untuk variabel ini berasal.
Buku Terlaris - Vixie cron 3.0pl1
Nenek moyang yang umum dari varian cron populer adalah Vixie cron 3.0pl1, yang disajikan pada milis comp.sources.unix 1992. Fitur utama dari versi ini akan kami pertimbangkan lebih detail.
Cron Vixie hadir dalam dua program (cron dan crontab). Seperti biasa, daemon bertanggung jawab untuk membaca dan memulai tugas dari tabel tugas sistem dan tabel tugas masing-masing pengguna, dan utilitas crontab bertanggung jawab untuk mengedit tabel pengguna.
Tabel tugas dan file konfigurasi
Tabel tugas pengguna super terletak di / etc / crontab. Sintaksis dari tabel sistem sesuai dengan sintaksis Vixie cron, disesuaikan dengan fakta bahwa kolom keenam menunjukkan nama pengguna yang atas nama tugas diluncurkan:
# vlad * * * * * vlad /path/to/exec
Tabel tugas pengguna umum terletak di / var / cron / tabs / nama pengguna dan menggunakan sintaksis umum. Ketika utilitas crontab diluncurkan, file-file ini diedit atas nama pengguna.
Daftar pengguna dengan akses ke crontab dikelola dalam file / var / cron / allow dan / var / cron / deny, di mana cukup untuk menambahkan nama pengguna sebagai baris terpisah.
Sintaks yang diperluas
Dibandingkan dengan POSIX crontab, solusi Paul Vixie mengandung beberapa modifikasi yang sangat berguna untuk sintaks tabel tugas utilitas.
Sintaks tabel baru telah tersedia: misalnya, Anda dapat menentukan hari dalam seminggu atau bulan dengan nama (Sen, Sel, dan sebagainya):
# * * * Jan Mon,Tue /path/to/exec
Anda dapat menentukan langkah di mana tugas diluncurkan:
# */2 * * * Mon,Tue /path/to/exec
Langkah dan interval dapat dicampur:
# 0-10/2 * * * * /path/to/exec
Alternatif intuitif untuk sintaks reguler 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 untuk mengubah lingkungan menjalankan aplikasi.
Variabel lingkungan USER, LOGNAME, dan HOME tidak hanya disediakan oleh daemon, tetapi juga diambil dari file passwd . Variabel PATH mendapatkan nilai "/ usr / bin: / bin", dan SHELL mendapatkan nilai "/ bin / sh". Nilai semua variabel kecuali LOGNAME dapat diubah dalam tabel pengguna.
Beberapa variabel lingkungan (terutama SHELL dan HOME) digunakan oleh cron sendiri untuk menjalankan tugas. Inilah yang menggunakan bash alih-alih standar sh untuk menjalankan tugas kustom mungkin terlihat seperti:
SHELL=/bin/bash HOME=/tmp/ # exec bash- /tmp/ * * * * * /path/to/exec
Pada akhirnya, semua variabel lingkungan yang didefinisikan dalam tabel (digunakan oleh cron atau diperlukan untuk proses) akan ditransfer ke tugas yang sedang berjalan.
Utilitas crontab menggunakan editor yang ditentukan dalam variabel lingkungan VISUAL atau EDITOR untuk mengedit file. Jika variabel-variabel ini tidak didefinisikan di lingkungan tempat crontab diluncurkan, maka "/ usr / ucb / vi" digunakan (ucb mungkin adalah University of California, Berkeley).
cron di Debian dan Ubuntu
Pengembang Debian dan turunannya telah merilis versi yang sangat dimodifikasi dari Vixie cron versi 3.0pl1. Tidak ada perbedaan dalam sintaksis file tabel, bagi pengguna, ini adalah cron Vixie yang sama. Fitur baru terbesar: dukungan syslog , SELinux, dan PAM .
Dari perubahan yang kurang terlihat, tetapi nyata - 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-Debian ditempatkan di /etc/cron.d, tempat daemon cron membacanya secara otomatis. Kontrol akses pengguna diatur oleh file /etc/cron.allow dan /etc/cron.deny.
Shell / bin / sh default masih digunakan sebagai shell default. Debian memainkan shell dash kompatibel-POSIX yang berjalan tanpa membaca konfigurasi apa pun (dalam mode non-interaktif).
Cron sendiri dalam versi terbaru Debian diluncurkan melalui systemd, dan konfigurasi peluncuran dapat dilihat di /lib/systemd/system/cron.service. Tidak ada yang istimewa dalam konfigurasi layanan, setiap manajemen tugas yang lebih baik 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, yang bekerja di sebuah cluster, melacak file menggunakan inotify, dan fitur lainnya ditambahkan.
Konfigurasi default di tempat biasa: tabel sistem di / etc / crontab, paket meletakkan tabel mereka di /etc/cron.d, tabel pengguna jatuh ke / var / spool / cron / crontab.
Daemon berjalan di bawah systemd, konfigurasi layanannya adalah /lib/systemd/system/crond.service.
Saat startup, distribusi Red Hat-like menggunakan / bin / sh secara default, yang perannya adalah bash standar. Perlu dicatat bahwa ketika menjalankan tugas cron melalui / bin / sh, bash shell dimulai dalam mode yang kompatibel dengan POSIX dan tidak membaca konfigurasi tambahan saat beroperasi dalam mode non-interaktif.
cronie di SLES dan openSUSE
Distribusi SLES Jerman dan turunannya openSUSE menggunakan kroni yang sama. Daemon di sini juga berjalan di bawah systemd, konfigurasi layanan di /usr/lib/systemd/system/cron.service. Konfigurasi: / etc / crontab, /etc/cron.d, / var / spool / cron / tabs. Sebagai / bin / sh bertindak bash yang sama, diluncurkan dalam mode non-interaktif yang kompatibel dengan POSIX.
Perangkat cron Vixie
Keturunan modern dari cron tidak berubah secara radikal dibandingkan dengan Vixie cron, tetapi bagaimanapun mereka telah memperoleh kemampuan baru yang tidak diharuskan untuk memahami prinsip-prinsip program. Banyak dari ekstensi ini yang berantakan dan membingungkan kodenya. Kode sumber cron asli oleh Paul Vixie adalah kesenangan untuk dibaca.
Oleh karena itu, saya memutuskan untuk menganalisis perangkat cron menggunakan contoh program umum untuk kedua cabang pengembangan cron - Vixie cron 3.0pl1. Saya akan menyederhanakan contoh dengan menghapus ifdefs yang menyulitkan membaca dan menghilangkan detail sekunder.
Pekerjaan iblis dapat dibagi menjadi beberapa tahap:
- Inisialisasi program.
- Kumpulkan dan perbarui daftar tugas yang harus dijalankan.
- Operasi loop cron utama.
- Peluncuran tugas.
Mari kita mengurutkannya.
Inisialisasi
Ketika diluncurkan, setelah memeriksa argumen proses, cron menginstal penangan sinyal SIGCHLD dan SIGHUP. Log pertama penyelesaian proses anak, yang kedua menutup deskriptor file dari file log:
signal(SIGCHLD, sigchld_handler); signal(SIGHUP, sighup_handler);
Daemon cron dalam sistem selalu bekerja sendiri, hanya sebagai superuser dan dari direktori utama cron. Panggilan berikut membuat kunci file dengan PID dari proses daemon, pastikan bahwa pengguna sudah benar, dan ubah direktori saat ini menjadi yang utama:
acquire_daemonlock(0); set_cron_uid(); set_cron_cwd();
Jalur default diatur, yang akan digunakan saat memulai proses:
setenv("PATH", _PATH_DEFPATH, 1);
Kemudian prosesnya "di-demonisasi": proses ini membuat salinan proses anak dengan memanggil garpu dan sesi baru dalam proses anak (memanggil setsid). Tidak perlu lagi proses induk - dan ini menyelesaikan pekerjaan:
switch (fork()) { case -1: exit(0); break; case 0: (void) setsid(); break; default: _exit(0); }
Pengakhiran proses induk melepaskan kunci pada file kunci. Selain itu, Anda perlu memperbarui PID dalam file ke anak. Setelah itu, database tugas diisi:
acquire_daemonlock(0); database.head = NULL; database.tail = NULL; database.mtime = (time_t) 0; load_database(&database);
Selanjutnya cron melanjutkan ke siklus kerja utama. Tetapi sebelum itu, lihat memuat daftar tugas.
Mengumpulkan dan memperbarui daftar tugas
Fungsi load_database bertanggung jawab untuk memuat daftar tugas. Ini memeriksa crontab sistem utama dan direktori dengan file pengguna. Jika file dan direktori tidak berubah, maka daftar tugas tidak dibaca ulang. Kalau tidak, daftar tugas baru mulai terbentuk.
Mengunduh 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); }
Kemudian database lama diganti dengan yang baru.
Dalam contoh di atas, memanggil fungsi process_crontab memastikan bahwa ada pengguna yang cocok dengan nama file tabel (kecuali itu adalah 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, baik variabel lingkungan (baris dari bentuk VAR = nilai) diatur oleh fungsi load_env / env_set, atau deskripsi tugas (* * * * / path / ke / exec) dibaca oleh fungsi load_entry.
Entitas entri yang dikembalikan oleh load_entry adalah tugas kami yang ditempatkan pada daftar tugas umum. Dalam fungsi itu sendiri, analisis panjang format waktu dilakukan, tetapi kami lebih tertarik pada pembentukan variabel lingkungan dan parameter peluncuran tugas:
e->uid = pw->pw_uid; e->gid = pw->pw_gid; 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); } sprintf(envstr, "%s=%s", "LOGNAME", pw->pw_name); e->envp = env_set(e->envp, envstr);
Siklus utama juga berfungsi dengan daftar tugas saat ini.
Siklus utama
Cron asli dari Versi 7 Unix bekerja cukup sederhana: dalam siklus saya membaca ulang konfigurasi, menjalankan tugas menit saat ini sebagai superuser dan tidur sampai awal menit berikutnya. Pendekatan sederhana pada mesin lama ini membutuhkan terlalu banyak sumber daya.
Versi alternatif diusulkan di SysV, di mana daemon tertidur baik sampai menit berikutnya, yang tugasnya didefinisikan, atau selama 30 menit. Sumber daya untuk membaca kembali konfigurasi dan memeriksa tugas dalam mode ini dikonsumsi lebih sedikit, tetapi menjadi tidak nyaman untuk memperbarui daftar tugas dengan cepat.
Vixie cron kembali memeriksa daftar tugas satu menit sekali, karena pada akhir tahun 80-an sumber daya pada mesin Unix standar telah menjadi jauh lebih besar:
load_database(&database); run_reboot_jobs(&database); cron_sync(); while (TRUE) { cron_sleep(); load_database(&database); cron_tick(&database); TargetTime += 60; }
Fungsi cron_sleep, yang memanggil fungsi job_runqueue (iterasi dan memulai tugas) dan do_command (memulai setiap tugas individu), secara langsung terlibat dalam pelaksanaan tugas. Fungsi terakhir harus dipertimbangkan secara lebih rinci.
Peluncuran tugas
Fungsi do_command dieksekusi dalam gaya Unix yang baik, yaitu, ia melakukan fork untuk eksekusi tugas asinkron. Proses induk terus meluncurkan tugas, proses anak sedang mempersiapkan proses tugas:
switch (fork()) { case -1: break; case 0: acquire_daemonlock(1); child_process(e, u); _exit(OK_EXIT); break; default: break; }
Ada banyak logika dalam child_process: ia mengambil output standar dan kesalahan mengalir ke dirinya sendiri, sehingga kemudian dapat dikirim ke email (jika variabel lingkungan MAILTO ditentukan dalam tabel tugas), dan, akhirnya, ia menunggu proses tugas utama selesai.
Proses tugas dibentuk oleh garpu lain:
switch (vfork()) { case -1: exit(ERROR_EXIT); case 0: (void) setsid(); setgid(e->gid); setuid(e->uid); chdir(env_get("HOME", e->envp)); { 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; }
Di sini, secara umum, dan seluruh cron. Saya menghilangkan beberapa detail menarik, misalnya, akuntansi untuk pengguna jarak jauh, tetapi menguraikan hal utama.
Kata penutup
Cron adalah program yang sangat sederhana dan bermanfaat, dibuat dalam tradisi terbaik dunia Unix. Dia tidak melakukan sesuatu yang berlebihan, tetapi dia telah melakukan pekerjaannya dengan luar biasa selama beberapa dekade terakhir. Mengenal kode versi yang disertakan dengan Ubuntu tidak lebih dari satu jam, dan saya mendapat banyak kesenangan! Semoga saya bisa membaginya dengan Anda.
Saya tidak tahu tentang Anda, tetapi agak menyedihkan bagi saya untuk menyadari bahwa pemrograman modern, dengan kecenderungan untuk menyulitkan dan abstrak kembali, telah lama berhenti memiliki kesederhanaan seperti itu.
Ada banyak alternatif modern untuk cron: systemd-timer memungkinkan Anda untuk mengatur sistem yang kompleks dengan dependensi, di fcron Anda dapat lebih fleksibel mengontrol konsumsi sumber daya berdasarkan tugas. Tapi secara pribadi, saya selalu punya crontab paling sederhana.
Singkatnya, cintai Unix, gunakan program sederhana dan jangan lupa membaca mana untuk platform Anda!