Cron unter Linux: Geschichte, Verwendung und Gerät

Cron unter Linux: Geschichte, Verwendung und Gerät

Der Klassiker schrieb, dass glückliche Stunden nicht zuschauen. In jenen wilden Zeiten gab es weder Programmierer noch Unix, aber heute wissen Programmierer genau: Cron wird anstelle von ihnen die Zeit im Auge behalten.

Befehlszeilenprogramme sind für mich sowohl eine Schwäche als auch eine lästige Pflicht. sed, awk, wc, cut und andere alte Programme werden täglich von Skripten auf unseren Servern ausgeführt. Viele davon sind als Aufgaben für Cron konzipiert, einen Planer aus den 70er Jahren.

Lange Zeit habe ich cron nur oberflächlich verwendet, ohne auf Details einzugehen, aber eines Tages, als ich beim Ausführen eines Skripts auf einen Fehler stieß, beschloss ich, mich gründlich damit zu befassen. So entstand dieser Artikel, während ich ihn schrieb, machte ich mich mit POSIX crontab, den wichtigsten Cron-Optionen in beliebten Linux-Distributionen und der Struktur einiger davon vertraut.

Verwenden Sie Linux und führen Sie Cron-Tasks aus? Interessieren Sie sich für Systemanwendungsarchitektur unter Unix? Dann machen wir uns auf den Weg!

Inhalt

Entstehung der Arten

Die regelmäßige Ausführung von Benutzer- oder Systemprogrammen ist in allen Betriebssystemen eine offensichtliche Notwendigkeit. Daher erkannten Programmierer schon vor langer Zeit den Bedarf an Diensten, die es ihnen ermöglichen, Aufgaben zentral zu planen und auszuführen.

Unix-ähnliche Betriebssysteme gehen auf Unix der Version 7 zurück, das in den 70er Jahren des letzten Jahrhunderts in den Bell Labs entwickelt wurde, unter anderem vom berühmten Ken Thompson. Version 7 Unix enthielt außerdem cron, einen Dienst zum regelmäßigen Ausführen von Superuser-Aufgaben.

Ein typischer moderner Cron ist ein einfaches Programm, aber der Betriebsalgorithmus der Originalversion war noch einfacher: Der Dienst wachte einmal pro Minute auf, las eine Tabelle mit Aufgaben aus einer einzelnen Datei (/etc/lib/crontab) und führte sie aus Superuser gibt die Aufgaben an, die im aktuellen Moment hätten ausgeführt werden sollen.

Anschließend wurden verbesserte Versionen des einfachen und nützlichen Dienstes mit allen Unix-ähnlichen Betriebssystemen ausgeliefert.

Verallgemeinerte Beschreibungen des Crontab-Formats und der Grundprinzipien der Funktionsweise des Dienstprogramms wurden 1992 in den Hauptstandard unixähnlicher Betriebssysteme – POSIX – aufgenommen, und so wurde cron von einem De-facto-Standard zu einem De-jure-Standard.

Im Jahr 1987 veröffentlichte Paul Vixie, nachdem er Unix-Benutzer nach ihren Wünschen für Cron befragt hatte, eine weitere Version des Daemons, die einige der Probleme des traditionellen Cron korrigierte und die Syntax von Tabellendateien erweiterte.

Mit der dritten Version von Vixie cron begann er, die POSIX-Anforderungen zu erfüllen, außerdem hatte das Programm eine liberale Lizenz, oder besser gesagt, es gab überhaupt keine Lizenz, mit Ausnahme der Wünsche in der README-Datei: Der Autor gibt keine Garantien, der Name des Autors kann nicht gelöscht werden und das Programm kann nur zusammen mit dem Quellcode verkauft werden. Es stellte sich heraus, dass diese Anforderungen mit den Prinzipien freier Software kompatibel waren, die in jenen Jahren immer beliebter wurden. Daher verwendeten einige der wichtigsten Linux-Distributionen, die in den frühen 90er Jahren auf den Markt kamen, Vixie cron als Systemversion und entwickeln es auch heute noch weiter.

Insbesondere entwickeln Red Hat und SUSE einen Fork von Vixie cron – cronie, und Debian und Ubuntu verwenden die Originalausgabe von Vixie cron mit vielen Patches.

