รันคำสั่ง unix อย่างแม่นยำในช่วงเวลาสั้น ๆ โดยไม่เกิดความล่าช้าในการสะสมเมื่อเวลาผ่านไป


38

คำถาม

ฉันต้องการที่จะสามารถเรียกใช้คำสั่งยูนิกซ์ได้อย่างแม่นยำทุกวินาทีในระยะเวลานาน

ฉันต้องการวิธีแก้ปัญหาซึ่งไม่ล่าช้าหลังเวลาใดเวลาหนึ่งเนื่องจากเวลาที่คำสั่งนั้นต้องการสำหรับการดำเนินการ การนอนหลับ , นาฬิกาและบางหลามสคริปต์ล้มเหลวฉันทั้งหมดในเรื่องนี้

ในไมโครคอนโทรลเลอร์เช่นhttp://Arduino.ccฉันจะทำเช่นนั้นผ่านการขัดจังหวะนาฬิกาฮาร์ดแวร์ ฉันต้องการที่จะรู้ว่ามีวิธีการแก้สคริปต์เปลือกแม่นยำเวลาที่คล้ายกัน โซลูชั่นทั้งหมดที่ฉันพบใน StackExchange.com ทำให้เกิดความล่าช้าเวลาที่เห็นได้ชัดเจนหากใช้งานเกินชั่วโมง ดูรายละเอียดด้านล่าง

วัตถุประสงค์ / การใช้งานจริง

ฉันต้องการทดสอบว่าการเชื่อมต่อเครือข่ายของฉันนั้นต่อเนื่องหรือไม่โดยการส่งการประทับเวลาผ่านnc(netcat) ทุก ๆ 1 วินาที

ผู้ส่ง:

precise-timestamp-generator | tee netcat-sender.txt | nc $receiver $port

ผู้รับ:

nc -l -p $port > netcat-receiver.txt

หลังจากเสร็จสิ้นให้เปรียบเทียบบันทึกสองรายการ:

diff netcat-sender.txt netcat-receiver.txt

diffs จะเป็น timestamps ที่ไม่ถูกส่ง จากนี้ฉันจะรู้ว่าเวลาใดที่ LAN / WAN / ISP ของฉันสร้างปัญหา


แนวทางการนอนหลับ

while [ true ]; do date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done | tee timelog-sleep.txt

รับออฟเซ็ตในช่วงเวลาหนึ่งเนื่องจากคำสั่งภายในลูปยังใช้เวลาเล็กน้อย

ความแม่นยำ

cat timelog-sleep.txt

2012-07-16 00:45:16
[...]
2012-07-16 10:20:36

วินาทีที่ผ่านไป: 34520

wc -l timelog-sleep.txt

บรรทัดในไฟล์: 34243

สรุปความแม่นยำ:

  • 34520-34243 = ปัญหาเวลา 277
  • 34520/34243 = 1.008 = ลด 0.8%

โซลูชัน REPEAT PYTHON

พบที่: ทำซ้ำคำสั่ง Unix ทุก ๆ x วินาทีตลอดไป

repeat.py 1 "date '+%Y-%m-%d %H:%M:%S'" >> timelog-repeat-py.txt

ควรหลีกเลี่ยงการชดเชยเวลา แต่ไม่สามารถทำได้

ความแม่นยำ

wc -l timelog-repeat-py.txt

2012-07-16 13:42:44
[...]
2012-07-16 16:45:24

วินาทีที่ผ่านไป: 10960

wc -l timelog-repeat-py.txt

บรรทัดในไฟล์: 10859

สรุปความแม่นยำ:

  • 10960-10859 = ปัญหาการจับเวลา 101
  • 10960/10859 = 1.009 = 0.9% ปิด

Solution WATCH

watch -n 1 "date '+%Y-%m-%d %H:%M:%S' >> ~/Desktop/timelog-watch.txt"

ความแม่นยำ

wc -l timelog-watch.txt
2012-07-16 11:04:08
[...]
2012-07-16 13:25:47

วินาทีที่ผ่านไป: 8499

wc -l timelog-watch.txt

บรรทัดในไฟล์: 8366

