Cron ใน Linux: ประวัติ การใช้งาน และอุปกรณ์

Cron ใน Linux: ประวัติ การใช้งาน และอุปกรณ์

คลาสสิกเขียนว่าชั่วโมงแห่งความสุขอย่าดู ในช่วงเวลาที่ยากลำบากนั้นไม่มีทั้งโปรแกรมเมอร์และ Unix แต่ทุกวันนี้โปรแกรมเมอร์รู้แน่ว่า cron จะคอยติดตามเวลาแทน

ยูทิลิตี้บรรทัดคำสั่งมีทั้งจุดอ่อนและงานที่น่าเบื่อสำหรับฉัน sed, awk, wc, cut และโปรแกรมเก่าอื่นๆ รันโดยสคริปต์บนเซิร์ฟเวอร์ของเราทุกวัน ส่วนมากได้รับการออกแบบให้เป็นงานสำหรับ cron ซึ่งเป็นตัวกำหนดตารางเวลาที่มีมาตั้งแต่ปี 70

เป็นเวลานานที่ฉันใช้ cron แบบเผินๆ โดยไม่ได้ลงรายละเอียด แต่วันหนึ่งเมื่อฉันพบข้อผิดพลาดขณะเรียกใช้สคริปต์ ฉันจึงตัดสินใจตรวจสอบอย่างละเอียด นี่คือลักษณะที่ปรากฏของบทความนี้ ในขณะที่เขียน ฉันเริ่มคุ้นเคยกับ POSIX crontab ซึ่งเป็นตัวเลือก cron หลักในลีนุกซ์รุ่นยอดนิยมและโครงสร้างของบางส่วน

คุณใช้ Linux และใช้งาน cron หรือไม่? คุณสนใจสถาปัตยกรรมแอปพลิเคชันระบบใน Unix หรือไม่? ถ้าอย่างนั้นเราก็ไป!

Содержание

ต้นกำเนิดของสายพันธุ์

การดำเนินการของผู้ใช้หรือโปรแกรมระบบเป็นระยะมีความจำเป็นอย่างเห็นได้ชัดในระบบปฏิบัติการทั้งหมด ดังนั้นโปรแกรมเมอร์จึงตระหนักถึงความต้องการบริการที่ช่วยให้พวกเขาสามารถวางแผนและดำเนินงานจากส่วนกลางได้เมื่อนานมาแล้ว

ระบบปฏิบัติการที่มีลักษณะคล้าย Unix มีต้นกำเนิดย้อนกลับไปถึง Unix เวอร์ชัน 7 ซึ่งพัฒนาขึ้นในช่วงทศวรรษที่ 70 ของศตวรรษที่ผ่านมาที่ Bell Labs รวมถึงโดย Ken Thompson ผู้โด่งดัง เวอร์ชัน 7 Unix ยังรวม cron ซึ่งเป็นบริการสำหรับการรันงาน superuser เป็นประจำ

cron สมัยใหม่ทั่วไปเป็นโปรแกรมง่ายๆ แต่อัลกอริธึมการทำงานของเวอร์ชันดั้งเดิมนั้นง่ายกว่า: บริการจะเริ่มทำงานนาทีละครั้ง อ่านตารางที่มีงานจากไฟล์เดียว (/etc/lib/crontab) และดำเนินการสำหรับ superuser งานที่ควรจะดำเนินการในปัจจุบัน

ต่อจากนั้น เวอร์ชันปรับปรุงของบริการที่เรียบง่ายและมีประโยชน์ก็มาพร้อมกับระบบปฏิบัติการที่คล้ายกับ Unix ทั้งหมด

คำอธิบายทั่วไปของรูปแบบ crontab และหลักการพื้นฐานของการทำงานของยูทิลิตี้นั้นรวมอยู่ในมาตรฐานหลักของระบบปฏิบัติการที่คล้าย Unix - POSIX - ในปี 1992 และด้วยเหตุนี้ cron จากมาตรฐานพฤตินัยจึงกลายเป็นมาตรฐานทางนิตินัย

