Cron u Linuxu: povijest, upotreba i uređaj

Cron u Linuxu: povijest, upotreba i uređaj

Klasik je napisao da sretni sati ne gledaju. U tim divljim vremenima nije bilo ni programera ni Unixa, ali danas programeri sigurno znaju: cron će pratiti vrijeme umjesto njih.

Pomoćni programi naredbenog retka za mene su i slabost i zadatak. sed, awk, wc, cut i drugi stari programi pokreću se skriptama na našim poslužiteljima svaki dan. Mnogi od njih dizajnirani su kao zadaci za cron, planer izvorno iz 70-ih.

Dugo sam koristio cron površno, ne ulazeći u detalje, ali jednog dana, kada sam naišao na grešku prilikom pokretanja skripte, odlučio sam to temeljito ispitati. Tako je nastao ovaj članak, dok sam ga pisao upoznao sam se s POSIX crontabom, glavnim cron opcijama u popularnim Linux distribucijama i strukturom nekih od njih.

Koristite li Linux i izvodite cron zadatke? Jeste li zainteresirani za arhitekturu aplikacija sustava u Unixu? Onda krećemo!

sadržaj

Podrijetlo vrste

Periodično izvršavanje korisničkih ili sistemskih programa očigledna je potreba u svim operacijskim sustavima. Stoga su programeri davno shvatili potrebu za servisima koji im omogućuju centralno planiranje i izvršavanje zadataka.

Operativni sustavi slični Unixu vuku svoje podrijetlo od verzije 7 Unixa, razvijene 70-ih godina prošlog stoljeća u Bell Labsu, uključujući i slavnog Kena Thompsona. Verzija 7 Unixa također je uključivala cron, uslugu za redovito izvršavanje zadataka superkorisnika.

Tipičan moderni cron je jednostavan program, ali algoritam rada izvorne verzije bio je još jednostavniji: servis se budio jednom u minuti, čitao tablicu sa zadacima iz jedne datoteke (/etc/lib/crontab) i izvodio za superkorisnik oni zadaci koji su trebali biti izvršeni u trenutnom trenutku.

Nakon toga, poboljšane verzije jednostavne i korisne usluge isporučene su sa svim operativnim sustavima sličnim Unixu.

Generalizirani opisi crontab formata i osnovnih principa rada uslužnog programa uključeni su 1992. godine u glavni standard Unix-like operativnih sustava - POSIX, te je tako cron od standarda de facto postao standard de jure.

Godine 1987. Paul Vixie, nakon što je ispitao korisnike Unixa o njihovim željama za cron, izdao je drugu verziju demona koji je ispravio neke od problema tradicionalnog crona i proširio sintaksu tabličnih datoteka.

Do treće verzije Vixie cron je počeo ispunjavati zahtjeve POSIX-a, osim toga, program je imao liberalnu licencu, točnije licence uopće nije bilo, osim želja u README-u: autor ne daje jamstva, ime autora ne može se izbrisati, a program se može prodati samo zajedno s izvornim kodom. Pokazalo se da su ovi zahtjevi kompatibilni s načelima slobodnog softvera koji je tih godina dobivao na popularnosti, pa su neke od ključnih distribucija Linuxa koje su se pojavile početkom 90-ih uzele Vixie cron kao sustav i razvijaju ga i danas.

Konkretno, Red Hat i SUSE razvijaju fork Vixie cron - cronie, a Debian i Ubuntu koriste originalno izdanje Vixie cron s mnogo zakrpa.

Najprije se upoznajmo s korisničkim uslužnim programom crontab opisanim u POSIX-u, nakon čega ćemo pogledati proširenja sintakse u Vixie cron-u i korištenje varijacija Vixie cron-a u popularnim distribucijama Linuxa. I na kraju, trešnja na torti je analiza cron daemon uređaja.

POSIX crontab

Ako je izvorni cron uvijek radio za superkorisnika, moderni planeri često se bave zadacima običnih korisnika, što je sigurnije i praktičnije.

Crons se isporučuju kao skup od dva programa: stalno pokrenut cron daemon i crontab uslužni program dostupan korisnicima. Potonji vam omogućuje uređivanje tablica zadataka specifičnih za svakog korisnika u sustavu, dok demon pokreće zadatke iz korisničkih i sistemskih tablica.

В POSIX standard ponašanje daemona nije opisano ni na koji način i samo je korisnički program formaliziran crontab. Postojanje mehanizama za pokretanje korisničkih zadataka se, naravno, podrazumijeva, ali nije detaljno opisano.

Pozivanjem uslužnog programa crontab možete učiniti četiri stvari: urediti korisničku tablicu zadataka u uređivaču, učitati tablicu iz datoteke, prikazati trenutnu tablicu zadataka i očistiti tablicu zadataka. Primjeri kako radi uslužni program crontab:

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

