2011-04-29 7 views
7

मैं आवृत्तियों पर बाधा डालना चाहता हूं जो दस की शक्तियां हैं, इसलिए/dev/rtc से इंटरप्ट को सक्षम करना आदर्श नहीं है। मैं इंटरप्ट्स के बीच 1 मिलीसेकंड या 250 माइक्रोसॉन्ड सोना चाहता हूं।मैं लिनक्स में सबसे सटीक रीयलटाइम आवधिक इंटरप्ट कैसे प्राप्त करूं?

/dev/hpet से आवधिक इंटरप्ट को सक्षम करना बहुत अच्छी तरह से काम करता है, लेकिन ऐसा लगता है कि यह कुछ मशीनों पर काम नहीं करता है। जाहिर है, मैं उन मशीनों पर इसका उपयोग नहीं कर सकता जिनके पास वास्तव में एचपीईटी नहीं है। लेकिन मैं इसे कुछ मशीनों पर काम नहीं कर सकता हूं जिनके पास क्लॉकसोर्स के रूप में उपलब्ध है। उदाहरण के लिए, कोर 2 क्वाड पर, कर्नेल प्रलेखन में शामिल उदाहरण प्रोग्राम HPET_IE_ON पर मतदान पर सेट होने पर विफल रहता है।

हार्डवेयर डिवाइस ड्राइवर के साथ इंटरफेसिंग करने के बजाय लिनक्स द्वारा प्रदान किए गए itimer इंटरफ़ेस का उपयोग करना अच्छा होगा। और कुछ प्रणालियों पर, itimer आवधिक इंटरप्ट प्रदान करता है जो समय के साथ अधिक स्थिर होते हैं। यही कारण है कि, हॉपेट बिल्कुल वांछित आवृत्ति पर बाधा नहीं डाल सकता है, क्योंकि इंटरप्ट्स दीवार के समय से निकलने लगते हैं। लेकिन मैं कुछ सिस्टमों को नींद के रास्ते (10+ मिलीसेकंड) को देख रहा हूं, उन्हें एक आईटीमर का उपयोग करना चाहिए।

यहां इंटरप्ट के लिए itimer का उपयोग कर एक परीक्षण प्रोग्राम है। कुछ प्रणालियों पर यह केवल एक चेतावनी मुद्रित करेगा कि यह लगभग 100 माइक्रोसॉन्ड या फिर लक्ष्य समय पर सो गया है। दूसरों पर, यह चेतावनी के बैच प्रिंट करेगा कि यह लक्ष्य समय पर 10+ मिलीसेकंड सो गया था। -lrt साथ संकलित और 50 -f sudo chrt के साथ चलाने के [नाम]

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <error.h> 
#include <errno.h> 
#include <sys/ioctl.h> 
#include <sys/types.h> 
#include <sys/time.h> 
#include <time.h> 
#include <signal.h> 
#include <fcntl.h> 
#define NS_PER_SECOND 1000000000LL 
#define TIMESPEC_TO_NS(aTime) ((NS_PER_SECOND * ((long long int) aTime.tv_sec)) \ 
    + aTime.tv_nsec) 

int main() 
{ 
    // Block alarm signal, will be waited on explicitly 
    sigset_t lAlarm; 
    sigemptyset(&lAlarm); 
    sigaddset(&lAlarm, SIGALRM ); 
    sigprocmask(SIG_BLOCK, &lAlarm, NULL); 

    // Set up periodic interrupt timer 
    struct itimerval lTimer; 
    int lReceivedSignal = 0; 

    lTimer.it_value.tv_sec = 0; 
    lTimer.it_value.tv_usec = 250; 
    lTimer.it_interval = lTimer.it_value; 

    // Start timer 
    if (setitimer(ITIMER_REAL, &lTimer, NULL) != 0) 
    { 
     error(EXIT_FAILURE, errno, "Could not start interval timer"); 
    } 
    struct timespec lLastTime; 
    struct timespec lCurrentTime; 
    clock_gettime(CLOCK_REALTIME, &lLastTime); 
    while (1) 
    { 
     //Periodic wait 
     if (sigwait(&lAlarm, &lReceivedSignal) != 0) 
     { 
      error(EXIT_FAILURE, errno, "Failed to wait for next clock tick"); 
     } 
     clock_gettime(CLOCK_REALTIME, &lCurrentTime); 
     long long int lDifference = 
      (TIMESPEC_TO_NS(lCurrentTime) - TIMESPEC_TO_NS(lLastTime)); 
     if (lDifference > 300000) 
     { 
      fprintf(stderr, "Waited too long: %lld\n", lDifference ); 
     } 
     lLastTime = lCurrentTime; 
    } 
    return 0; 
} 
+0

यह एक कर्नेल बग हो सकता है। मेरा itimer उदाहरण 2.6.32 का उपयोग कर सभी मशीनों पर ठीक काम करता है लेकिन 2.6.35 या 2.6.38 पर नहीं। – Matt