สรุปความแม่นยำ:

  • 8499-8366 = 133 ปัญหาเรื่องเวลา
  • 8499/8366 = 1.016 = 1.6% ปิด

5
ความละเอียด, ความแม่นยำที่ต้องการคืออะไรและทำไมคุณถึงต้องการ / คุณใช้เพื่ออะไร
jippie

จะเกิดอะไรขึ้นถ้าคุณniceทำกระบวนการที่หลับ
Tilo Wiklund

1
คุณคิดว่าจะใช้การตั้งเวลาจริงเพื่อลดความล่าช้าในการโทร sleep () หรือไม่
mdpc

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

2
เรียกใช้คำสั่งแต่ละคำสั่งในหัวข้อของตนเองวิธีการบล็อกที่เกี่ยวข้องกับ IO จะไม่ทำให้คุณเสียเวลา
Joel Cornett

คำตอบ:


12

สคริปต์ Perl นี้ฉันทำงานได้อย่างไร?

#!/usr/bin/perl

use strict;
use warnings;
use Time::HiRes qw/time sleep/;

sub launch {
    return if fork;
    exec @_;
    die "Couldn't exec";
}

$SIG{CHLD} = 'IGNORE';

my $interval = shift;
my $start = time();
while (1) {
    launch(@ARGV);
    $start += $interval;
    sleep $start - time();
}

ใช้: perl timer.pl 1 date '+%Y-%m-%d %H:%M:%S'

มันใช้เวลา 45 นาทีโดยไม่มีการข้ามครั้งเดียวและฉันสงสัยว่ามันจะยังคงทำเช่นนั้นต่อไปเว้นแต่ก) โหลดของระบบจะสูงจนส้อม () ใช้เวลามากกว่าหนึ่งวินาทีหรือ b) แทรกวินาทีกระโดด

ไม่สามารถรับประกันได้ว่าคำสั่งจะทำงานตามช่วงเวลาที่แน่นอนเนื่องจากมีค่าใช้จ่ายบางส่วน แต่ฉันสงสัยว่ามันแย่กว่าสารละลายที่ขัดจังหวะ

ฉันใช้เวลาประมาณหนึ่งชั่วโมงโดยใช้date +%N(nanoseconds, GNU extension) และใช้สถิติบางอย่างกับมัน ความล่าช้ามากที่สุดคือ 1 155 microseconds ค่าเฉลี่ย (ค่าเฉลี่ยเลขคณิต) 216 ,s, มัธยฐาน 219 ,s, ส่วนเบี่ยงเบนมาตรฐาน 42 .s มันวิ่งเร็วกว่า 270 95s 95% ของเวลา ฉันไม่คิดว่าคุณจะสามารถเอาชนะได้ยกเว้นด้วยโปรแกรม C


1
ฉันวิ่งข้ามคืนโดยไม่มีแอพพลิเคชั่นผู้ใช้อื่น ๆ ที่มีช่วงเวลา 1 วินาทีและมันวิ่งไป 29241 วินาทีโดยไม่ข้ามวินาทีเดียว! สิ่งนี้จะเหมาะสมกับวัตถุประสงค์ของฉัน จากนั้นฉันก็วิ่งอีกครั้งในเช้าวันนี้ด้วยช่วงเวลา 0.1 วินาทีGNU dateด้วย+%Nและหลังจากนั้นเพียง 3 นาทีก็เกิดข้อผิดพลาดขึ้นมา: Time::HiRes::sleep(-0.00615549): negative time not invented yet at ~/bin/repeat.pl line 23.บรรทัดที่ 23 ในสคริปต์ที่บันทึกไว้ของฉัน:sleep $start - time();
porg

หากคุณรันด้วยช่วงเวลา 0.01 วินาทีหรือ 0.001 วินาทีมันเป็นเพียงไม่กี่วินาทีหรือน้อยกว่าจนกว่าโปรแกรมจะยกเลิกข้อผิดพลาด "เวลาเชิงลบ" แต่สำหรับวัตถุประสงค์ของฉันมันเหมาะกับ!
porg

28

ualarm()ฟังก์ชันPOSIX ช่วยให้คุณสามารถกำหนดเคอร์เนลเพื่อส่งสัญญาณกระบวนการของคุณเป็นระยะด้วยความแม่นยำระดับไมโครวินาที