Machen wir uns zunächst mit dem in POSIX beschriebenen Benutzerdienstprogramm crontab vertraut. Anschließend werfen wir einen Blick auf die in Vixie cron bereitgestellten Syntaxerweiterungen und die Verwendung von Variationen von Vixie cron in gängigen Linux-Distributionen. Und schließlich ist das Sahnehäubchen die Analyse des Cron-Daemon-Geräts.

POSIX-Crontab

Wenn der ursprüngliche Cron immer für den Superuser funktionierte, kümmern sich moderne Scheduler häufig um Aufgaben normaler Benutzer, was sicherer und bequemer ist.

Crons werden als Satz aus zwei Programmen bereitgestellt: dem ständig laufenden Cron-Daemon und dem Dienstprogramm crontab, das den Benutzern zur Verfügung steht. Letzteres ermöglicht Ihnen die Bearbeitung von Aufgabentabellen, die für jeden Benutzer im System spezifisch sind, während der Daemon Aufgaben aus Benutzer- und Systemtabellen startet.

В POSIX-Standard Das Verhalten des Daemons wird in keiner Weise beschrieben und nur das Benutzerprogramm formalisiert crontab. Das Vorhandensein von Mechanismen zum Starten von Benutzeraufgaben wird natürlich vorausgesetzt, aber nicht im Detail beschrieben.

Durch Aufrufen des Dienstprogramms crontab können Sie vier Dinge tun: die Aufgabentabelle des Benutzers im Editor bearbeiten, die Tabelle aus einer Datei laden, die aktuelle Aufgabentabelle anzeigen und die Aufgabentabelle löschen. Beispiele für die Funktionsweise des Dienstprogramms crontab:

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

Wenn angerufen crontab -e Es wird der in der Standardumgebungsvariablen angegebene Editor verwendet EDITOR.

Die Aufgaben selbst werden im folgenden Format beschrieben:

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

Die ersten fünf Felder der Datensätze: Minuten [1..60], Stunden [0..23], Tage des Monats [1..31], Monate [1..12], Tage der Woche [0..6]. .0], wobei XNUMX Sonntag ist. Das letzte, sechste Feld ist eine Zeile, die vom Standardbefehlsinterpreter ausgeführt wird.

In den ersten fünf Feldern können Werte durch Kommas getrennt aufgelistet werden:

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

Oder mit Bindestrich:

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

Der Benutzerzugriff auf die Aufgabenplanung wird in POSIX durch die Dateien cron.allow und cron.deny geregelt, in denen Benutzer mit Zugriff auf crontab bzw. Benutzer ohne Zugriff auf das Programm aufgeführt sind. Der Standard regelt den Speicherort dieser Dateien in keiner Weise.

Laut Standard müssen mindestens vier Umgebungsvariablen an gestartete Programme übergeben werden:

  1. HOME – Home-Verzeichnis des Benutzers.
  2. LOGNAME – Benutzeranmeldung.
  3. PATH ist der Pfad, unter dem Sie Standard-Systemdienstprogramme finden.
  4. SHELL – Pfad zum verwendeten Befehlsinterpreter.

Insbesondere sagt POSIX nichts darüber aus, woher die Werte für diese Variablen stammen.

Bestseller - Vixie cron 3.0pl1

Der gemeinsame Vorfahr der beliebten Cron-Varianten ist Vixie cron 3.0pl1, das 1992 in der Mailingliste comp.sources.unix eingeführt wurde. Wir werden die Hauptfunktionen dieser Version genauer betrachten.

Vixie cron gibt es in zwei Programmen (cron und crontab). Wie üblich ist der Daemon für das Lesen und Ausführen von Aufgaben aus der Systemaufgabentabelle und einzelnen Benutzeraufgabentabellen verantwortlich, und das Dienstprogramm crontab ist für die Bearbeitung von Benutzertabellen verantwortlich.

Aufgabentabelle und Konfigurationsdateien

Die Superuser-Aufgabentabelle befindet sich in /etc/crontab. Die Syntax der Systemtabelle entspricht der Syntax von Vixie cron, mit der Ausnahme, dass in der sechsten Spalte darin der Name des Benutzers angegeben ist, unter dessen Namen die Aufgabe gestartet wird:

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

Reguläre Benutzeraufgabentabellen befinden sich in /var/cron/tabs/username und verwenden dieselbe Syntax. Wenn Sie das Dienstprogramm crontab als Benutzer ausführen, werden diese Dateien bearbeitet.

Die Listen der Benutzer mit Zugriff auf crontab werden in den Dateien /var/cron/allow und /var/cron/deny verwaltet, wo Sie lediglich den Benutzernamen in einer separaten Zeile eingeben müssen.

