Cron trong Linux: lịch sử, cách sử dụng và thiết bị

Cron trong Linux: lịch sử, cách sử dụng và thiết bị

Cổ điển đã viết rằng giờ hạnh phúc không xem. Vào thời kỳ hoang dã đó, không có lập trình viên cũng như Unix, nhưng ngày nay các lập trình viên biết chắc chắn: cron sẽ theo dõi thời gian thay vì họ.

Các tiện ích dòng lệnh vừa là điểm yếu vừa là một việc vặt đối với tôi. sed, awk, wc, cut và các chương trình cũ khác được chạy hàng ngày bằng các tập lệnh trên máy chủ của chúng tôi. Nhiều trong số chúng được thiết kế làm nhiệm vụ cho cron, một công cụ lập lịch có từ những năm 70.

Lâu nay tôi sử dụng cron một cách hời hợt, không đi sâu vào chi tiết, nhưng một ngày nọ, khi gặp lỗi khi chạy một đoạn script, tôi quyết định tìm hiểu kỹ. Đây là cách bài viết này xuất hiện, trong khi viết nó, tôi đã làm quen với POSIX crontab, các tùy chọn cron chính trong các bản phân phối Linux phổ biến và cấu trúc của một số trong số chúng.

Bạn có đang sử dụng Linux và đang chạy các tác vụ cron không? Bạn có quan tâm đến kiến ​​trúc ứng dụng hệ thống trong Unix không? Sau đó chúng ta lên đường!

nội dung

Nguồn gốc của loài

Việc thực hiện định kỳ các chương trình người dùng hoặc hệ thống là một điều cần thiết hiển nhiên trong tất cả các hệ điều hành. Do đó, các lập trình viên đã nhận ra nhu cầu về các dịch vụ cho phép họ lập kế hoạch và thực hiện các nhiệm vụ một cách tập trung từ lâu.

Các hệ điều hành giống Unix có nguồn gốc từ Phiên bản 7 Unix, được phát triển vào những năm 70 của thế kỷ trước tại Bell Labs, trong đó có Ken Thompson nổi tiếng. Phiên bản 7 Unix cũng bao gồm cron, một dịch vụ để chạy các tác vụ siêu người dùng thường xuyên.

Một cron hiện đại điển hình là một chương trình đơn giản, nhưng thuật toán vận hành của phiên bản gốc thậm chí còn đơn giản hơn: dịch vụ thức dậy mỗi phút một lần, đọc bảng với các tác vụ từ một tệp duy nhất (/etc/lib/crontab) và thực hiện cho superuser những tác vụ đáng lẽ phải được thực hiện tại thời điểm hiện tại.

Sau đó, các phiên bản cải tiến của dịch vụ đơn giản và hữu ích này đã được cung cấp cùng với tất cả các hệ điều hành giống Unix.

Các mô tả tổng quát về định dạng crontab và các nguyên tắc hoạt động cơ bản của tiện ích đã được đưa vào tiêu chuẩn chính của các hệ điều hành giống Unix - POSIX - vào năm 1992, và do đó cron từ một tiêu chuẩn thực tế đã trở thành một tiêu chuẩn de jure.

Năm 1987, Paul Vixie, sau khi khảo sát người dùng Unix về mong muốn của họ đối với cron, đã phát hành một phiên bản daemon khác giúp khắc phục một số vấn đề của cron truyền thống và mở rộng cú pháp của các tệp bảng.

Đến phiên bản thứ ba của Vixie cron bắt đầu đáp ứng các yêu cầu POSIX, ngoài ra, chương trình đã có giấy phép tự do, hay đúng hơn là không có giấy phép nào cả, ngoại trừ mong muốn trong README: tác giả không đưa ra đảm bảo, tên tác giả không thể xóa và chương trình chỉ có thể được bán cùng với mã nguồn. Những yêu cầu này hóa ra lại tương thích với các nguyên tắc của phần mềm miễn phí đang trở nên phổ biến trong những năm đó, vì vậy một số bản phân phối Linux quan trọng xuất hiện vào đầu những năm 90 đã lấy Vixie cron làm hệ thống của chúng và vẫn đang phát triển nó cho đến ngày nay.

Đặc biệt, Red Hat và SUSE phát triển một nhánh của Vixie cron - cronie, còn Debian và Ubuntu sử dụng phiên bản gốc của Vixie cron với nhiều bản vá.

