Cron Linuxban: előzmények, használat és eszköz

Cron Linuxban: előzmények, használat és eszköz

A klasszikus azt írta, hogy a boldog órákat nem nézik. Azokban a vad időkben nem voltak sem programozók, sem Unix, de ma már a programozók biztosan tudják: helyettük a cron fogja nyomon követni az időt.

A parancssori segédprogramok egyszerre jelentenek gyengeséget és fáradságot. A sed, awk, wc, cut és más régi programokat minden nap szkriptek futtatják szervereinken. Sok közülük a cron feladatként készült, egy eredetileg a 70-es évek ütemezője.

Sokáig felületesen, a részletekbe nem bocsátkozva használtam a cron-t, de egy nap, amikor egy script futtatása közben hibába ütköztem, úgy döntöttem, alaposan utánanézek. Így jelent meg ez a cikk, írása közben megismerkedtem a POSIX crontabbal, a népszerű Linux disztribúciók főbb cron opcióival és ezek egy részének felépítésével.

Linuxot használ, és cron feladatokat futtat? Érdekel a Unix rendszeralkalmazás-architektúrája? Akkor már úton vagyunk!

Tartalom

A fajok eredete

A felhasználói vagy rendszerprogramok időszakos végrehajtása minden operációs rendszerben nyilvánvaló szükségesség. Ezért a programozók már rég felismerték, hogy olyan szolgáltatásokra van szükség, amelyek lehetővé teszik számukra a feladatok központi tervezését és végrehajtását.

A Unix-szerű operációs rendszerek a Unix 7-es verziójára nyúlnak vissza, amelyet a múlt század 70-es éveiben fejlesztett ki a Bell Labs, köztük a híres Ken Thompson. A Unix 7-es verziója tartalmazta a cron-t is, amely a szuperfelhasználói feladatok rendszeres futtatására szolgáló szolgáltatás.

Egy tipikus modern cron egy egyszerű program, de az eredeti verzió működési algoritmusa ennél is egyszerűbb volt: a szolgáltatás percenként egyszer felébredt, egyetlen fájlból (/etc/lib/crontab) beolvasott egy táblázatot a feladatokkal és végrehajtotta a superuser azokat a feladatokat, amelyeket az aktuális pillanatban végre kellett volna hajtani.

Ezt követően az egyszerű és hasznos szolgáltatás továbbfejlesztett változatait minden Unix-szerű operációs rendszerhez szállították.

A crontab formátum általánosított leírása és a segédprogram működésének alapelvei 1992-ben bekerültek a Unix-szerű operációs rendszerek fő szabványába, a POSIX-be, így a de facto szabványból származó cron de jure szabvány lett.

1987-ben Paul Vixie, miután megkérdezte a Unix felhasználókat a cron iránti kívánságaikról, kiadta a démon egy másik verzióját, amely kijavította a hagyományos cron néhány problémáját, és kibővítette a táblafájlok szintaxisát.

A Vixie cron harmadik verziója már kezdett megfelelni a POSIX követelményeknek, ráadásul a programnak liberális licence volt, vagy inkább nem volt licenc, kivéve a README-ben szereplő kívánságokat: a szerző nem ad garanciát, a szerző neve nem törölhető, és a program csak forráskóddal együtt értékesíthető. Ezek a követelmények kompatibilisnek bizonyultak az akkoriban egyre népszerűbb szabad szoftverek elveivel, így a 90-es évek elején megjelent kulcsfontosságú Linux disztribúciók közül néhány a Vixie cront vette rendszereként, és még ma is fejleszti.

Különösen a Red Hat és a SUSE fejleszti a Vixie cron - cronie forkját, a Debian és az Ubuntu pedig a Vixie cron eredeti kiadását használja számos javítással.

Először ismerkedjünk meg a POSIX-ben leírt crontab felhasználói segédprogrammal, majd a Vixie cronban biztosított szintaxis-kiterjesztéseket és a Vixie cron variációinak használatát a népszerű Linux disztribúciókban. És végül a cseresznye a tortán a cron démon eszköz elemzése.