Erweiterte Syntax

Im Vergleich zu POSIX crontab enthält die Lösung von Paul Vixey mehrere sehr nützliche Änderungen an der Syntax der Aufgabentabellen des Dienstprogramms.

Es ist eine neue Tabellensyntax verfügbar: Sie können beispielsweise Wochentage oder Monate namentlich angeben (Mo, Di usw.):

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

Sie können den Schritt angeben, durch den Aufgaben gestartet werden:

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

Schritte und Intervalle können gemischt werden:

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

Es werden intuitive Alternativen zur üblichen Syntax unterstützt (Neustart, jährlich, jährlich, monatlich, wöchentlich, täglich, Mitternacht, stündlich):

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

Umgebung zur Aufgabenausführung

Mit Vixie Cron können Sie die Umgebung laufender Anwendungen ändern.

Die Umgebungsvariablen USER, LOGNAME und HOME werden nicht einfach vom Daemon bereitgestellt, sondern aus einer Datei entnommen passwd. Die PATH-Variable ist auf „/usr/bin:/bin“ und die SHELL-Variable auf „/bin/sh“ gesetzt. Die Werte aller Variablen außer LOGNAME können in Benutzertabellen geändert werden.

Einige Umgebungsvariablen (insbesondere SHELL und HOME) werden von Cron selbst zum Ausführen der Aufgabe verwendet. So könnte die Verwendung von Bash anstelle von Standard-Sh zum Ausführen benutzerdefinierter Aufgaben aussehen:

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

Letztendlich werden alle in der Tabelle definierten Umgebungsvariablen (die von Cron verwendet werden oder vom Prozess benötigt werden) an die laufende Aufgabe übergeben.

Zum Bearbeiten von Dateien verwendet crontab den in der Umgebungsvariablen VISUAL oder EDITOR angegebenen Editor. Wenn in der Umgebung, in der crontab ausgeführt wurde, diese Variablen nicht definiert sind, wird „/usr/ucb/vi“ verwendet (ucb ist wahrscheinlich die University of California, Berkeley).

cron auf Debian und Ubuntu

Die Entwickler von Debian und abgeleiteten Distributionen haben veröffentlicht stark modifizierte Version Vixie Cron Version 3.0pl1. Es gibt keine Unterschiede in der Syntax von Tabellendateien; für Benutzer ist es der gleiche Vixie-Cron. Größte neue Funktion: Unterstützung syslog, SELinux и PAM.

Weniger auffällige, aber greifbare Änderungen umfassen den Speicherort von Konfigurationsdateien und Aufgabentabellen.

Benutzertabellen in Debian befinden sich im Verzeichnis /var/spool/cron/crontabs, die Systemtabelle ist immer noch dort – in /etc/crontab. Debian-Paketspezifische Aufgabentabellen werden in /etc/cron.d abgelegt, von wo aus sie der Cron-Daemon automatisch liest. Die Benutzerzugriffskontrolle wird durch die Dateien /etc/cron.allow und /etc/cron.deny gesteuert.

Die Standard-Shell ist immer noch /bin/sh, was in Debian eine kleine POSIX-kompatible Shell ist Strich, gestartet ohne das Lesen einer Konfiguration (im nicht interaktiven Modus).

Cron selbst wird in den neuesten Versionen von Debian über systemd gestartet, und die Startkonfiguration kann in /lib/systemd/system/cron.service eingesehen werden. In der Dienstkonfiguration gibt es nichts Besonderes; eine subtilere Aufgabenverwaltung kann über Umgebungsvariablen erfolgen, die direkt in der Crontab jedes Benutzers deklariert werden.

Cronie auf RedHat, Fedora und CentOS

Cronie – Fork von Vixie Cron Version 4.1. Wie in Debian hat sich die Syntax nicht geändert, aber Unterstützung für PAM und SELinux, die Arbeit in einem Cluster, die Verfolgung von Dateien mithilfe von inotify und andere Funktionen wurden hinzugefügt.

Die Standardkonfiguration befindet sich an den üblichen Stellen: Die Systemtabelle befindet sich in /etc/crontab, Pakete legen ihre Tabellen in /etc/cron.d ab, Benutzertabellen befinden sich in /var/spool/cron/crontabs.

Der Daemon läuft unter Systemd-Kontrolle, die Dienstkonfiguration ist /lib/systemd/system/crond.service.