Trước tiên chúng ta hãy làm quen với tiện ích người dùng crontab được mô tả trong POSIX, sau đó chúng ta sẽ xem xét các phần mở rộng cú pháp được cung cấp trong Vixie cron và việc sử dụng các biến thể của Vixie cron trong các bản phân phối Linux phổ biến. Và cuối cùng, điều quan trọng nhất là việc phân tích thiết bị cron daemon.

crontab POSIX

Nếu cron ban đầu luôn làm việc cho siêu người dùng thì các bộ lập lịch hiện đại thường xử lý các tác vụ của người dùng thông thường, an toàn và thuận tiện hơn.

Crons được cung cấp dưới dạng một bộ gồm hai chương trình: cron daemon chạy liên tục và tiện ích crontab có sẵn cho người dùng. Cái sau cho phép bạn chỉnh sửa các bảng nhiệm vụ cụ thể cho từng người dùng trong hệ thống, trong khi daemon khởi chạy các tác vụ từ bảng người dùng và bảng hệ thống.

В Tiêu chuẩn POSIX hành vi của daemon không được mô tả dưới bất kỳ hình thức nào và chỉ có chương trình người dùng được chính thức hóa crontab. Tất nhiên, sự tồn tại của các cơ chế khởi chạy tác vụ của người dùng là ngụ ý nhưng không được mô tả chi tiết.

Bằng cách gọi tiện ích crontab, bạn có thể thực hiện bốn việc: chỉnh sửa bảng nhiệm vụ của người dùng trong trình chỉnh sửa, tải bảng từ một tệp, hiển thị bảng nhiệm vụ hiện tại và xóa bảng nhiệm vụ. Ví dụ về cách hoạt động của tiện ích crontab:

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

Khi được gọi crontab -e trình soạn thảo được chỉ định trong biến môi trường tiêu chuẩn sẽ được sử dụng EDITOR.

Bản thân các nhiệm vụ được mô tả theo định dạng sau:

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

Năm trường đầu tiên của bản ghi: phút [1..60], giờ [0..23], ngày trong tháng [1..31], tháng [1..12], ngày trong tuần [0. .6], trong đó 0 là Chủ nhật. Trường cuối cùng, thứ sáu, là một dòng sẽ được thực thi bởi trình thông dịch lệnh tiêu chuẩn.

Trong năm trường đầu tiên, các giá trị có thể được liệt kê cách nhau bằng dấu phẩy:

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

Hoặc với dấu gạch nối:

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

Quyền truy cập của người dùng vào lập lịch tác vụ được quy định trong POSIX bởi các tệp cron.allow và cron.deny, tương ứng liệt kê những người dùng có quyền truy cập vào crontab và những người dùng không có quyền truy cập vào chương trình. Tiêu chuẩn không quy định vị trí của các tệp này theo bất kỳ cách nào.

Theo tiêu chuẩn, ít nhất bốn biến môi trường phải được chuyển cho các chương trình đã khởi chạy:

  1. HOME - thư mục chính của người dùng.
  2. LOGNAME - đăng nhập của người dùng.
  3. PATH là đường dẫn nơi bạn có thể tìm thấy các tiện ích hệ thống tiêu chuẩn.
  4. SHELL - đường dẫn đến trình thông dịch lệnh được sử dụng.

Đáng chú ý, POSIX không nói gì về việc giá trị của các biến này đến từ đâu.

Bán chạy nhất - Vixie cron 3.0pl1

Tổ tiên chung của các biến thể cron phổ biến là Vixie cron 3.0pl1, được giới thiệu trong danh sách gửi thư comp.sources.unix năm 1992. Chúng tôi sẽ xem xét các tính năng chính của phiên bản này chi tiết hơn.

Vixie cron có hai chương trình (cron và crontab). Như thường lệ, daemon chịu trách nhiệm đọc và chạy các tác vụ từ bảng tác vụ hệ thống và bảng tác vụ người dùng cá nhân, còn tiện ích crontab chịu trách nhiệm chỉnh sửa bảng người dùng.

Bảng tác vụ và tập tin cấu hình

Bảng tác vụ siêu người dùng nằm trong /etc/crontab. Cú pháp của bảng hệ thống tương ứng với cú pháp của Vixie cron, ngoại trừ cột thứ sáu trong đó cho biết tên của người dùng thay mặt họ thực hiện tác vụ:

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

Các bảng tác vụ người dùng thông thường được đặt trong /var/cron/tabs/username và sử dụng cú pháp tương tự. Khi bạn chạy tiện ích crontab với tư cách người dùng, đây là những tệp được chỉnh sửa.