ในปี 1987 Paul Vixie ได้สำรวจผู้ใช้ Unix เกี่ยวกับความปรารถนาของพวกเขาสำหรับ cron และได้เปิดตัว daemon อีกเวอร์ชันหนึ่งที่แก้ไขปัญหาบางอย่างของ cron แบบเดิมและขยายไวยากรณ์ของไฟล์ตาราง

โดย Vixie cron รุ่นที่สามเริ่มเป็นไปตามข้อกำหนด POSIX นอกจากนี้โปรแกรมมีใบอนุญาตแบบเสรีนิยมหรือค่อนข้างไม่มีใบอนุญาตเลย ยกเว้นความปรารถนาใน README: ผู้เขียนไม่ได้ให้การรับประกัน ชื่อของผู้เขียน ไม่สามารถลบได้และโปรแกรมสามารถขายได้พร้อมกับซอร์สโค้ดเท่านั้น ข้อกำหนดเหล่านี้เข้ากันได้กับหลักการของซอฟต์แวร์เสรีที่ได้รับความนิยมในช่วงหลายปีที่ผ่านมา ดังนั้นลีนุกซ์รุ่นหลักบางรุ่นที่ปรากฏในช่วงต้นทศวรรษที่ 90 จึงใช้ Vixie cron เป็นระบบหนึ่งและยังคงพัฒนาอยู่ในปัจจุบัน

โดยเฉพาะอย่างยิ่ง Red Hat และ SUSE พัฒนาทางแยกของ Vixie cron - cronie และ Debian และ Ubuntu ใช้ Vixie cron รุ่นดั้งเดิมพร้อมแพตช์มากมาย

ก่อนอื่นมาทำความรู้จักกับยูทิลิตี้ผู้ใช้ crontab ที่อธิบายไว้ใน POSIX หลังจากนั้นเราจะดูส่วนขยายไวยากรณ์ที่มีให้ใน Vixie cron และการใช้รูปแบบต่างๆ ของ Vixie cron ในลีนุกซ์รุ่นยอดนิยม และสุดท้าย เชอร์รี่บนเค้กคือการวิเคราะห์อุปกรณ์ครอนดีมอน

POSIX crontab

หาก cron ดั้งเดิมใช้งานได้กับ superuser เสมอ ตัวกำหนดเวลาสมัยใหม่มักจะจัดการกับงานของผู้ใช้ทั่วไปซึ่งมีความปลอดภัยและสะดวกกว่า

Crons จัดทำเป็นชุดของสองโปรแกรม: cron daemon ที่ทำงานอย่างต่อเนื่องและยูทิลิตี้ crontab ที่ผู้ใช้สามารถใช้งานได้ ส่วนหลังช่วยให้คุณสามารถแก้ไขตารางงานเฉพาะสำหรับผู้ใช้แต่ละรายในระบบได้ ในขณะที่ daemon จะเรียกใช้งานจากตารางผู้ใช้และตารางระบบ

В มาตรฐาน POSIX พฤติกรรมของ daemon ไม่ได้อธิบายไว้ แต่อย่างใด และมีเพียงโปรแกรมผู้ใช้เท่านั้นที่เป็นทางการ crontab. แน่นอนว่าการมีอยู่ของกลไกในการเปิดตัวงานของผู้ใช้นั้นเป็นการบอกเป็นนัย แต่ไม่ได้อธิบายโดยละเอียด

ด้วยการเรียกยูทิลิตี้ crontab คุณสามารถทำสี่สิ่งได้: แก้ไขตารางงานของผู้ใช้ในตัวแก้ไข โหลดตารางจากไฟล์ แสดงตารางงานปัจจุบัน และล้างตารางงาน ตัวอย่างการทำงานของยูทิลิตี้ crontab:

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

เมื่อถูกเรียก crontab -e ตัวแก้ไขที่ระบุในตัวแปรสภาพแวดล้อมมาตรฐานจะถูกนำมาใช้ EDITOR.

งานต่างๆ อธิบายไว้ในรูปแบบต่อไปนี้:

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