Bei Red Hat-ähnlichen Distributionen wird standardmäßig /bin/sh beim Start verwendet, was der Standard-Bash entspricht. Es ist zu beachten, dass beim Ausführen von Cron-Jobs über /bin/sh die Bash-Shell im POSIX-kompatiblen Modus startet und keine zusätzliche Konfiguration liest, da sie im nicht interaktiven Modus ausgeführt wird.

Cronie in SLES und openSUSE

Die deutsche Distribution SLES und ihr Derivat openSUSE verwenden denselben Cronie. Der Daemon wird hier auch unter systemd gestartet, die Dienstkonfiguration befindet sich in /usr/lib/systemd/system/cron.service. Konfiguration: /etc/crontab, /etc/cron.d, /var/spool/cron/tabs. /bin/sh ist dieselbe Bash, die im POSIX-kompatiblen nicht interaktiven Modus ausgeführt wird.

Vixie Cron-Gerät

Moderne Nachkommen von cron haben sich im Vergleich zu Vixie cron nicht grundlegend verändert, aber dennoch neue Funktionen erhalten, die zum Verständnis der Prinzipien des Programms nicht erforderlich sind. Viele dieser Erweiterungen sind schlecht gestaltet und verwirren den Code. Es ist eine Freude, den ursprünglichen Cron-Quellcode von Paul Vixey zu lesen.

Daher habe ich beschlossen, das Cron-Gerät am Beispiel eines Cron-Programms zu analysieren, das beiden Entwicklungszweigen gemeinsam ist – Vixie cron 3.0pl1. Ich werde die Beispiele vereinfachen, indem ich ifdefs entferne, die das Lesen erschweren, und kleinere Details weglasse.

Die Arbeit des Dämons kann in mehrere Phasen unterteilt werden:

  1. Programminitialisierung.
  2. Sammeln und Aktualisieren der Liste der auszuführenden Aufgaben.
  3. Haupt-Cron-Schleife läuft.
  4. Starten Sie eine Aufgabe.

Schauen wir sie uns der Reihe nach an.

Initialisierung

Beim Start installiert cron nach Überprüfung der Prozessargumente die Signalhandler SIGCHLD und SIGHUP. Der erste macht einen Protokolleintrag über die Beendigung des Kindprozesses, der zweite schließt den Dateideskriptor der Protokolldatei:

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

Der Cron-Daemon läuft immer alleine auf dem System, nur als Superuser und aus dem Haupt-Cron-Verzeichnis. Die folgenden Aufrufe erstellen eine Sperrdatei mit der PID des Daemon-Prozesses, stellen sicher, dass der Benutzer korrekt ist und ändern das aktuelle Verzeichnis in das Hauptverzeichnis:

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

Es ist der Standardpfad festgelegt, der beim Starten von Prozessen verwendet wird:

setenv("PATH", _PATH_DEFPATH, 1);

Dann wird der Prozess „dämonisiert“: Er erstellt eine untergeordnete Kopie des Prozesses durch den Aufruf von fork und eine neue Sitzung im untergeordneten Prozess (Aufruf von „setsid“). Der übergeordnete Prozess wird nicht mehr benötigt und wird beendet:

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

Durch die Beendigung des übergeordneten Prozesses wird die Sperre für die Sperrdatei aufgehoben. Darüber hinaus ist es erforderlich, die PID in der Datei auf das untergeordnete Element zu aktualisieren. Anschließend wird die Aufgabendatenbank ausgefüllt:

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

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

Anschließend geht cron zum Hauptarbeitszyklus über. Vorher lohnt sich aber noch ein Blick auf das Laden der Aufgabenliste.

Sammeln und Aktualisieren der Aufgabenliste

Die Funktion „load_database“ ist für das Laden der Aufgabenliste verantwortlich. Es überprüft die Hauptsystem-Crontab und das Verzeichnis mit Benutzerdateien. Wenn sich die Dateien und das Verzeichnis nicht geändert haben, wird die Aufgabenliste nicht erneut gelesen. Andernfalls beginnt sich eine neue Aufgabenliste zu bilden.

Laden einer Systemdatei mit speziellen Datei- und Tabellennamen:

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

Benutzertabellen in einer Schleife laden:

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

Anschließend wird die alte Datenbank durch eine neue ersetzt.

In den obigen Beispielen überprüft der Funktionsaufruf „process_crontab“, ob ein Benutzer vorhanden ist, der dem Namen der Tabellendatei entspricht (es sei denn, es handelt sich um einen Superuser) und ruft dann „load_user“ auf. Letzterer liest bereits Zeile für Zeile die Datei selbst:

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