Na poziv crontab -e koristit će se uređivač naveden u standardnoj varijabli okruženja EDITOR.

Sami zadaci opisani su u sljedećem formatu:

# строки-комментарии игнорируются
#
# задача, выполняемая ежеминутно
* * * * * /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

Prvih pet polja zapisa: minute [1..60], sati [0..23], dani u mjesecu [1..31], mjeseci [1..12], dani u tjednu [0. .6], gdje je 0 nedjelja. Posljednje, šesto, polje je linija koju će izvršiti standardni interpreter naredbi.

U prvih pet polja mogu se navesti vrijednosti odvojene zarezima:

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

Ili s crticom:

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

Korisnički pristup raspoređivanju zadataka reguliran je u POSIX-u datotekama cron.allow i cron.deny, koje navode korisnike s pristupom crontabu i korisnike koji nemaju pristup programu. Standard ni na koji način ne regulira mjesto tih datoteka.

Prema standardu, najmanje četiri varijable okruženja moraju se proslijediti pokrenutim programima:

  1. HOME - početni imenik korisnika.
  2. LOGNAME — prijava korisnika.
  3. PATH je staza na kojoj možete pronaći standardne pomoćne programe sustava.
  4. SHELL — put do korištenog tumača naredbi.

Naime, POSIX ne govori ništa o tome odakle dolaze vrijednosti za ove varijable.

Najprodavaniji - Vixie cron 3.0pl1

Zajednički predak popularnih cron varijanti je Vixie cron 3.0pl1, predstavljen na comp.sources.unix mailing listi 1992. godine. Detaljnije ćemo razmotriti glavne značajke ove verzije.

Vixie cron dolazi u dva programa (cron i crontab). Kao i obično, demon je odgovoran za čitanje i pokretanje zadataka iz tablice zadataka sustava i pojedinačnih tablica zadataka korisnika, a uslužni program crontab odgovoran je za uređivanje korisničkih tablica.

Tablica zadataka i konfiguracijske datoteke

Tablica zadataka superkorisnika nalazi se u /etc/crontab. Sintaksa sistemske tablice odgovara sintaksi Vixie crona, s tim da šesti stupac u njoj označava ime korisnika u čije ime se pokreće zadatak:

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

Redovne tablice korisničkih zadataka nalaze se u /var/cron/tabs/username i koriste istu sintaksu. Kada pokrenete uslužni program crontab kao korisnik, ovo su datoteke koje se uređuju.

Popisima korisnika s pristupom crontabu upravlja se u datotekama /var/cron/allow i /var/cron/deny, gdje samo trebate unijeti korisničko ime u poseban redak.

Proširena sintaksa

U usporedbi s POSIX crontabom, rješenje Paula Vixeya sadrži nekoliko vrlo korisnih izmjena sintakse tablica zadataka uslužnog programa.

Nova sintaksa tablice postala je dostupna: na primjer, možete navesti dane u tjednu ili mjesece po imenu (pon, uto i tako dalje):

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

Možete odrediti korak kroz koji se zadaci pokreću:

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

Koraci i intervali mogu se miješati:

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

Podržane su intuitivne alternative uobičajenoj sintaksi (ponovno pokretanje, godišnje, godišnje, mjesečno, tjedno, dnevno, ponoć, svaki sat):

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

Okruženje izvršenja zadatka

Vixie cron omogućuje promjenu okruženja pokrenutih aplikacija.

Varijable okoline USER, LOGNAME i HOME nisu samo dane od demona, već su preuzete iz datoteke passwd. Varijabla PATH postavljena je na "/usr/bin:/bin", a varijabla SHELL na "/bin/sh". Vrijednosti svih varijabli osim LOGNAME mogu se mijenjati u korisničkim tablicama.

Neke varijable okruženja (najviše SHELL i HOME) koristi sam cron za pokretanje zadatka. Evo kako bi korištenje basha umjesto standardnog sh-a za pokretanje prilagođenih zadataka moglo izgledati:

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

Naposljetku, sve varijable okruženja definirane u tablici (koje koristi cron ili su potrebne procesu) bit će proslijeđene zadatku koji se izvodi.

Za uređivanje datoteka, crontab koristi uređivač naveden u varijabli okruženja VISUAL ili EDITOR. Ako okruženje u kojem je pokrenut crontab nema definirane ove varijable, tada se koristi "/usr/ucb/vi" (ucb je vjerojatno Sveučilište Kalifornije, Berkeley).

cron na Debianu i Ubuntuu