POSIX crontab

Ha az eredeti cron mindig működött a szuperfelhasználónál, a modern ütemezők gyakran a hétköznapi felhasználók feladataival foglalkoznak, ami biztonságosabb és kényelmesebb.

A Cronok két programból állnak: a folyamatosan futó cron démonból és a felhasználók számára elérhető crontab segédprogramból. Ez utóbbi lehetővé teszi az egyes felhasználókhoz tartozó feladattáblázatok szerkesztését a rendszerben, míg a démon felhasználói és rendszertáblázatokból indít feladatokat.

В POSIX szabvány a démon viselkedése semmilyen módon nincs leírva, és csak a felhasználói program formalizált crontab. A felhasználói feladatok elindítására szolgáló mechanizmusok megléte természetesen benne van, de nincs részletesen leírva.

A crontab segédprogram meghívásával négy dolgot tehet: szerkesztheti a felhasználó feladattábláját a szerkesztőben, betöltheti a táblát egy fájlból, megjelenítheti az aktuális feladattáblázatot, és törölheti a feladattáblázatot. Példák a crontab segédprogram működésére:

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

Amikor hívják crontab -e a szabványos környezeti változóban megadott szerkesztő kerül felhasználásra EDITOR.

Magukat a feladatokat a következő formátumban ismertetjük:

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

A rekordok első öt mezője: perc [1..60], óra [0..23], hónap napjai [1..31], hónapok [1..12], hét napjai [0. .6], ahol a 0 a vasárnap. Az utolsó, hatodik mező egy sor, amelyet a szabványos parancsértelmező hajt végre.

Az első öt mezőben az értékek felsorolhatók vesszővel elválasztva:

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

Vagy kötőjellel:

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

A felhasználói hozzáférést a feladatütemezéshez a POSIX-ben a cron.allow és cron.deny fájlok szabályozzák, amelyek felsorolják a crontabhoz hozzáféréssel rendelkező, illetve a programhoz hozzáféréssel nem rendelkező felhasználókat. A szabvány semmilyen módon nem szabályozza ezen fájlok helyét.

A szabvány szerint legalább négy környezeti változót kell átadni az elindított programoknak:

  1. HOME – a felhasználó saját könyvtára.
  2. LOGNAME – felhasználói bejelentkezés.
  3. A PATH az az útvonal, ahol szabványos rendszersegédprogramokat találhat.
  4. SHELL — a használt parancsértelmező elérési útja.

A POSIX nem mond semmit arról, hogy honnan származnak ezeknek a változóknak az értékei.

Legkeresettebb - Vixie cron 3.0pl1

A népszerű cron változatok közös őse a Vixie cron 3.0pl1, amelyet 1992-ben vezettek be a comp.sources.unix levelezőlistán. Részletesebben megvizsgáljuk ennek a verziónak a főbb jellemzőit.

A Vixie cron két programban érhető el (cron és crontab). Szokás szerint a démon felelős a feladatok beolvasásáért és futtatásáért a rendszerfeladattáblából és az egyes felhasználói feladattáblákból, a crontab segédprogram pedig a felhasználói táblák szerkesztéséért.

Feladattábla és konfigurációs fájlok

A szuperfelhasználói feladattábla az /etc/crontab fájlban található. A rendszertábla szintaxisa megegyezik a Vixie cron szintaxisával, azzal az eltéréssel, hogy a hatodik oszlopban található annak a felhasználónak a neve, akinek nevében a feladat elindul:

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

A normál felhasználói feladattáblázatok a /var/cron/tabs/username könyvtárban találhatók, és ugyanazt a szintaxist használják. Amikor felhasználóként futtatja a crontab segédprogramot, ezek a fájlok kerülnek szerkesztésre.

A crontabhoz hozzáféréssel rendelkező felhasználók listája a /var/cron/allow és /var/cron/deny fájlokban kezelhető, ahol csak a felhasználónevet kell külön sorban megadni.