ชักโปรแกรมง่าย ๆ :

 #include<unistd.h>
 #include<signal.h>
 void tick(int sig){
     write(1, "\n", 1);
 }
 int main(){
     signal(SIGALRM, tick);
     ualarm(1000000, 1000000); //alarm in a second, and every second after that.
     for(;;)
         pause();
 }

รวบรวม

 gcc -O2 tick.c -o tick

จากนั้นแนบกับสิ่งที่คุณต้องทำเป็นระยะเช่น:

./tick | while read x; do
    date "+%Y-%m-%d %H:%M:%S"
done | tee timelog-sleep.txt

ฉันต้องการเชลล์พิเศษหรือ C-std สำหรับสิ่งนั้นหรือไม่? ฉันได้รวบรวมมัน (ซึ่งให้คำเตือนเล็ก ๆ น้อย ๆ ที่หายไปกลับ) แต่ไม่มีการผลิตเอาท์พุท
คณิตศาสตร์

@ คณิตศาสตร์ด้วย-std=c99คุณจะไม่ได้รับคำเตือนเกี่ยวกับการส่งคืนที่หายไป มิฉะนั้นคุณไม่ควรต้องการอะไรเป็นพิเศษ คุณพิมพ์ศูนย์พิเศษผิดหรือเปล่า? strace ./tickจะแสดงให้คุณเห็นว่ามันทำอะไรจากมุมมองของตึกระฟ้า
Dave

ฉันได้รับ: gcc -O2 -std = c99 -o tick tick.c tick.c: ในฟังก์ชั่น 'main': tick.c: 10: 5: คำเตือน: การประกาศโดยนัยของฟังก์ชัน 'ualarm' [-Wimplicit-function-declaration ] tick.c: ในฟังก์ชั่น 'ติ๊ก': tick.c: 5: 10: คำเตือน: ละเว้นการส่งคืนค่าของ 'เขียน' ประกาศด้วยคุณสมบัติ warn_unused_result [- ผลการใช้งานที่ไม่ถูกต้อง] :: ดูเหมือนว่าระบบของฉัน (Ubuntu 12.04) ไม่สนับสนุน อย่างไรก็ตามอย่างน้อยมีหน้าคนที่ ualarm ควรอยู่ใน unistd.h (gcc คือ 4.6.3)
คณิตศาสตร์

28

คุณลองwatchกับพารามิเตอร์--preciseหรือไม่

watch -n 1 --precise "date '+%Y-%m-%d %H:%M:%S.%N' >> ~/Desktop/timelog-watch.txt"

จากหน้าคน:

โดยทั่วไปช่วงเวลานี้จะถูกตีความว่าเป็นจำนวนเวลาระหว่างความสมบูรณ์ของการรันหนึ่งคำสั่งและการเริ่มต้นของการรันครั้งถัดไป อย่างไรก็ตามด้วยตัวเลือก -p หรือ --precise คุณสามารถใช้ความพยายามดูเพื่อรันคำสั่งทุกช่วงเวลาไม่กี่วินาที ลองด้วย ntptime และสังเกตว่าวินาทีเศษส่วนยังคงอยู่ (เกือบ) เหมือนเมื่อเทียบกับโหมดปกติที่เพิ่มขึ้นอย่างต่อเนื่อง

แม้ว่าพารามิเตอร์อาจไม่พร้อมใช้งานในระบบของคุณ

คุณควรพิจารณาว่าจะเกิดอะไรขึ้นเมื่อการเรียกใช้โปรแกรมของคุณต้องการมากกว่าหนึ่งวินาที ควรดำเนินการตามกำหนดการครั้งต่อไปควรถูกข้ามไปหรือไม่หรือจะทำงานช้า

อัปเดต : ฉันใช้งานสคริปต์มาระยะหนึ่งแล้วและก็ไม่ได้สูญเสียไปเพียงขั้นตอนเดียว:

2561 lines
start: 2012-07-17 09:46:34.938805108
end:   2012-07-17 10:29:14.938547796

อัปเดต:การ--preciseตั้งค่าสถานะเป็นการเพิ่ม Debian แต่การแก้ไขค่อนข้างง่าย: http://patch-tracker.debian.org/patch/series/view/procps/1:3.2.8-9squeeze1/watch_precision_time.patch