ห้าฟิลด์แรกของบันทึก: นาที [1..60], ชั่วโมง [0..23], วันของเดือน [1..31], เดือน [1..12], วันในสัปดาห์ [0. .6] โดยที่ 0 คือวันอาทิตย์ ฟิลด์สุดท้ายที่หกคือบรรทัดที่จะถูกดำเนินการโดยล่ามคำสั่งมาตรฐาน

ในห้าฟิลด์แรก สามารถแสดงรายการค่าโดยคั่นด้วยเครื่องหมายจุลภาค:

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

หรือมียัติภังค์:

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

การเข้าถึงการกำหนดเวลางานของผู้ใช้ได้รับการควบคุมใน POSIX โดยไฟล์ cron.allow และ cron.deny ซึ่งแสดงรายการผู้ใช้ที่มีสิทธิ์เข้าถึง crontab และผู้ใช้ที่ไม่สามารถเข้าถึงโปรแกรมตามลำดับ มาตรฐานไม่ได้ควบคุมตำแหน่งของไฟล์เหล่านี้ แต่อย่างใด

ตามมาตรฐาน จะต้องส่งตัวแปรสภาพแวดล้อมอย่างน้อยสี่ตัวไปยังโปรแกรมที่เรียกใช้งาน:

  1. HOME - โฮมไดเร็กตอรี่ของผู้ใช้
  2. LOGNAME — เข้าสู่ระบบของผู้ใช้
  3. PATH คือเส้นทางที่คุณสามารถค้นหายูทิลิตี้ระบบมาตรฐานได้
  4. SHELL - เส้นทางไปยังล่ามคำสั่งที่ใช้

โดยเฉพาะอย่างยิ่ง POSIX ไม่ได้พูดอะไรเกี่ยวกับที่มาของค่าสำหรับตัวแปรเหล่านี้

สินค้าขายดี - Vixie cron 3.0pl1

บรรพบุรุษร่วมกันของตัวแปร cron ยอดนิยมคือ Vixie cron 3.0pl1 ซึ่งเปิดตัวในรายชื่อผู้รับจดหมาย comp.sources.unix ในปี 1992 เราจะพิจารณาคุณสมบัติหลักของเวอร์ชันนี้โดยละเอียดยิ่งขึ้น

Vixie cron มาในสองโปรแกรม (cron และ crontab) ตามปกติ daemon มีหน้าที่อ่านและรันงานจากตารางงานระบบและตารางงานของผู้ใช้แต่ละราย และยูทิลิตี้ crontab มีหน้าที่แก้ไขตารางผู้ใช้

ตารางงานและไฟล์การกำหนดค่า

ตารางงาน superuser อยู่ใน /etc/crontab ไวยากรณ์ของตารางระบบสอดคล้องกับไวยากรณ์ของ Vixie cron โดยมีข้อยกเว้นว่าคอลัมน์ที่หกในนั้นระบุชื่อของผู้ใช้ภายใต้ชื่อที่เปิดตัวงาน:

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

ตารางงานของผู้ใช้ทั่วไปอยู่ใน /var/cron/tabs/username และใช้ไวยากรณ์เดียวกัน เมื่อคุณเรียกใช้ยูทิลิตี crontab ในฐานะผู้ใช้ ไฟล์เหล่านี้คือไฟล์ที่ได้รับการแก้ไข

รายชื่อผู้ใช้ที่สามารถเข้าถึง crontab จะได้รับการจัดการในไฟล์ /var/cron/allow และ /var/cron/deny ซึ่งคุณเพียงแค่ต้องป้อนชื่อผู้ใช้ในบรรทัดแยกต่างหาก

ไวยากรณ์เพิ่มเติม

เมื่อเปรียบเทียบกับ POSIX crontab โซลูชันของ Paul Vixey มีการปรับเปลี่ยนไวยากรณ์ของตารางงานของยูทิลิตี้ที่มีประโยชน์มากหลายประการ

ไวยากรณ์ตารางใหม่พร้อมใช้งานแล้ว ตัวอย่างเช่น คุณสามารถระบุวันในสัปดาห์หรือเดือนตามชื่อ (จันทร์ อังคาร และอื่นๆ):

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

คุณสามารถระบุขั้นตอนที่จะเริ่มงานได้:

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