Danh sách người dùng có quyền truy cập vào crontab được quản lý trong các tệp /var/cron/allow và /var/cron/deny, nơi bạn chỉ cần nhập tên người dùng vào một dòng riêng.

Cú pháp mở rộng

So với crontab POSIX, giải pháp của Paul Vixey chứa một số sửa đổi rất hữu ích đối với cú pháp của các bảng nhiệm vụ của tiện ích.

Cú pháp bảng mới đã có sẵn: ví dụ: bạn có thể chỉ định các ngày trong tuần hoặc tháng theo tên (Thứ Hai, Thứ Ba, v.v.):

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

Bạn có thể chỉ định bước thực hiện nhiệm vụ:

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

Các bước và khoảng thời gian có thể được trộn lẫn:

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

Các lựa chọn thay thế trực quan cho cú pháp thông thường được hỗ trợ (khởi động lại, hàng năm, hàng năm, hàng tháng, hàng tuần, hàng ngày, nửa đêm, hàng giờ):

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

Môi trường thực hiện nhiệm vụ

Vixie cron cho phép bạn thay đổi môi trường của các ứng dụng đang chạy.

Các biến môi trường USER, LOGNAME và HOME không chỉ được cung cấp bởi daemon mà được lấy từ một tệp passwd. Biến PATH được đặt thành "/usr/bin:/bin" và biến SHELL được đặt thành "/bin/sh". Giá trị của tất cả các biến ngoại trừ LOGNAME có thể được thay đổi trong bảng người dùng.

Một số biến môi trường (đáng chú ý nhất là SHELL và HOME) được chính cron sử dụng để chạy tác vụ. Đây là cách sử dụng bash thay vì sh tiêu chuẩn để chạy các tác vụ tùy chỉnh có thể trông như sau:

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

Cuối cùng, tất cả các biến môi trường được xác định trong bảng (được sử dụng bởi cron hoặc được quy trình cần) sẽ được chuyển đến tác vụ đang chạy.

Để chỉnh sửa tệp, crontab sử dụng trình chỉnh sửa được chỉ định trong biến môi trường VISUAL hoặc EDITOR. Nếu môi trường nơi crontab được chạy không xác định các biến này thì "/usr/ucb/vi" sẽ được sử dụng (ucb có thể là Đại học California, Berkeley).

cron trên Debian và Ubuntu

Các nhà phát triển Debian và các bản phân phối phái sinh đã phát hành phiên bản sửa đổi cao Vixie cron phiên bản 3.0pl1. Không có sự khác biệt trong cú pháp của các tệp bảng; đối với người dùng, nó giống như cron Vixie. Tính năng mới lớn nhất: Hỗ trợ syslog, SELinux и PAM.

Những thay đổi ít được chú ý hơn nhưng rõ ràng bao gồm vị trí của tệp cấu hình và bảng tác vụ.

Bảng người dùng trong Debian nằm trong thư mục /var/spool/cron/crontabs, bảng hệ thống vẫn ở đó - trong /etc/crontab. Các bảng tác vụ dành riêng cho gói Debian được đặt trong /etc/cron.d, từ đó trình nền cron tự động đọc chúng. Kiểm soát quyền truy cập của người dùng được kiểm soát bởi các tệp /etc/cron.allow và /etc/cron.deny.

Shell mặc định vẫn là /bin/sh, trong Debian là shell nhỏ tương thích POSIX dấu gạch ngang, được khởi chạy mà không đọc bất kỳ cấu hình nào (ở chế độ không tương tác).

Bản thân Cron trong các phiên bản Debian mới nhất được khởi chạy thông qua systemd và cấu hình khởi chạy có thể được xem trong /lib/systemd/system/cron.service. Không có gì đặc biệt trong cấu hình dịch vụ; mọi thao tác quản lý tác vụ tinh tế hơn đều có thể được thực hiện thông qua các biến môi trường được khai báo trực tiếp trong crontab của mỗi người dùng.

cronie trên RedHat, Fedora và CentOS

bạn thân — nhánh của Vixie cron phiên bản 4.1. Giống như trong Debian, cú pháp không thay đổi, nhưng hỗ trợ PAM và SELinux, làm việc trong cụm, theo dõi tệp bằng inotify và các tính năng khác đã được thêm vào.

Cấu hình mặc định nằm ở những vị trí thông thường: bảng hệ thống nằm trong /etc/crontab, các gói đặt bảng của chúng trong /etc/cron.d, bảng người dùng nằm trong /var/spool/cron/crontabs.