Programeri Debiana i izvedenih distribucija objavili su visoko modificirana verzija Vixie cron verzija 3.0pl1. Nema razlika u sintaksi tabličnih datoteka; za korisnike je to isti Vixie cron. Najveća nova značajka: podrška syslog, Uklanjanje poteškoća и PAM.

Manje uočljive, ali opipljive promjene uključuju lokaciju konfiguracijskih datoteka i tablica zadataka.

Korisničke tablice u Debianu nalaze se u direktoriju /var/spool/cron/crontabs, sistemska tablica je još uvijek tamo - u /etc/crontab. Tablice zadataka specifičnih za Debian pakete smještene su u /etc/cron.d, odakle ih cron demon automatski čita. Kontrolu korisničkog pristupa kontroliraju datoteke /etc/cron.allow i /etc/cron.deny.

Zadana ljuska je i dalje /bin/sh, što je u Debianu mala ljuska kompatibilna s POSIX-om crtica, pokrenut bez čitanja konfiguracije (u neinteraktivnom načinu).

Sam Cron se u zadnjim verzijama Debiana pokreće preko systemd-a, a konfiguraciju pokretanja možete vidjeti u /lib/systemd/system/cron.service. Nema ničeg posebnog u konfiguraciji usluge; svako suptilnije upravljanje zadacima može se obaviti putem varijabli okruženja deklariranih izravno u crontabu svakog korisnika.

prijatelj na RedHatu, Fedori i CentOS-u

kroni — fork Vixie cron verzije 4.1. Kao iu Debianu, sintaksa se nije promijenila, ali je dodana podrška za PAM i SELinux, rad u klasteru, praćenje datoteka pomoću inotifyja i druge značajke.

Zadana konfiguracija je na uobičajenim mjestima: sistemska tablica je u /etc/crontab, paketi stavljaju svoje tablice u /etc/cron.d, korisničke tablice idu u /var/spool/cron/crontabs.

Daemon radi pod kontrolom systemd, konfiguracija usluge je /lib/systemd/system/crond.service.

Na distribucijama sličnim Red Hatu, /bin/sh se koristi prema zadanim postavkama pri pokretanju, što je standardni bash. Treba imati na umu da prilikom pokretanja cron poslova putem /bin/sh, bash shell se pokreće u POSIX-kompatibilnom načinu rada i ne čita nikakve dodatne konfiguracije, radeći u neinteraktivnom načinu rada.

cronie u SLES-u i openSUSE-u

Njemačka distribucija SLES i njezina izvedenica openSUSE koriste istog prijatelja. Daemon se ovdje također pokreće pod systemd, konfiguracija usluge nalazi se u /usr/lib/systemd/system/cron.service. Konfiguracija: /etc/crontab, /etc/cron.d, /var/spool/cron/tabs. /bin/sh je isti bash koji radi u POSIX-kompatibilnom neinteraktivnom načinu rada.

Vixie cron uređaj

Moderni potomci crona nisu se radikalno promijenili u usporedbi s Vixie cron, ali su ipak stekli nove značajke koje nisu potrebne za razumijevanje principa programa. Mnoga od ovih proširenja su loše dizajnirana i zbunjuju kod. Izvorni cron izvorni kod Paula Vixeya užitak je čitati.

Stoga sam odlučio analizirati cron uređaj na primjeru cron programa zajedničkog za obje grane razvoja - Vixie cron 3.0pl1. Pojednostavit ću primjere uklanjanjem ifdefova koji kompliciraju čitanje i izostavljanjem manjih detalja.

Rad demona može se podijeliti u nekoliko faza:

  1. Inicijalizacija programa.
  2. Prikupljanje i ažuriranje popisa zadataka za pokretanje.
  3. Glavna cron petlja radi.
  4. Pokrenite zadatak.

Pogledajmo ih redom.

Inicijalizacija

Kada se pokrene, nakon provjere argumenata procesa, cron instalira rukovatelje signalima SIGCHLD i SIGHUP. Prvi pravi unos u dnevnik o prekidu podređenog procesa, drugi zatvara deskriptor datoteke dnevnika:

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

Cron demon uvijek radi sam na sustavu, samo kao superkorisnik i iz glavnog cron direktorija. Sljedeći pozivi stvaraju datoteku zaključavanja s PID-om daemon procesa, uvjeravaju se da je korisnik ispravan i mijenjaju trenutni direktorij u glavni:

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

Postavlja se zadana staza koja će se koristiti prilikom pokretanja procesa:

setenv("PATH", _PATH_DEFPATH, 1);

Zatim se proces “demonizira”: stvara podređenu kopiju procesa pozivanjem fork-a i nove sesije u podređenom procesu (pozivanjem setsid). Nadređeni proces više nije potreban i izlazi:

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