Dabei wird entweder die Umgebungsvariable mit den Funktionen „load_env“ bzw. „env_set“ gesetzt (Zeilen der Form VAR=value) oder die Aufgabenbeschreibung mit der Funktion „load_entry“ ausgelesen (* * * * * /path/to/exec).

Die von „load_entry“ zurückgegebene Eintragsentität ist unsere Aufgabe, die in der allgemeinen Liste der Aufgaben platziert wird. Die Funktion selbst führt eine ausführliche Analyse des Zeitformats durch, wir sind jedoch mehr an der Bildung von Umgebungsvariablen und Task-Startparametern interessiert:

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

Die Hauptschleife arbeitet mit der aktuellen Aufgabenliste.

Hauptschleife

Der ursprüngliche Cron von Version 7 Unix funktionierte ganz einfach: Er las die Konfiguration in einer Schleife erneut, startete die Aufgaben der aktuellen Minute als Superuser und schlief bis zum Beginn der nächsten Minute. Dieser einfache Ansatz erforderte auf älteren Maschinen zu viele Ressourcen.

In SysV wurde eine alternative Version vorgeschlagen, bei der der Daemon entweder bis zur nächsten Minute, für die die Aufgabe definiert wurde, oder für 30 Minuten in den Ruhezustand ging. In diesem Modus wurden weniger Ressourcen für das erneute Lesen der Konfiguration und das Überprüfen von Aufgaben verbraucht, aber das schnelle Aktualisieren der Aufgabenliste wurde umständlich.

Vixie Cron überprüfte wieder einmal pro Minute Aufgabenlisten, glücklicherweise gab es Ende der 80er Jahre deutlich mehr Ressourcen auf Standard-Unix-Rechnern:

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

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

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

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

Die Funktion cron_sleep ist direkt an der Ausführung von Aufgaben beteiligt und ruft die Funktionen job_runqueue (Aufgaben aufzählen und ausführen) und do_command (jede einzelne Aufgabe ausführen) auf. Es lohnt sich, die letzte Funktion genauer zu betrachten.

Eine Aufgabe ausführen

Die Funktion do_command wird im guten Unix-Stil ausgeführt, das heißt, sie führt einen Fork durch, um die Aufgabe asynchron auszuführen. Der übergeordnete Prozess startet weiterhin Aufgaben, der untergeordnete Prozess bereitet den Aufgabenprozess vor:

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

In child_process steckt ziemlich viel Logik: Es nimmt Standardausgabe- und Fehlerströme auf sich, um sie dann an die E-Mail zu senden (sofern die Umgebungsvariable MAILTO in der Aufgabentabelle angegeben ist) und wartet schließlich auf den Hauptprozess Prozess der zu erledigenden Aufgabe.

Der Aufgabenprozess wird durch einen weiteren Fork gebildet:

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

Das ist im Grunde alles, was Cron ist. Ich habe einige interessante Details weggelassen, zum Beispiel die Abrechnung von Remote-Benutzern, aber ich habe das Wesentliche skizziert.

Nachwort

Cron ist ein überraschend einfaches und nützliches Programm, das in den besten Traditionen der Unix-Welt entwickelt wurde. Sie macht nichts Besonderes, aber sie macht ihren Job schon seit mehreren Jahrzehnten wunderbar. Es dauerte nicht länger als eine Stunde, den Code für die mit Ubuntu gelieferte Version durchzuarbeiten, und es hat mir viel Spaß gemacht! Ich hoffe, ich konnte es mit Ihnen teilen.

Ich weiß nicht, wie es Ihnen geht, aber ich bin ein wenig traurig darüber, dass die moderne Programmierung mit ihrer Tendenz zur Überkomplizierung und Überabstraktion einer solchen Einfachheit schon lange nicht mehr förderlich ist.

Es gibt viele moderne Alternativen zu cron: Mit systemd-timers können Sie komplexe Systeme mit Abhängigkeiten organisieren, mit fcron können Sie den Ressourcenverbrauch durch Aufgaben flexibler regulieren. Mir persönlich hat aber immer die einfachste Crontab gereicht.

Kurz gesagt: Lieben Sie Unix, verwenden Sie einfache Programme und vergessen Sie nicht, das Mana für Ihre Plattform zu lesen!

Source: habr.com

Kommentar hinzufügen