Kiterjesztett szintaxis

A POSIX crontabhoz képest Paul Vixey megoldása számos nagyon hasznos módosítást tartalmaz a segédprogram feladattáblázatainak szintaxisában.

Új táblázat szintaxis vált elérhetővé: például megadhatja a hét napjait vagy hónapjait név szerint (hétfő, kedd stb.):

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

Megadhatja azt a lépést, amelyen keresztül a feladatok elindulnak:

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

A lépések és az intervallumok keverhetők:

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

A szokásos szintaxis intuitív alternatívái támogatottak (újraindítás, évente, évente, havonta, hetente, naponta, éjfél, óránként):

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

Feladatvégrehajtási környezet

A Vixie cron lehetővé teszi a futó alkalmazások környezetének megváltoztatását.

A USER, LOGNAME és HOME környezeti változókat nem egyszerűen a démon biztosítja, hanem egy fájlból veszik passwd. A PATH változó értéke "/usr/bin:/bin", a SHELL változó pedig "/bin/sh". A LOGNAME kivételével az összes változó értéke módosítható a felhasználói táblákban.

Néhány környezeti változót (leginkább SHELL és HOME) maga a cron használ a feladat futtatásához. Így nézhet ki a bash használata a standard sh helyett az egyéni feladatok futtatásához:

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

Végül a táblázatban definiált összes környezeti változó (amelyet a cron használ vagy a folyamathoz szükséges) át lesz adva a futó feladatnak.

A fájlok szerkesztéséhez a crontab a VISUAL vagy EDITOR környezeti változóban megadott szerkesztőt használja. Ha a környezetben, ahol a crontab futott, nincsenek megadva ezek a változók, akkor az "/usr/ucb/vi" fájlt használja (az ucb valószínűleg a Kaliforniai Egyetem, Berkeley).

cron Debianon és Ubuntun

A Debian és a származékos disztribúciók fejlesztői kiadták erősen módosított változat Vixie cron 3.0pl1 verzió. A táblázatfájlok szintaxisában nincs különbség, a felhasználók számára ugyanaz a Vixie cron. Legnagyobb új funkció: Támogatás syslog, SELinux и PAM.

A kevésbé észrevehető, de kézzelfogható változások közé tartozik a konfigurációs fájlok és a feladattáblázatok helye.

A Debian felhasználói táblái a /var/spool/cron/crontabs könyvtárban találhatók, a rendszertábla továbbra is ott van - az /etc/crontab könyvtárban. A Debian csomagspecifikus feladattáblák az /etc/cron.d könyvtárba kerülnek, ahonnan a cron démon automatikusan beolvassa őket. A felhasználói hozzáférés szabályozását az /etc/cron.allow és /etc/cron.deny fájlok szabályozzák.

Az alapértelmezett shell továbbra is a /bin/sh, ami a Debianban egy kis POSIX-kompatibilis shell gondolatjel, konfiguráció olvasása nélkül indul el (nem interaktív módban).

Maga a Cron a Debian legújabb verzióiban a systemd-n keresztül indul el, az indítási konfiguráció pedig a /lib/systemd/system/cron.service fájlban tekinthető meg. A szolgáltatás konfigurációjában nincs semmi különös, minden finomabb feladatkezelés elvégezhető az egyes felhasználók crontabjában közvetlenül deklarált környezeti változókon keresztül.

crownie a RedHat, a Fedora és a CentOS rendszeren

Cronie — a Vixie cron 4.1-es verziójának villája. A Debianhoz hasonlóan a szintaxis nem változott, de hozzáadták a PAM és a SELinux támogatását, a fürtben való munkát, a fájlok nyomon követését az inotify segítségével és egyéb funkciókat.

Az alapértelmezett konfiguráció a szokásos helyeken van: a rendszertábla az /etc/crontab könyvtárban van, a csomagok a /etc/cron.d könyvtárban, a felhasználói táblák a /var/spool/cron/crontabs könyvtárban vannak.