Trình nền chạy dưới sự kiểm soát của systemd, cấu hình dịch vụ là /lib/systemd/system/crond.service.

Trên các bản phân phối giống Red Hat, /bin/sh được sử dụng theo mặc định khi khởi động, đây là bash tiêu chuẩn. Cần lưu ý rằng khi chạy các công việc định kỳ thông qua /bin/sh, bash shell khởi động ở chế độ tuân thủ POSIX và không đọc bất kỳ cấu hình bổ sung nào, chạy ở chế độ không tương tác.

cronie trong SLES và openSUSE

Bản phân phối SLES của Đức và openSUSE phái sinh của nó sử dụng cùng một phương thức. Daemon ở đây cũng được khởi chạy dưới systemd, cấu hình dịch vụ nằm trong /usr/lib/systemd/system/cron.service. Cấu hình: /etc/crontab, /etc/cron.d, /var/spool/cron/tabs. /bin/sh là cùng một bash chạy ở chế độ không tương tác tuân thủ POSIX.

Thiết bị cron Vixie

Hậu duệ hiện đại của cron không thay đổi hoàn toàn so với Vixie cron, nhưng vẫn có được các tính năng mới không bắt buộc phải hiểu nguyên tắc của chương trình. Nhiều tiện ích mở rộng trong số này được thiết kế kém và gây nhầm lẫn cho mã. Mã nguồn cron gốc của Paul Vixey rất thú vị khi đọc.

Do đó, tôi quyết định phân tích thiết bị cron bằng ví dụ về chương trình cron chung cho cả hai nhánh phát triển - Vixie cron 3.0pl1. Tôi sẽ đơn giản hóa các ví dụ bằng cách loại bỏ các ifdef gây khó khăn cho việc đọc và bỏ qua các chi tiết nhỏ.

Công việc của con quỷ có thể được chia thành nhiều giai đoạn:

  1. Khởi tạo chương trình.
  2. Thu thập và cập nhật danh sách các nhiệm vụ sẽ chạy.
  3. Vòng lặp cron chính đang chạy.
  4. Bắt đầu một nhiệm vụ.

Chúng ta hãy nhìn vào chúng theo thứ tự.

Khởi tạo

Khi khởi động, sau khi kiểm tra các đối số của quy trình, cron sẽ cài đặt bộ xử lý tín hiệu SIGCHLD và SIGHUP. Cái đầu tiên tạo một mục nhật ký về việc chấm dứt tiến trình con, cái thứ hai đóng bộ mô tả tệp của tệp nhật ký:

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

Trình nền cron luôn chạy một mình trên hệ thống, chỉ với tư cách là siêu người dùng và từ thư mục cron chính. Các lệnh gọi sau đây tạo một tệp khóa với PID của quy trình daemon, đảm bảo rằng người dùng đúng và thay đổi thư mục hiện tại thành thư mục chính:

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

Đường dẫn mặc định được đặt, đường dẫn này sẽ được sử dụng khi bắt đầu quá trình:

setenv("PATH", _PATH_DEFPATH, 1);

Sau đó, quy trình được "daemon hóa": nó tạo một bản sao con của quy trình bằng cách gọi fork và một phiên mới trong quy trình con (gọi setsid). Quá trình gốc không còn cần thiết nữa và nó thoát ra:

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

Việc chấm dứt tiến trình gốc sẽ giải phóng khóa trên tệp khóa. Ngoài ra, cần phải cập nhật PID trong tệp cho trẻ. Sau đó, cơ sở dữ liệu tác vụ được điền vào:

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

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

Sau đó cron chuyển sang chu trình làm việc chính. Nhưng trước đó, bạn nên xem qua việc tải danh sách nhiệm vụ.

Thu thập và cập nhật danh sách nhiệm vụ

Hàm Load_database chịu trách nhiệm tải danh sách các tác vụ. Nó kiểm tra crontab hệ thống chính và thư mục chứa các tập tin người dùng. Nếu các tập tin và thư mục không thay đổi, danh sách tác vụ sẽ không được đọc lại. Nếu không, một danh sách nhiệm vụ mới sẽ bắt đầu hình thành.

Đang tải tệp hệ thống với tên tệp và bảng đặc biệt:

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

Đang tải bảng người dùng trong một vòng lặp:

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

Sau đó cơ sở dữ liệu cũ được thay thế bằng cơ sở dữ liệu mới.

Trong các ví dụ trên, lệnh gọi hàm process_crontab xác minh rằng người dùng khớp với tên tệp bảng có tồn tại (trừ khi đó là siêu người dùng) và sau đó gọi Load_user. Cái sau đã đọc từng dòng một của tệp:

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