สามารถผสมขั้นตอนและช่วงเวลาได้:

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

รองรับทางเลือกที่ใช้งานง่ายสำหรับไวยากรณ์ปกติ (รีบูต, รายปี, รายปี, รายเดือน, รายสัปดาห์, รายวัน, เที่ยงคืน, ชั่วโมง):

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

สภาพแวดล้อมการดำเนินงาน

Vixie cron ช่วยให้คุณสามารถเปลี่ยนสภาพแวดล้อมของแอปพลิเคชันที่ทำงานอยู่ได้

ตัวแปรสภาพแวดล้อม USER, LOGNAME และ HOME ไม่ได้จัดทำโดย daemon แต่นำมาจากไฟล์ passwd. ตัวแปร PATH ถูกตั้งค่าเป็น "/usr/bin:/bin" และตัวแปร SHELL ถูกตั้งค่าเป็น "/bin/sh" ค่าของตัวแปรทั้งหมดยกเว้น LOGNAME สามารถเปลี่ยนแปลงได้ในตารางผู้ใช้

ตัวแปรสภาพแวดล้อมบางตัว (ที่โดดเด่นที่สุดคือ SHELL และ HOME) ถูกใช้โดย cron เองเพื่อรันงานนี้ นี่คือสิ่งที่การใช้ bash แทน sh มาตรฐานเพื่อรันงานที่กำหนดเองอาจมีลักษณะดังนี้:

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

ในที่สุด ตัวแปรสภาพแวดล้อมทั้งหมดที่กำหนดไว้ในตาราง (ใช้โดย cron หรือจำเป็นโดยกระบวนการ) จะถูกส่งผ่านไปยังงานที่กำลังทำงานอยู่

หากต้องการแก้ไขไฟล์ crontab ใช้เครื่องมือแก้ไขที่ระบุในตัวแปรสภาพแวดล้อม VISUAL หรือ EDITOR หากสภาพแวดล้อมที่รัน crontab ไม่ได้กำหนดตัวแปรเหล่านี้ไว้ ระบบจะใช้ "/usr/ucb/vi" (ucb อาจเป็น University of California, Berkeley)

cron บน Debian และ Ubuntu

นักพัฒนาซอฟต์แวร์ Debian และอนุพันธ์ได้เปิดตัวแล้ว เวอร์ชันที่มีการปรับเปลี่ยนอย่างมาก Vixie cron เวอร์ชัน 3.0pl1. ไวยากรณ์ของไฟล์ตารางไม่มีความแตกต่าง สำหรับผู้ใช้ Vixie cron เดียวกัน คุณสมบัติใหม่ที่ใหญ่ที่สุด: การสนับสนุน syslog, SELinux и PAM.

การเปลี่ยนแปลงที่สังเกตเห็นได้น้อยลง แต่จับต้องได้รวมถึงตำแหน่งของไฟล์การกำหนดค่าและตารางงาน

ตารางผู้ใช้ใน Debian อยู่ในไดเร็กทอรี /var/spool/cron/crontabs ตารางระบบยังคงอยู่ - ใน /etc/crontab ตารางงานเฉพาะแพ็คเกจ Debian จะถูกวางไว้ใน /etc/cron.d ซึ่ง cron daemon จะอ่านตารางเหล่านั้นโดยอัตโนมัติ การควบคุมการเข้าถึงของผู้ใช้จะถูกควบคุมโดยไฟล์ /etc/cron.allow และ /etc/cron.deny

เชลล์เริ่มต้นยังคงเป็น /bin/sh ซึ่งใน Debian เป็นเชลล์ที่สอดคล้องกับ POSIX ขนาดเล็ก สาดเปิดตัวโดยไม่ต้องอ่านการกำหนดค่าใดๆ (ในโหมดไม่โต้ตอบ)

Cron ใน Debian เวอร์ชันล่าสุดเปิดตัวผ่าน systemd และการกำหนดค่าการเปิดตัวสามารถดูได้ใน /lib/systemd/system/cron.service การกำหนดค่าบริการไม่มีอะไรพิเศษ การจัดการงานที่ละเอียดกว่านี้สามารถทำได้ผ่านตัวแปรสภาพแวดล้อมที่ประกาศโดยตรงใน crontab ของผู้ใช้แต่ละคน