Prekid nadređenog procesa otključava datoteku zaključavanja. Osim toga, potrebno je ažurirati PID u datoteci za dijete. Nakon toga se popunjava baza zadataka:

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

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

Zatim cron prelazi na glavni radni ciklus. Ali prije toga, vrijedi pogledati učitavanje popisa zadataka.

Prikupljanje i ažuriranje popisa zadataka

Funkcija load_database odgovorna je za učitavanje popisa zadataka. Provjerava crontab glavnog sustava i direktorij s korisničkim datotekama. Ako se datoteke i direktorij nisu promijenili, popis zadataka se ne čita ponovno. U protivnom se počinje formirati novi popis zadataka.

Učitavanje sistemske datoteke s posebnim nazivima datoteka i tablica:

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

Učitavanje korisničkih tablica u petlji:

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);
}

Nakon toga se stara baza podataka zamjenjuje novom.

U gornjim primjerima, poziv funkcije process_crontab provjerava postoji li korisnik koji odgovara nazivu datoteke tablice (osim ako nije superkorisnik), a zatim poziva load_user. Potonji već čita samu datoteku red po red:

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

Ovdje se ili postavlja varijabla okruženja (redovi u obliku VAR=vrijednost) pomoću funkcija load_env / env_set ili se opis zadatka čita (* * * * * /path/to/exec) pomoću funkcije load_entry.

Entitet unosa koji load_entry vraća je naš zadatak, koji se nalazi na općem popisu zadataka. Sama funkcija provodi detaljnu analizu formata vremena, ali nas više zanima formiranje varijabli okruženja i parametara pokretanja zadatka:

/* пользователь и группа для запуска задачи берутся из 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);

Glavna petlja radi s trenutnim popisom zadataka.

Glavna petlja

Izvorni cron iz verzije 7 Unixa radio je prilično jednostavno: ponovno je čitao konfiguraciju u petlji, pokretao zadatke tekuće minute kao superkorisnik i spavao do početka sljedeće minute. Ovaj jednostavan pristup na starijim strojevima zahtijevao je previše resursa.

U SysV-u je predložena alternativna verzija, u kojoj je demon odlazio u stanje mirovanja ili do najbliže minute za koju je zadatak definiran, ili do 30 minuta. Potrošeno je manje resursa za ponovno čitanje konfiguracije i provjeru zadataka u ovom načinu rada, ali brzo ažuriranje popisa zadataka postalo je nezgodno.

Vixie cron vratio se provjeravanju popisa zadataka jednom u minuti, srećom do kraja 80-ih bilo je znatno više resursa na standardnim Unix strojevima:

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

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

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

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

Funkcija cron_sleep izravno je uključena u izvršavanje zadataka, pozivajući funkcije job_runqueue (nabrajanje i pokretanje zadataka) i do_command (izvođenje svakog pojedinačnog zadatka). Posljednju funkciju vrijedi detaljnije ispitati.

Pokretanje zadatka

Funkcija do_command izvodi se u dobrom stilu Unixa, to jest, radi račvanje za asinkrono izvođenje zadatka. Roditeljski proces nastavlja s pokretanjem zadataka, podređeni proces priprema proces zadatka:

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

Postoji dosta logike u child_processu: prihvaća standardni izlaz i tokove pogrešaka, zatim ih šalje na mail (ako je varijabla okoline MAILTO navedena u tablici zadatka), i na kraju čeka da glavni proces zadatka potpuna.

Proces zadatka formira još jedna vilica:

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

To je zapravo sve što cron jest. Izostavio sam neke zanimljive detalje, na primjer, računovodstvo za udaljene korisnike, ali sam iznio ono glavno.

pogovor

Cron je iznenađujuće jednostavan i koristan program, napravljen u najboljim tradicijama Unix svijeta. Ona ne radi ništa posebno, ali već nekoliko desetljeća odlično radi svoj posao. Proučavanje koda za verziju koja dolazi s Ubuntuom nije trajalo više od sat vremena i jako sam se zabavio! Nadam se da sam to uspio podijeliti s vama.

Ne znam za vas, ali ja sam pomalo tužan kada shvatim da moderno programiranje, sa svojom tendencijom prekompliciranja i apstrahiranja, već dugo nije bilo pogodno za takvu jednostavnost.

Postoji mnogo modernih alternativa cronu: systemd-timeri omogućuju organiziranje složenih sustava s ovisnostima, fcron vam omogućuje fleksibilniju regulaciju potrošnje resursa po zadacima. Ali osobno, najjednostavniji crontab mi je uvijek bio dovoljan.

Ukratko, volite Unix, koristite jednostavne programe i ne zaboravite pročitati manu za svoju platformu!

Izvor: www.habr.com

Dodajte komentar