Ở đây, biến môi trường được đặt (các dòng có dạng VAR=value) bằng cách sử dụng hàm Load_env / env_set hoặc mô tả tác vụ được đọc (* * * * * /path/to/exec) bằng hàm Load_entry.

Thực thể mục nhập mà Load_entry trả về là nhiệm vụ của chúng tôi, được đặt trong danh sách nhiệm vụ chung. Bản thân hàm này thực hiện phân tích cú pháp chi tiết về định dạng thời gian, nhưng chúng tôi quan tâm nhiều hơn đến việc hình thành các biến môi trường và tham số khởi chạy tác vụ:

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

Vòng lặp chính hoạt động với danh sách nhiệm vụ hiện tại.

Vòng lặp chính

Cron gốc từ Unix phiên bản 7 hoạt động khá đơn giản: nó đọc lại cấu hình trong một vòng lặp, khởi chạy các tác vụ của phút hiện tại với tư cách là siêu người dùng và ngủ cho đến đầu phút tiếp theo. Cách tiếp cận đơn giản này trên các máy cũ đòi hỏi quá nhiều tài nguyên.

Một phiên bản thay thế đã được đề xuất trong SysV, trong đó daemon sẽ chuyển sang chế độ ngủ cho đến phút gần nhất mà nhiệm vụ được xác định hoặc trong 30 phút. Tiêu tốn ít tài nguyên hơn để đọc lại cấu hình và kiểm tra tác vụ ở chế độ này, nhưng việc cập nhật nhanh chóng danh sách tác vụ trở nên bất tiện.

Vixie cron quay lại kiểm tra danh sách nhiệm vụ mỗi phút một lần, may mắn thay vào cuối những năm 80, có nhiều tài nguyên hơn đáng kể trên các máy Unix tiêu chuẩn:

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

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

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

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

Hàm cron_sleep tham gia trực tiếp vào việc thực thi các tác vụ, gọi hàm job_runqueue (liệt kê và chạy các tác vụ) và do_command (chạy từng tác vụ riêng lẻ). Chức năng cuối cùng đáng để xem xét chi tiết hơn.

Chạy một nhiệm vụ

Hàm do_command được thực thi theo kiểu Unix tốt, nghĩa là nó thực hiện một nhánh để thực hiện tác vụ một cách không đồng bộ. Tiến trình cha tiếp tục khởi chạy các tác vụ, tiến trình con chuẩn bị tiến trình tác vụ:

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

Có khá nhiều logic trong child_process: nó nhận các luồng đầu ra tiêu chuẩn và lỗi vào chính nó để gửi nó tới thư (nếu biến môi trường MAILTO được chỉ định trong bảng tác vụ), và cuối cùng, đợi main quá trình thực hiện nhiệm vụ.

Quá trình nhiệm vụ được hình thành bởi một ngã ba khác:

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

Về cơ bản đó là tất cả cron. Tôi đã bỏ qua một số chi tiết thú vị, chẳng hạn như tính toán người dùng từ xa, nhưng tôi đã tóm tắt điều chính.

bạt

Cron là một chương trình đơn giản và hữu ích một cách đáng ngạc nhiên, được tạo ra theo truyền thống tốt nhất của thế giới Unix. Cô ấy không làm gì thêm, nhưng cô ấy đã làm công việc của mình một cách tuyệt vời trong nhiều thập kỷ nay. Việc tìm hiểu mã cho phiên bản đi kèm với Ubuntu mất không quá một giờ và tôi đã có rất nhiều niềm vui! Tôi hy vọng tôi có thể chia sẻ nó với bạn.

Không biết bạn thế nào, nhưng tôi hơi buồn khi nhận ra rằng lập trình hiện đại, với xu hướng quá phức tạp và quá trừu tượng, đã lâu không còn có lợi cho sự đơn giản như vậy.

Có nhiều lựa chọn thay thế hiện đại cho cron: systemd-timers cho phép bạn tổ chức các hệ thống phức tạp với các phần phụ thuộc, fcron cho phép bạn điều chỉnh mức tiêu thụ tài nguyên theo tác vụ một cách linh hoạt hơn. Nhưng cá nhân tôi, crontab đơn giản nhất luôn là đủ đối với tôi.

Tóm lại, hãy yêu thích Unix, sử dụng các chương trình đơn giản và đừng quên đọc mana cho nền tảng của bạn!

Nguồn: www.habr.com

Thêm một lời nhận xét