cronie บน RedHat, Fedora และ CentOS

cronie - ทางแยกของ Vixie cron เวอร์ชัน 4.1 เช่นเดียวกับใน Debian ไวยากรณ์ไม่เปลี่ยนแปลง แต่รองรับ PAM และ SELinux การทำงานในคลัสเตอร์ การติดตามไฟล์โดยใช้ inotify และเพิ่มคุณสมบัติอื่น ๆ

การกำหนดค่าเริ่มต้นอยู่ในตำแหน่งปกติ: ตารางระบบอยู่ใน /etc/crontab, แพ็คเกจใส่ตารางใน /etc/cron.d, ตารางผู้ใช้อยู่ใน /var/spool/cron/crontabs

daemon ทำงานภายใต้การควบคุมของ systemd การกำหนดค่าบริการคือ /lib/systemd/system/crond.service

ในการแจกแจงแบบ Red Hat นั้น /bin/sh จะถูกใช้เป็นค่าเริ่มต้นเมื่อเริ่มต้นระบบ ซึ่งเป็น bash มาตรฐาน ควรสังเกตว่าเมื่อรันงาน cron ผ่าน /bin/sh bash shell จะเริ่มทำงานในโหมดที่สอดคล้องกับ POSIX และไม่อ่านการกำหนดค่าเพิ่มเติมใด ๆ ซึ่งทำงานในโหมดที่ไม่โต้ตอบ

cronie ใน SLES และ openSUSE

SLES การจัดจำหน่ายของเยอรมันและ openSUSE ที่เป็นอนุพันธ์ใช้ cronie เดียวกัน daemon ที่นี่ยังเปิดตัวภายใต้ systemd การกำหนดค่าบริการอยู่ใน /usr/lib/systemd/system/cron.service การกำหนดค่า: /etc/crontab, /etc/cron.d, /var/spool/cron/tabs /bin/sh เป็น bash เดียวกันกับที่ทำงานในโหมดไม่โต้ตอบที่สอดคล้องกับ POSIX

อุปกรณ์ Vixie cron

ทายาทสมัยใหม่ของ cron ไม่ได้เปลี่ยนแปลงอย่างรุนแรงเมื่อเทียบกับ Vixie cron แต่ยังคงได้รับคุณสมบัติใหม่ที่ไม่จำเป็นต้องเข้าใจหลักการของโปรแกรม ส่วนขยายเหล่านี้จำนวนมากได้รับการออกแบบมาไม่ดีและทำให้โค้ดสับสน ซอร์สโค้ด cron ต้นฉบับโดย Paul Vixey รู้สึกยินดีที่ได้อ่าน

ดังนั้นฉันจึงตัดสินใจวิเคราะห์อุปกรณ์ cron โดยใช้ตัวอย่างของโปรแกรม cron ทั่วไปสำหรับการพัฒนาทั้งสองสาขา - Vixie cron 3.0pl1 ฉันจะทำให้ตัวอย่างง่ายขึ้นโดยการลบ ifdefs ที่ทำให้การอ่านซับซ้อนและละเว้นรายละเอียดปลีกย่อย

งานของปีศาจสามารถแบ่งออกเป็นหลายขั้นตอน:

  1. การเริ่มต้นโปรแกรม
  2. การรวบรวมและอัพเดตรายการงานที่จะรัน
  3. cron หลักทำงานอยู่
  4. เริ่มงาน

มาดูกันตามลำดับ

การเริ่มต้น

เมื่อเริ่มต้น หลังจากตรวจสอบอาร์กิวเมนต์ของกระบวนการแล้ว cron จะติดตั้งตัวจัดการสัญญาณ SIGCHLD และ SIGHUP อันแรกสร้างรายการบันทึกเกี่ยวกับการยุติกระบวนการลูก ส่วนอันที่สองปิดตัวอธิบายไฟล์ของไฟล์บันทึก:

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