A démon systemd vezérlés alatt fut, a szolgáltatás konfigurációja /lib/systemd/system/crond.service.

A Red Hat-szerű disztribúciókon alapértelmezés szerint a /bin/sh-t használják indításkor, ami a szabványos bash. Megjegyzendő, hogy a cron feladatok /bin/sh-n keresztüli futtatásakor a bash shell POSIX-kompatibilis módban indul el, és nem olvas semmilyen további konfigurációt, nem interaktív módban fut.

cronie az SLES-ben és az openSUSE-ban

A német SLES disztribúció és származéka, az openSUSE ugyanazt a barátot használja. A démon itt is systemd alatt indul, a szolgáltatás konfigurációja a /usr/lib/systemd/system/cron.service könyvtárban található. Konfiguráció: /etc/crontab, /etc/cron.d, /var/spool/cron/tabs. A /bin/sh ugyanaz a bash, amely POSIX-kompatibilis, nem interaktív módban fut.

Vixie cron készülék

A cron modern leszármazottai nem változtak radikálisan a Vixie cronhoz képest, de még mindig olyan új funkciókat szereztek, amelyek nem szükségesek a program elveinek megértéséhez. E kiterjesztések közül sok rosszul van megtervezve, és összezavarja a kódot. Paul Vixey eredeti cron forráskódját öröm olvasni.

Ezért úgy döntöttem, hogy elemzem a cron eszközt egy mindkét fejlesztési ágban közös cron program - Vixie cron 3.0pl1 - példáján. Leegyszerűsítem a példákat az olvasást bonyolító ifdef-ek eltávolításával és a kisebb részletek kihagyásával.

A démon munkája több szakaszra osztható:

  1. Program inicializálása.
  2. A futtatandó feladatok listájának összegyűjtése és frissítése.
  3. Fő cron hurok fut.
  4. Indítson el egy feladatot.

Nézzük őket sorban.

Inicializálás

Indításkor a folyamatargumentumok ellenőrzése után a cron telepíti a SIGCHLD és SIGHUP jelkezelőket. Az első naplóbejegyzést készít a gyermekfolyamat leállásáról, a második pedig bezárja a naplófájl fájlleíróját:

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

A cron démon mindig egyedül fut a rendszeren, csak szuperfelhasználóként és a fő cron könyvtárból. A következő hívások létrehoznak egy zárfájlt a démonfolyamat PID-jével, győződjön meg arról, hogy a felhasználó helyes, és módosítsa az aktuális könyvtárat a fő könyvtárra:

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

Be van állítva az alapértelmezett elérési út, amelyet a folyamatok indításakor használunk:

setenv("PATH", _PATH_DEFPATH, 1);

Ezután a folyamatot „démonizálják”: a fork meghívásával létrehozza a folyamat gyermekmásolatát, és egy új szekciót a gyermekfolyamatban (a setsid meghívásával). A szülő folyamatra már nincs szükség, és kilép:

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

A szülőfolyamat leállítása feloldja a zárolási fájl zárolását. Ezenkívül frissíteni kell a gyermek PID-jét a fájlban. Ezt követően a feladat adatbázis kitöltésre kerül:

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

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

Ezután a cron áttér a fő munkaciklusra. De előtte érdemes egy pillantást vetni a feladatlista betöltésére.

Feladatlista összegyűjtése, frissítése

A load_database függvény feladata a feladatlista betöltése. Ellenőrzi a fő rendszer crontab-ját és a felhasználói fájlokat tartalmazó könyvtárat. Ha a fájlok és a könyvtár nem változott, a feladatlista nem kerül újraolvasásra. Ellenkező esetben új feladatlista kezd kialakulni.

Speciális fájl- és táblanevekkel rendelkező rendszerfájl betöltése:

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

Felhasználói táblák betöltése ciklusban:

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