วิธีที่จะไป หวังว่าฉันจะได้ 10 สิ่งนี้
krlmlr

เวอร์ชันของwatchตัวเลือกที่รองรับนั้น มันไม่ได้อยู่บนเครื่องที่ฉันตรวจสอบ
tylerl

มันเป็นเวอร์ชั่น 0.3.0 ซึ่งเป็นเวอร์ชั่นปัจจุบันบน Ubuntu 12.04 มันมาจากรุ่น 3.2.8-11ubuntu6 ของแพ็คเกจ procps
daniel kullmann

อืมที่ procps --preciseแพคเกจที่มาไม่ได้สนับสนุน นี่คือการเพิ่ม Debian (3.2.8-9, watch_precision_time.patch)
daniel kullmann

1
ตกลง แต่เหมือนกับ mdpc ในความคิดเห็นเกี่ยวกับคำถามที่ระบุ: สิ่งนี้อาจล้มเหลวเมื่อระบบของคุณมีภาระมาก ฉันเพิ่งทดสอบร่วมกับความเครียด (วางภาระบนดิสก์และแกน) และได้รับนี้: 2012-07-24 07:20:21.864818595 2012-07-24 07:20:22.467458430 2012-07-24 07:20:23.068575669 2012-07-24 07:20:23.968415439 สิ่งเรียลไทม์ (เคอร์เนล ฯลฯ ) มีอยู่ด้วยเหตุผล!
คณิตศาสตร์

18

crontabมีความละเอียด 1 นาที หากคุณพอใจกับเวลาล่าช้าสะสมต่อนาทีและจากนั้นรีเซ็ตในนาทีถัดไปแนวคิดพื้นฐานนี้สามารถใช้งานได้:

* * * * * for second in $(seq 0 59); do /path/to/script.sh & sleep 1s;done

โปรดทราบว่าscript.shจะทำงานในพื้นหลัง ซึ่งจะช่วยลดความล่าช้าที่เกิดขึ้นกับการวนซ้ำแต่ละครั้ง

ขึ้นอยู่กับว่าsleepเกิดความล่าช้ามากแค่ไหนมีโอกาส 59 วินาทีที่ทับซ้อนกับ 0 วินาทีในนาทีถัดไป

แก้ไขเพื่อโยนในผลลัพธ์บางรูปแบบในรูปแบบเดียวกันกับคำถาม:

$ cat timelog-cron
2012-07-16 20:51:01
...
2012-07-16 22:43:00

1 ชั่วโมง 52 นาที = 6720 วินาที

$ wc -l timelog-cron
6720 timelog-cron

ปัญหาการจับเวลา 0, ลด 0% การสะสมเวลาใด ๆ จะรีเซ็ตทุกนาที


1
ฉันขอถามได้ไหมว่าทำไมเรื่องนี้จึงถูกลดระดับลง
Izkata

2
มันน่าแฮ็ค
hhaamu

2
@hhaamu สิ่งที่น่าเกลียดเกี่ยวกับมันคืออะไร? วัตถุประสงค์ทั่วไปของระบบปฏิบัติการบนพีซีไม่ได้ออกแบบมาสำหรับการดำเนินการกำหนดเวลาที่แม่นยำมากดังนั้นคุณคาดหวังอะไรอีก หากคุณต้องการเวลา "ฉลาด" และแม่นยำอย่างยิ่งคุณจะต้องใช้ตัวกำหนดเวลา CPU อื่นหรือเปลี่ยนไปใช้เคอร์เนลเรียลไทม์หรือใช้ฮาร์ดแวร์เฉพาะเป็นต้นนี่เป็นวิธีการแก้ปัญหาที่สมบูรณ์แบบและฉันไม่เห็นเหตุผลใด ๆ downvotes แน่นอนว่าเป็นการปรับปรุงในรุ่นที่มีเพียง "ทำงานในพื้นหลัง" โดยไม่มีการซิงค์ซ้ำเป็นครั้งคราวผ่าน cron
jw013