cron daemon จะรันโดยลำพังบนระบบเสมอ เฉพาะในฐานะผู้ใช้ขั้นสูงและจากไดเร็กทอรี cron หลัก การเรียกต่อไปนี้จะสร้างไฟล์ล็อคด้วย PID ของกระบวนการ daemon ตรวจสอบให้แน่ใจว่าผู้ใช้ถูกต้อง และเปลี่ยนไดเร็กทอรีปัจจุบันเป็นไดเร็กทอรีหลัก:

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

มีการตั้งค่าเส้นทางเริ่มต้นซึ่งจะใช้เมื่อเริ่มต้นกระบวนการ:

setenv("PATH", _PATH_DEFPATH, 1);

จากนั้นกระบวนการจะถูก "daemonized" โดยจะสร้างสำเนาย่อยของกระบวนการโดยการเรียก fork และเซสชันใหม่ในกระบวนการย่อย (การเรียก setsid) ไม่จำเป็นต้องใช้กระบวนการหลักอีกต่อไป และจะออกจากระบบ:

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

การยุติกระบวนการพาเรนต์จะปลดล็อกไฟล์ล็อก นอกจากนี้ จำเป็นต้องอัปเดต PID ในไฟล์ไปยังบุตรหลานด้วย หลังจากนี้ฐานข้อมูลงานจะถูกกรอก:

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

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

จากนั้น cron จะเข้าสู่วงจรการทำงานหลัก แต่ก่อนหน้านั้นก็ควรดูการโหลดรายการงานก่อน

การรวบรวมและอัพเดตรายการงาน

ฟังก์ชัน load_database มีหน้าที่โหลดรายการงาน จะตรวจสอบ crontab ระบบหลักและไดเร็กทอรีด้วยไฟล์ผู้ใช้ หากไฟล์และไดเร็กทอรีไม่มีการเปลี่ยนแปลง รายการงานจะไม่ถูกอ่านซ้ำ มิฉะนั้น รายการงานใหม่จะเริ่มก่อตัวขึ้น

กำลังโหลดไฟล์ระบบด้วยชื่อไฟล์และตารางพิเศษ:

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

กำลังโหลดตารางผู้ใช้ในวง:

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

หลังจากนั้นฐานข้อมูลเก่าจะถูกแทนที่ด้วยฐานข้อมูลใหม่

ในตัวอย่างข้างต้น การเรียกใช้ฟังก์ชัน process_crontab จะตรวจสอบว่ามีผู้ใช้ที่ตรงกับชื่อไฟล์ตารางอยู่ (ยกเว้นว่าเป็น superuser) จากนั้นจึงเรียก load_user ส่วนหลังอ่านไฟล์ทีละบรรทัดแล้ว:

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

ในที่นี้ มีการตั้งค่าตัวแปรสภาพแวดล้อม (บรรทัดของรูปแบบ VAR=value) โดยใช้ฟังก์ชัน load_env / env_set หรืออ่านคำอธิบายงาน (* * * * * /path/to/exec) โดยใช้ฟังก์ชัน load_entry

เอนทิตีรายการที่ load_entry ส่งคืนคืองานของเรา ซึ่งอยู่ในรายการงานทั่วไป ฟังก์ชั่นนี้ดำเนินการแยกวิเคราะห์รูปแบบเวลาอย่างละเอียด แต่เราสนใจในการสร้างตัวแปรสภาพแวดล้อมและพารามิเตอร์การเริ่มงานมากกว่า:

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

ลูปหลักทำงานร่วมกับรายการงานปัจจุบัน

ลูปหลัก

cron ดั้งเดิมจาก Unix เวอร์ชัน 7 ทำงานค่อนข้างง่าย โดยอ่านการกำหนดค่าซ้ำในลูป เริ่มงานของนาทีปัจจุบันในฐานะ superuser และเข้าสู่โหมดสลีปจนกระทั่งเริ่มนาทีถัดไป วิธีการง่ายๆ นี้ในเครื่องรุ่นเก่าต้องใช้ทรัพยากรมากเกินไป

