2011-04-29 4 views
7

10의 제곱 주파수에서 인터럽트되기를 원하므로/dev/rtc에서 인터럽트를 활성화하는 것이 이상적이지 않습니다. 인터럽트간에 1 밀리 초 또는 250 마이크로 초를 자고 싶습니다. 는/dev/HPET에서주기적인 인터럽트를 사용Linux에서 가장 정확한 실시간 정기 인터럽트를 얻으려면 어떻게해야합니까?

꽤 잘 작동하지만, 일부 컴퓨터에서 작동하지 않습니다. 분명히 HPET가없는 컴퓨터에서는 사용할 수 없습니다. 하지만 클럭 소스로 사용할 수있는 일부 컴퓨터에서도 작동하지 않습니다. 예를 들어 Core 2 Quad에서 poll으로 설정하면 HPET_IE_ON에서 커널 설명서에 포함 된 예제 프로그램이 실패합니다.

대신 직접 하드웨어 장치 드라이버와 인터페이스의 리눅스가 제공하는 itimer 인터페이스를 사용하는 것이 좋을 것입니다. 그리고 일부 시스템에서는 itimer가 주기적으로보다 안정적인 인터럽트를 제공합니다. 즉, hpet은 내가 원하는 빈도로 정확히 인터럽트 할 수 없으므로 인터럽트가 벽 시간에서 벗어나기 시작합니다. 그러나 일부 시스템은 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 번), 다른 프로세스와의 CPU 충돌이 있습니다. Shielded CPU

    1. 장소 과정을 처음 잠 과정 유무 :

      (물론 윈도우) 리눅스에서 "최고"타이밍을 달성 내가 찾은 메커니즘은 다음을 수행하는 것입니다 1ms 동안. 보호 된 CPU 인 경우 프로세스 은 OS 스케줄러의 틱 경계에서 깨우기 오른쪽이어야합니다.

    2. RDTSC를 직접 또는 CLOCK_MONOTONIC을 사용하여 현재 시간을 캡처하십시오. 모든 미래 기간의 절대 웨이크 업 시간을 계산하기위한 제로 시간으로 사용하십시오. 이렇게하면 시간이 지남에 따라 드리프트가 최소화됩니다. 하드웨어 시간 계가 시간이 지남에 따라 변동하기 때문에 완전히 제거 할 수는 없습니다 (열 문제 등). 그러나 이것은 꽤 좋은 출발입니다.
    3. 은 (즉, OS 스케쥴러가 될 수있는 가장 정확 같이) 목표 절대 기상 시간의 짧은 1ms의 인용 슬립 기능 다음 타이트한 루프가 계속 RDTSC/CLOCK_REALTIME 값을 검사에서 CPU 레코딩을 만든다.

    그것은 몇 가지 작업이 필요하지만 당신은이 방법을 사용하여 꽤 좋은 결과를 얻을 수 있습니다. 살펴보고 싶은 관련 질문은 here입니다.

  • +2

    리눅스는 이제 모든 jiffy를 실행하는 스케줄러에 의존하기보다는 준비가 될 때마다 작업을 스케줄 할 수 있습니다 (이것이 NO_HZ 옵션이라고 생각합니다). HPET 메서드 (read() 호출을 차단)는 HPET 메서드가 전혀 작동하지 않을 경우 250 마이크로 초마다 높은 실시간 우선 순위 작업으로 컨텍스트 전환을 위해 잘 작동합니다. itimer 메서드는 일반적으로 같은 일을하기 위해 작동합니다. 일부 컴퓨터에서는 항상 작동합니다. 다른 사람들은 대개 작동하며 (나는 경고 메시지가 넘치지 않습니다), 1MS +를 기다릴 때가 있습니다. – Matt

    4

    나는 베어 setitimer은() 설치와 같은 문제를 했어. 문제는 프로세스가 기본적으로 정적 우선 순위 레벨 0에 대한 SCHED_OTHER 정책으로 예약된다는 것입니다. 즉, 다른 모든 프로세스와 풀이되어 동적 인 우선 순위가 결정됩니다. 잠시 시스템로드가 발생하면 대기 시간이 생깁니다.

    이 솔루션은 것은 sched_setscheduler() 시스템 호출을 사용하여 적어도 하나의 고정 우선 순위를 높이고, 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 프로그램을 사용하여 동일한 작업을 수행하는 것이지만 RT 프로세스의 PID를 알아야합니다.

    sudo chrt -f -p 1 <pid> 
    

    내 블로그 게시물 here을 참조하십시오.

    관련 문제