उत्तर

4

समय तंत्र आप उपयोग के बावजूद, यह अपने कार्य के रन स्थिति में परिवर्तन, का एक संयोजन करने पर निर्भर करता है जब कर्नेल अनुसूचक शुरू हो जाती है (आमतौर पर प्रति सेकंड 100 या 1000 बार), और अन्य प्रक्रियाओं के साथ सीपीयू विवाद। एक Shielded CPU

  • पर

    1. प्लेस प्रक्रिया प्रक्रिया शुरू में सोने के लिए कहें:

      तंत्र मैं पाया है कि लिनक्स पर "सर्वश्रेष्ठ" समय (विंडोज के साथ-साथ) को प्राप्त होता है निम्न करने के लिए है 1 एमएमएस के लिए यदि एक संरक्षित सीपीयू पर, आपकी प्रक्रिया ओएस शेड्यूलर की टिक सीमा

    2. पर सही हो, तो वर्तमान समय को कैप्चर करने के लिए या तो RDTSC सीधे या CLOCK_MONOTONIC का उपयोग करें। भविष्य की अवधि के लिए पूर्ण जागरूकता के समय की गणना के लिए इसे शून्य समय के रूप में उपयोग करें। यह समय के साथ बहाव को कम करने में मदद करेगा। हार्डवेयर टाइमकीपिंग समय के साथ उतार-चढ़ाव (थर्मल मुद्दों और इसी तरह) के बाद पूरी तरह से समाप्त नहीं किया जा सकता है लेकिन यह एक बहुत अच्छी शुरुआत है।
    3. एक नींद फ़ंक्शन बनाएं जो लक्षित पूर्ण जागरूकता समय से 1ms कम हो जाता है (क्योंकि यह ओएस शेड्यूलर सबसे सटीक है) तो सीपीयू को एक कसकर लूप में लगातार आरडीटीएससी/CLOCK_REALTIME मान की जांच करें।

    यह कुछ काम करता है लेकिन आप इस दृष्टिकोण का उपयोग करके बहुत अच्छे परिणाम प्राप्त कर सकते हैं। एक संबंधित प्रश्न जिसे आप देखना चाहते हैं, here पाया जा सकता है।

  • +2

    लिनक्स अब जब भी वे तैयार हो, शेड्यूलर पर भरोसा करने के बजाय तैयार होने पर कार्यों को शेड्यूल कर सकते हैं (मुझे विश्वास है कि यह NO_HZ विकल्प है)। एचपीईटी विधि (एक पठन() कॉल पर अवरुद्ध) एचपीईटी विधि बिल्कुल काम करता है तो हर 250 माइक्रोसॉन्ड के उच्च वास्तविक समय-प्राथमिकता कार्य में संदर्भ-स्विचिंग के लिए ठीक काम करता है। Itimer विधि आमतौर पर एक ही काम करने के लिए काम करता है। कुछ मशीनों पर, यह हर समय काम करता है। दूसरों पर, यह आमतौर पर काम करता है (मुझे चेतावनी संदेश से बाढ़ नहीं है), लेकिन कई बार जब मैं 1MS + प्रतीक्षा करता हूं। – Matt

    4

    मुझे एक नंगे setitimer() सेटअप के साथ एक ही समस्या है। समस्या यह है कि आपकी प्रक्रिया को डिफ़ॉल्ट रूप से स्थिर प्राथमिकता स्तर 0 पर SCHED_OTHER नीति द्वारा निर्धारित किया गया है। इसका मतलब है कि आप सभी अन्य प्रक्रियाओं के साथ पूल में हैं, और गतिशील प्राथमिकताएं तय करती हैं। पल कुछ सिस्टम लोड है, आप विलंबता प्राप्त करते हैं।

    समाधान शेड_सेटस्केल्डर() सिस्टम कॉल का उपयोग करना है, कम से कम एक को अपनी स्थिर प्राथमिकता बढ़ाएं, और SCHED_FIFO नीति निर्दिष्ट करें। यह नाटकीय सुधार का कारण बनता है।

    #include <sched.h> 
    ... 
    int main(int argc, char *argv[]) 
    { 
        ... 
        struct sched_param schedp; 
        schedp.sched_priority = 1; 
        sched_setscheduler(0, SCHED_FIFO, &schedp); 
        ... 
    } 
    

    आपको ऐसा करने में सक्षम होने के लिए रूट के रूप में चलाने के लिए है। विकल्प ऐसा करने के लिए chrt प्रोग्राम का उपयोग करना है, लेकिन आपको अपनी आरटी प्रक्रिया के पीआईडी ​​को जानना चाहिए।

    sudo chrt -f -p 1 <pid> 
    

    इसके बारे में मेरे ब्लॉग पोस्ट को देखें here

    संबंधित मुद्दे