Ezt követően a régi adatbázis lecserélődik egy újra.

A fenti példákban a process_crontab függvényhívás ellenőrzi, hogy létezik-e a táblafájl nevének megfelelő felhasználó (hacsak nem szuperfelhasználó), majd meghívja a load_user-t. Ez utóbbi már magát a fájlt is beolvassa soronként:

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

Itt vagy a környezeti változót állítjuk be (VAR=value alakú sorok) a load_env / env_set függvényekkel, vagy a feladatleírást olvassuk be (* * * * * /path/to/exec) a load_entry függvény segítségével.

A load_entry által visszaadott bejegyzés entitás a mi feladatunk, amely az általános feladatlistában található. Maga a függvény az időformátum bőbeszédű elemzését végzi, de minket inkább a környezeti változók és a feladatindítási paraméterek kialakítása érdekel:

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

A főhurok az aktuális feladatlistával működik.

Fő hurok

A Unix 7-es verziójának eredeti cronja egészen egyszerűen működött: ciklusban újraolvasta a konfigurációt, szuperfelhasználóként elindította az aktuális perc feladatait, és aludt a következő perc kezdetéig. Ez az egyszerű megközelítés a régebbi gépeken túl sok erőforrást igényelt.

A SysV-ben egy alternatív változatot javasoltak, amelyben a démon vagy a legközelebbi percig, amelyre a feladat meghatározásra került, vagy 30 percig aludt. Ebben a módban kevesebb erőforrást igényelt a konfiguráció újraolvasása és a feladatok ellenőrzése, de a feladatlista gyors frissítése kényelmetlenné vált.

Vixie cron percenként egyszer visszatért a feladatlisták ellenőrzéséhez, szerencsére a 80-as évek végére lényegesen több erőforrás állt rendelkezésre a szabványos Unix gépeken:

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

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

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

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

A cron_sleep függvény közvetlenül részt vesz a feladatok végrehajtásában, meghívva a job_runqueue (feladatok felsorolása és futtatása) és do_command (az egyes feladatok futtatása) függvényeket. Az utolsó függvényt érdemes részletesebben megvizsgálni.

Feladat futtatása

A do_command függvény jó Unix-stílusban fut, vagyis egy elágazást végez a feladat aszinkron végrehajtására. A szülő folyamat folytatja a feladatok elindítását, a gyermek folyamat előkészíti a feladatfolyamatot:

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

A child_process-ben elég sok logika van: magára veszi a szabványos kimeneti és hibafolyamokat, hogy aztán postára küldje (ha a MAILTO környezeti változót megadjuk a feladattáblázatban), és végül megvárja a fő a feladat elvégzésének folyamata.

A feladat folyamatát egy másik villa képezi:

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

Lényegében ennyi a cron. Kihagytam néhány érdekes részletet, például a távoli felhasználók elszámolását, de a lényeget felvázoltam.

utószó

A Cron egy meglepően egyszerű és hasznos program, amely a Unix világ legjobb hagyományai szerint készült. Semmi extrát nem csinál, de már több évtizede csodálatosan végzi a munkáját. Az Ubuntuhoz tartozó verzió kódjának átolvasása nem tartott tovább egy óránál, és nagyon jól éreztem magam! Remélem sikerült megosztanom veletek.

Nem tudom, ti hogy vagytok vele, de egy kicsit szomorúan veszem észre, hogy a modern programozás a túlbonyolításra és absztraktságra való hajlamával már régóta nem kedvezett az ilyen egyszerűségnek.

A cronnak számos modern alternatívája létezik: a systemd-timerek lehetővé teszik a komplex rendszerek függőségi rendszerezését, az fcron pedig az erőforrás-felhasználás feladatokonkénti rugalmasabb szabályozását. De személy szerint nekem mindig elég volt a legegyszerűbb crontab.

Röviden, szeresd a Unixot, használj egyszerű programokat, és ne felejtsd el elolvasni a platformod manát!

Forrás: will.com

Hozzászólás