มีการเสนอเวอร์ชันทางเลือกใน SysV ซึ่ง daemon จะเข้าสู่โหมดสลีปจนกว่าจะถึงนาทีที่ใกล้ที่สุดซึ่งกำหนดงานไว้ หรือเป็นเวลา 30 นาที มีการใช้ทรัพยากรน้อยลงในการอ่านการกำหนดค่าอีกครั้งและตรวจสอบงานในโหมดนี้ แต่การอัปเดตรายการงานอย่างรวดเร็วนั้นไม่สะดวก

Vixie cron กลับมาตรวจสอบรายการงานนาทีละครั้ง โชคดีที่ในช่วงปลายยุค 80 มีทรัพยากรมากขึ้นในเครื่อง Unix มาตรฐาน:

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

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

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

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

ฟังก์ชัน cron_sleep เกี่ยวข้องโดยตรงในการดำเนินการงาน โดยเรียกใช้ฟังก์ชัน job_runqueue (ระบุและรันงาน) และ do_command (เรียกใช้งานแต่ละงาน) ฟังก์ชั่นสุดท้ายควรค่าแก่การตรวจสอบรายละเอียดเพิ่มเติม

กำลังรันภารกิจ

ฟังก์ชัน do_command ดำเนินการในรูปแบบ Unix ที่ดี กล่าวคือ ฟังก์ชัน do_command ทำหน้าที่แยกการทำงานแบบอะซิงโครนัส กระบวนการหลักยังคงเรียกใช้งานต่อไป กระบวนการย่อยเตรียมกระบวนการงาน:

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

มีตรรกะค่อนข้างมากใน child_process: มันนำเอาต์พุตมาตรฐานและสตรีมข้อผิดพลาดมาสู่ตัวมันเอง เพื่อที่จะส่งไปที่เมล (หากระบุตัวแปรสภาพแวดล้อม MAILTO ในตารางงาน) และสุดท้ายก็รอตัวแปรหลัก กระบวนการของงานที่จะเสร็จสมบูรณ์

กระบวนการงานเกิดขึ้นจากทางแยกอื่น:

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

โดยพื้นฐานแล้วทั้งหมดคือ cron ฉันละเว้นรายละเอียดที่น่าสนใจบางอย่าง เช่น การบัญชีสำหรับผู้ใช้ระยะไกล แต่ฉันสรุปสิ่งสำคัญไว้แล้ว

เล่ม

Cron เป็นโปรแกรมที่เรียบง่ายและมีประโยชน์อย่างน่าประหลาดใจ สร้างขึ้นจากประเพณีที่ดีที่สุดของโลก Unix เธอไม่ได้ทำอะไรเป็นพิเศษ แต่เธอทำงานได้อย่างยอดเยี่ยมมาหลายทศวรรษแล้ว การรับโค้ดสำหรับเวอร์ชันที่มาพร้อมกับ Ubuntu ใช้เวลาไม่เกินหนึ่งชั่วโมง และฉันก็สนุกมาก! ฉันหวังว่าฉันจะสามารถแบ่งปันกับคุณได้

ฉันไม่รู้เกี่ยวกับคุณ แต่ฉันเศร้านิดหน่อยที่รู้ว่าการเขียนโปรแกรมสมัยใหม่ซึ่งมีแนวโน้มที่จะซับซ้อนมากเกินไปและเป็นนามธรรมมากเกินไป ไม่ได้เอื้อต่อความเรียบง่ายดังกล่าวมาเป็นเวลานาน

มีทางเลือกที่ทันสมัยมากมายสำหรับ cron: systemd-timers ช่วยให้คุณสามารถจัดระเบียบระบบที่ซับซ้อนด้วยการพึ่งพา fcron ช่วยให้คุณควบคุมการใช้ทรัพยากรตามงานได้อย่างยืดหยุ่นมากขึ้น แต่โดยส่วนตัวแล้ว crontab ที่ง่ายที่สุดก็เพียงพอสำหรับฉันเสมอ

กล่าวโดยสรุป รัก Unix ใช้โปรแกรมง่ายๆ และอย่าลืมอ่านมานาสำหรับแพลตฟอร์มของคุณ!

ที่มา: will.com

เพิ่มความคิดเห็น