1
บวกกับการหยุดมันเป็นเรื่องง่าย ไม่จำเป็นต้องเสี่ยงที่จะฆ่ามันในรอบกลาง - ลบรายการออกจาก crontab และจบด้วยตัวเองในตอนท้ายของนาที
Izkata

คุณโชคดีที่ในระบบของคุณcronแม่นยำเป็นครั้งที่สอง แต่ไม่ใช่ในกรณีทั่วไป
Dmitry Grigoryev

15

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

คุณสามารถทำสิ่งนี้ได้คือทุบตีหรือภาษาการเขียนโปรแกรมอื่น ๆ แต่ที่สำคัญคือการใช้นาฬิกาเพื่อกำหนดระยะเวลาในการกำหนดเวลาพักเครื่องครั้งถัดไป ก่อนที่คุณจะนอนให้ตรวจสอบนาฬิกาดูว่าคุณเหลือเวลาเท่าไรแล้วนอนหลับพักผ่อนต่างกัน

เนื่องจากการกำหนดเวลากระบวนการประนีประนอมคุณไม่ได้รับการรับรองว่าจะตื่นขึ้นมาบนนาฬิกาของนาฬิกา แต่คุณควรจะปิดพอสมควร (ภายในไม่กี่มิลลิวินาทีไม่โหลดหรือภายในไม่กี่ร้อยมิลลิวินาทีภายใต้การโหลด) และคุณจะไม่สะสมข้อผิดพลาดเมื่อเวลาผ่านไปเนื่องจากแต่ละครั้งที่คุณซิงโครไนซ์ใหม่ทุกรอบการนอนหลับและลบข้อผิดพลาดสะสมใด ๆ

หากคุณต้องการทำเครื่องหมายที่นาฬิกาอย่างแน่นอนสิ่งที่คุณกำลังมองหาคือระบบปฏิบัติการแบบเรียลไทม์ซึ่งออกแบบมาเพื่อจุดประสงค์นี้โดยเฉพาะ


ฉันคิดว่ามันน่าจะเป็นไปได้มากที่โปรแกรม porg ได้ทำการทดสอบบล๊อกในขณะที่รันกระบวนการที่ตั้งใจ - ซึ่งเป็นเหตุผลที่พวกเขาควรทำเพื่อหลีกเลี่ยงการฆ่าเครื่องที่กำลังทำงานอยู่
symcbean

ไม่ว่าคุณจะบล็อคหรือไม่กลไกก็ใช้ได้ดี หากคุณบล็อกคุณจะนอนเวลาที่เหลือหลังจากการบล็อก หากคุณไม่ปิดกั้นการกำหนดเวลาหรือกระบวนการของคุณกำลังหลับอยู่ในขณะที่อีกกระบวนการกำลังทำงานอยู่ ไม่ว่าจะด้วยวิธีใดผลลัพธ์เดียวกัน
tylerl

@tylerl: บรรทัดคำสั่งที่เป็นรูปธรรมจะมีลักษณะอย่างไรสำหรับโซลูชันของคุณ
porg

ฉันเดาว่าคุณหมายถึงสิ่งเดียวกันเช่น@lynxlynxlynx
porg

@porg คุณต้องใช้date +%S.%Nเพื่อรับจำนวนวินาทีด้วยความแม่นยำระดับรองและusleepการนอนด้วยความแม่นยำระดับรอง แต่หลังจากนั้นมันเป็นเพียงเรื่องของคณิตศาสตร์
tylerl

7

ฉันมักจะยอมแพ้เมื่อมีบางสิ่งบางอย่างทำงานตามช่วงเวลา ฉันคิดว่าคุณจะต้องเขียนโปรแกรม C และให้ความสนใจอย่างมากกับส่วนของช่วงเวลา 1 วินาทีด้วยรหัสของคุณเอง คุณอาจจะต้องใช้กระบวนการทำเกลียวหรือกระบวนการสื่อสารหลายขั้นตอนเพื่อให้สิ่งนี้ทำงานได้ ใช้ความระมัดระวังเพื่อหลีกเลี่ยงค่าใช้จ่ายในการเริ่มต้นเธรด

การอ้างอิงหนึ่งที่ดูเหมือนว่าเกี่ยวข้องกับวันที่ถึงปี 1993: นาฬิกาสุ่มตัวอย่างสำหรับการประมาณค่าการใช้ประโยชน์ CPU และการทำโปรไฟล์รหัส คุณจะต้องดูภาคผนวก "รหัสที่มาที่เป็นปฏิปักษ์" เพื่อดูว่าพวกเขาวัดระยะเวลาอย่างแม่นยำอย่างไร โปรแกรมของพวกเขาในเวลาที่ถูกต้อง เนื่องจากรหัสมีอายุ 19 ปีมันอาจไม่ได้พอร์ตโดยตรงหรือง่ายดาย แต่ถ้าคุณอ่านและพยายามเข้าใจหลักการที่เกี่ยวข้องอาจแนะนำรหัสของคุณ

แก้ไข: พบการอ้างอิงอื่นที่อาจช่วยได้: ผลกระทบของความละเอียดสัญญาณนาฬิกาในการจัดตารางเวลาของกระบวนการโต้ตอบและซอฟต์เรียลไทม์ ที่จะช่วยให้คุณมีพื้นฐานทางทฤษฎี


4

ลองดูที่ nanosleep () (จาก http://linux.about.com/library/cmd/blcmdl2_nanosleep.htm ) แทนที่จะทำให้โปรแกรมของคุณนอนหลับ 1 วินาทีทำให้มันเข้าสู่โหมดสลีป (1 - กระสุนที่ใช้ในการทำงาน) วินาที คุณจะได้รับการแก้ไขที่ดีขึ้นมาก


คุณสามารถทำเช่นเดียวกันกับปกติคือsleep sleep 0.99ปัญหาคือจำนวนเวลาที่ใช้ในการเรียกใช้อยู่ไกลจากค่าคงที่แม้ค่าเฉลี่ยอาจเปลี่ยนแปลงตลอดเวลา
Dmitry Grigoryev

3

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

ดังนั้นสิ่งนี้น่าจะดีกว่า แต่ก็ยังไม่ดีพอ:

while [ true ]; do date "+%Y-%m-%d %H:%M:%S" & sleep 1; done | 
tee timelog-sleep.txt

ในคอมพิวเตอร์ของฉันมีข้อผิดพลาด 2 ข้อใน 20 นาทีหรือ 0,1 ต่อนาทีซึ่งเป็นการปรับปรุงห้าเท่าที่ต่ำกว่าการวิ่งของคุณ


ปัญหาsleep 1คือมันรับประกันว่าจะนอนหลับอย่างน้อยหนึ่งวินาที - ไม่น้อยกว่า ดังนั้นข้อผิดพลาดสะสม
hhaamu

การเปรียบเทียบผลลัพธ์ช่วงเวลาจากคอมพิวเตอร์สองเครื่องที่แตกต่างกันนั้นค่อนข้างไร้ความหมายเว้นแต่ว่าคุณได้เรียกใช้รหัสดั้งเดิมในระบบของคุณและได้รับผลลัพธ์เช่นเดียวกับ OP
Dmitry Grigoryev

1

น่าเกลียด แต่มันใช้งานได้ คุณควรคิดใหม่เกี่ยวกับการออกแบบโปรแกรมของคุณถ้าคุณต้องการวนซ้ำเช่นนี้ โดยทั่วไปจะตรวจสอบว่าทั้งวินาทีปัจจุบันเท่ากับการตรวจสอบก่อนหน้านี้และพิมพ์จำนวนนาโนวินาทีนับตั้งแต่การเปลี่ยนวินาที ความแม่นยำได้รับอิทธิพลจากการนอนหลับ. 001

while true; do T=$( date +%s ); while [[ $T -eq $( date +%s ) ]]; do sleep .001; done; date "+%N nanoseconds late"; done

ความแม่นยำอยู่ในหน่วยมิลลิวินาทีหากว่า 'น้ำหนักบรรทุก' date "+%N nanoseconds late"ไม่ได้ใช้เวลานานกว่าวินาที คุณสามารถลดภาระของ CPU trueโดยการเพิ่มระยะเวลาการนอนหลับหรือถ้าคุณจริงๆไม่คิดเพียงแค่เปลี่ยนคำสั่งการนอนหลับโดย

002112890 nanoseconds late
001847692 nanoseconds late
002273652 nanoseconds late
001317015 nanoseconds late
001650504 nanoseconds late
002180949 nanoseconds late
002338716 nanoseconds late
002064578 nanoseconds late
002160883 nanoseconds late

นี่เป็นข้อปฏิบัติที่ไม่ดีเพราะคุณทำให้โพล CPU ของคุณเป็นเหตุการณ์และคุณกำลังสูญเสียรอบ CPU คุณอาจต้องการแนบกับตัวจับเวลาขัดจังหวะ (ไม่สามารถทำได้จากการทุบตี) หรือใช้ฮาร์ดแวร์เฉพาะเช่นไมโครคอนโทรลเลอร์ พีซีและระบบปฏิบัติการไม่ได้ถูกออกแบบมาเพื่อความแม่นยำในการจับเวลาสูง


1

อีกวิธีหนึ่งคือการใช้ suspend ในลูปและส่ง SIGCONT จากโปรแกรมภายนอกที่แม่นยำ การส่งสัญญาณมีน้ำหนักเบามากและมีความหน่วงแฝงน้อยกว่าการดำเนินการบางอย่าง คุณสามารถจัดคิวคำสั่งล่วงหน้าไว้ล่วงหน้าด้วยคำสั่ง "at" แทบจะไม่มีใครใช้ "at" อีกต่อไปฉันไม่แน่ใจว่าแม่นยำแค่ไหน

หากความแม่นยำเป็นสิ่งสำคัญและคุณต้องการจริงจังกับเรื่องนี้ฟังก์ชั่นนี้จะคล้ายกับแอปพลิเคชั่นที่โดยทั่วไปแล้วคุณจะใช้ RTOS ซึ่งสามารถทำได้ภายใต้ Linux ด้วยเคอร์เนลแพตช์ RT-Preempt ซึ่งจะทำให้คุณมีความแม่นยำ การควบคุมการขัดจังหวะ แต่อาจรบกวนมากกว่าที่คุ้มค่า

https://rt.wiki.kernel.org/index.php/RT_PREEMPT_HOWTO

Xenomai อาจมีประโยชน์เช่นกันมันเป็นการนำ RTOS มาใช้อย่างสมบูรณ์และได้รับการจัดพอร์ตสำหรับ x86 และ x86_64 แต่มีบางโปรแกรมที่เกี่ยวข้อง

http://www.xenomai.org/index.php/Main_Page


1

ด้วยksh93(ซึ่งมีจุดลอย$SECONDSและบิวด์อินsleep)

typeset -F SECONDS=0
typeset -i i=0
while true; do
   cmd
   sleep "$((++i - SECONDS))"
done

สคริปต์เดียวกันจะทำงานด้วยzshเช่นกัน แต่จะเรียกใช้sleepคำสั่งของระบบของคุณ zshมีzselectbuiltin แต่มีความละเอียด 1/100 เท่านั้น


0

ฉันจะไปกับโปรแกรม C ขนาดเล็ก:

#include <sys/time.h>
#include <unistd.h>

int main(int argc, char **argv, char **envp)
{
    struct timeval start;
    int rc = gettimeofday(&start, NULL);
    if(rc != 0)
            return 1;

    for(;;)
    {
        struct timeval now;
        rc = gettimeofday(&now, NULL);
        useconds_t delay;
        if(now.tv_usec < start.tv_usec)
            delay = start.tv_usec - now.tv_usec;
        else
            delay = 1000000 - now.tv_usec + start.tv_usec;
        usleep(delay);
        pid_t pid = fork();
        if(pid == -1)
            return 1;
        if(pid == 0)
            _exit(execve(argv[1], &argv[1], envp));
    }
}

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

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

โปรแกรมนี้จะได้รับช่วงเวลาที่ค่อนข้างยาวหรือสั้นกว่าเมื่อปรับนาฬิกาด้วย NTP หรือตั้งค่าด้วยตนเอง หากโปรแกรมควรจัดการสิ่งนี้ POSIX จะให้timer_create(CLOCK_MONOTONIC, ...)สิ่งที่ไม่ได้รับผลกระทบจากสิ่งนี้


0

คุณควรติดตามเวลาปัจจุบันและเปรียบเทียบกับเวลาเริ่มต้น ดังนั้นคุณจะนอนเป็นจำนวนเงินที่คำนวณได้จากการคำนวณซ้ำแต่ละครั้งไม่ใช่จำนวนคงที่ ด้วยวิธีนี้คุณจะไม่เกิดข้อผิดพลาดในการจับเวลาและเปลี่ยนจากตำแหน่งที่คุณควรจะเป็นเพราะคุณรีเซ็ตการจับเวลาของคุณในแต่ละวงเป็นเวลาที่แน่นอนตั้งแต่เริ่มต้น

นอกจากนี้บางฟังก์ชั่นการนอนหลับจะกลับมาก่อนหากมีการหยุดพักชั่วคราวดังนั้นในกรณีนี้คุณจะต้องเรียกวิธีการนอนหลับของคุณอีกครั้งจนกว่าจะครบระยะเวลาเต็ม


0

นี่คือสคริปต์ทุบตีและความแม่นยำสูง มันใช้ usleep เพื่อความแม่นยำระดับไมโครวินาที

http://wiki.junkemailfilter.com/index.php/How_to_run_a_Linux_script_every_few_seconds_under_cron

มี 3 สะเก็ดที่นั่น ดูที่ด้านล่าง จะทำได้มากถึงประมาณ 2,400 การประมวลผลต่อนาทีด้วยความแม่นยำที่สมเหตุสมผล และมันง่ายมาก


0

อันนี้สามารถทำงานได้อย่างน้อย 100 ครั้งต่อวินาทีด้วยความละเอียดที่แม่นยำมาก

การมีอยู่ของไดเรกทอรีของจำนวนลูปต่อนาทีจะสร้างตาราง รุ่นนี้รองรับความละเอียดระดับไมโครวินาทีโดยที่คอมพิวเตอร์ของคุณสามารถรองรับได้ จำนวนการประมวลผลต่อนาทีไม่จำเป็นต้องหารให้เท่ากันด้วย 60 และไม่ จำกัด ที่ 60 ฉันได้ทดสอบไปที่ 6,000 และทำงานได้

เวอร์ชันนี้สามารถติดตั้งในไดเร็กทอรี /etc/init.d และรันเป็นเซอร์วิส

#! /bin/sh

# chkconfig: 2345 91 61
# description: This program is used to run all programs in a directory in parallel every X times per minute. \
#              Think of this program as cron with microseconds resolution.

# Microsecond Cron
# Usage: cron-ms start
# Copyright 2014 by Marc Perkel
# docs at http://wiki.junkemailfilter.com/index.php/How_to_run_a_Linux_script_every_few_seconds_under_cron"
# Free to use with attribution

# The scheduling is done by creating directories with the number of"
# executions per minute as part of the directory name."

# Examples:
#   /etc/cron-ms/7      # Executes everything in that directory  7 times a minute
#   /etc/cron-ms/30     # Executes everything in that directory 30 times a minute
#   /etc/cron-ms/600    # Executes everything in that directory 10 times a second
#   /etc/cron-ms/2400   # Executes everything in that directory 40 times a second

basedir=/etc/cron-ms

case "$1" in

   start|restart|reload)
   $0 stop
   mkdir -p /var/run/cron-ms
   for dir in $basedir/* ; do
      $0 ${dir##*/} &
   done
   exit
   ;;

   stop)
   rm -Rf /var/run/cron-ms
   exit
   ;;

esac

# Loops per minute is passed on the command line

loops=$1
interval=$((60000000/$loops))

# Just a heartbeat signal that can be used with monit to verify it's alive

touch /var/run/cron-ms

# After a restart the PIDs will be different allowing old processes to terminate

touch /var/run/cron-ms/$$

# Sleeps until a specific part of a minute with microsecond resolution. 60000000 is full minute

usleep $(( $interval - 10#$(date +%S%N) / 1000 % $interval ))

# Deleting the PID files exit the program

if [ ! -f /var/run/cron-ms/$$ ]
then
   exit
fi

# Run all the programs in the directory in parallel

for program in $basedir/$loops/* ; do
   if [ -x $program ] 
   then
      $program &> /dev/null &
   fi
done

exec $0 $loops
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.