Pergunta

Eu preciso de uma marca de timer com resolução de 1ms sob linux. Ele é usado para incrementar um valor de temporizador que por sua vez é usado para ver se vários eventos devem ser acionados. O timerfd_create POSIX não é uma opção por causa da exigência glibc. Tentei timer_create e timer_settimer, mas o melhor que eu recebo deles é uma resolução de 10ms, valores menores parecem padrão para resolução de 10ms. Getittimer e setitimer ter uma resolução de 10 ms de acordo com a página do manual.

A única maneira de fazer isso temporizador I atualmente pode pensar é usar clock_gettime com CLOCK_MONOTONIC no meu loop principal de um teste se um ms já passou, e se assim para aumentar o contador (e, em seguida, verificar se os vários eventos devem disparar ).

Existe uma maneira melhor de fazer isso do que constantemente consulta no circuito principal? Qual é a solução recomendada para isso?

A linguagem que estou usando é bom e velho c

Atualizar
Eu estou usando um 2.6.26 do kernel. Eu sei que você pode tê-lo interromper a 1 kHz, eo timer_ POSIX * funções, em seguida, pode ser programado para até 1ms mas isso não parece ser confiável e eu não quero usar isso, porque ele pode precisar de um novo kernel em algum sistemas. Alguns estoque Kernel parecem ainda tem a 100Hz configurado. E eu precisaria para detectar isso. O aplicativo pode ser executado em algo mais do que o meu sistema:)

Eu não consigo dormir para 1ms porque pode haver eventos de rede que eu tenho que reagir.

Como eu resolvê-lo Uma vez que não é tão importante que eu simplesmente declarou que o cronômetro global tem uma resolução de 100 ms. Todos os eventos usando o seu próprio temporizador tem que definir pelo menos 100ms para a expiração do temporizador. Eu estava mais ou menos querendo saber se haveria uma maneira melhor, daí a pergunta.

Por que eu aceitei a resposta Acho que a resposta de freespace melhor descrito por isso não é realmente possível sem tempo real do sistema Linux.

Foi útil?

Solução

Polling no circuito principal não é uma resposta ou -. Seu processo não pode ficar muito tempo de CPU, então mais do que 10ms irá decorrer antes o seu código começa a correr, tornando-moot

10ms é sobre a resolução do temporizador padrão para a maioria dos sistemas não- realtime operacionais (RTOS ). Mas é discutível em um não-RTOS - o comportamento do programador e despachante vai influenciar muito o quão rápido você pode responder a um temporizador expirar. Por exemplo, mesmo suponha que você tinha um temporizador resolução sub 10ms, você não pode responder ao timer expirar se o seu código não está em execução. Desde que você não pode prever quando seu código está indo para executar, você não pode responder a expiração do temporizador com precisão.

Não é de kernels curso em tempo real Linux, veja http://www.linuxdevices.com/articles /AT8073314981.html para uma lista. Um RTOS instalações ofertas em que você pode obter garantias macios ou duros sobre quando seu código está indo para executar. Esta é a única maneira de forma confiável e precisa responder a temporizadores expiram etc.

Outras dicas

Para obter 1ms temporizadores resolução fazer o libevent faz.

Organize suas temporizadores em um min-heap , isto é, o topo da pilha é o timer com o mais antigo de expiração do tempo (absoluto) (a rb-árvore também funcionaria, mas com mais sobrecarga). Antes de chamar select() ou epoll() em seu ciclo de eventos principal calcular o delta em milissegundos entre o tempo de expiração dos primeiros temporizador e agora. Utilize este delta como o tempo limite para select(). select() e epoll() tempos de espera tem resolução de 1 ms.

Eu tenho um teste de resolução de timer que usa o mecanismo foi explicado acima (mas não libevent). O teste mede a diferença entre o tempo do temporizador de validade desejado e seu termo real de 1ms, 5ms e 10ms temporizadores:

1000 deviation samples of  1msec timer: min=  -246115nsec max=  1143471nsec median=   -70775nsec avg=      901nsec stddev=    45570nsec
1000 deviation samples of  5msec timer: min=  -265280nsec max=   256260nsec median=  -252363nsec avg=     -195nsec stddev=    30933nsec
1000 deviation samples of 10msec timer: min=  -273119nsec max=   274045nsec median=   103471nsec avg=     -179nsec stddev=    31228nsec
1000 deviation samples of  1msec timer: min=  -144930nsec max=  1052379nsec median=  -109322nsec avg=     1000nsec stddev=    43545nsec
1000 deviation samples of  5msec timer: min= -1229446nsec max=  1230399nsec median=  1222761nsec avg=      724nsec stddev=   254466nsec
1000 deviation samples of 10msec timer: min= -1227580nsec max=  1227734nsec median=    47328nsec avg=      745nsec stddev=   173834nsec
1000 deviation samples of  1msec timer: min=  -222672nsec max=   228907nsec median=    63635nsec avg=       22nsec stddev=    29410nsec
1000 deviation samples of  5msec timer: min= -1302808nsec max=  1270006nsec median=  1251949nsec avg=     -222nsec stddev=   345944nsec
1000 deviation samples of 10msec timer: min= -1297724nsec max=  1298269nsec median=  1254351nsec avg=     -225nsec stddev=   374717nsec

O ran teste como um processo em tempo real sobre o Fedora 13 do kernel 2.6.34, a melhor precisão alcançada de 1 ms Timer foi avg = 22nsec stddev = 29410nsec.

Eu não tenho certeza que é a melhor solução, mas você pode considerar escrever um módulo do kernel pequena que usa os temporizadores do kernel de alto-rés fazer timing. Basicamente, você criar um arquivo de dispositivo para o qual lê só voltaria em intervalos de 1 ms.

Um exemplo deste tipo de abordagem é utilizada no asterisco PBX, através do módulo de ztdummy. Se você google para ztdummy pode encontrar o código que faz isso.

Eu acho que você vai ter problemas para alcançar a precisão de 1 ms com Linux padrão mesmo com a consulta constante no circuito principal, porque o kernel não garantir a sua aplicação terá CPU o tempo todo. Por exemplo, você pode ser colocado para dormir por dezenas de milissegundos por causa de multitarefa preemptiva e há pouco que você pode fazer sobre isso.

Você pode querer olhar para Real-Time Linux .

Se você está direcionando plataforma x86 você deve verificar temporizadores HPET. Esta é temporizador de hardware com grande precisão. Ele deve ser suportado pelo seu motherbord (agora todos eles apoiá-lo) e seu kernel deve contém driver para ele também. Eu usei-o algumas vezes sem problemas e foi capaz de conseguir muito melhor resolução do que 1ms.

Aqui está alguma documentação e exemplos:

Se bem me lembro obtendo resultados ok com gettimeofday / usleep polling baseado - Eu não estava precisando 1000 temporizadores um segundo ou nada, mas eu estava precisando de uma boa precisão com o timing para carrapatos eu fiz necessidade - meu aplicativo foi uma controlador de bateria eletrônica MIDI, e eu me lembro de ficar sub-milissegundo precisão, o que você precisa para uma máquina de tambor, se você não quer que ele soa como um mau baterista (esp contagem de MIDI embutido latências.) - IIRC (era 2005, de modo a minha memória é um pouco confuso) Eu estava ficando dentro de 200 microssegundos de vezes alvo com usleep.

No entanto, eu não estava correndo muito mais no sistema. Se você tem um ambiente controlado que você pode ser capaz de fugir com uma solução como essa. Se há mais coisas acontecendo no sistema (relógio cron disparar updatedb, etc.), então as coisas podem desmoronar.

Você está executando em um kernel Linux 2.4?

De VMware KB artigo # 1420 ( http://kb.vmware.com/kb/1420 ).

sistemas operacionais guest Linux manter tempo por interrupções de contagem do temporizador. Unpatched 2.4 e anteriores kernels programar o temporizador do sistema virtual para interrupções pedido do clock no 100Hz (100 interrupções por segundo). Kernels 2.6, por outro lado, solicitar interrupções em 1000Hz - dez vezes como muitas vezes. Alguns Kernels 2.4 modificados pela distribuição de fornecedores para conter 2,6 características também solicitar 1000Hz interrupções, ou em alguns casos, as interrupções em outras taxas, tal como 512Hz.

Em primeiro lugar, obter a fonte do kernel e compilá-lo com um parâmetro HZ ajustado.

  • Se HZ=1000, interrupções do timer 1000 vezes por segundo. É ok para usar HZ=1000 para uma máquina i386.
  • Em uma máquina incorporada, HZ pode ser limitada a 100 ou 200.

Para uma boa operação, opção PREEMPT_KERNEL deve estar ligado. tem kernels que não suporta essa opção corretamente. Você pode vê-los por pesquisa.

kernels recentes, ou seja 2.6.35.10, opções suportes NO_HZ, que se transforma em carrapatos dinâmicas. Isto significa que não haverá pulsos do timer quando em marcha lenta, mas uma marca de timer será gerada no momento especificado.

Há um remendo RT para o núcleo, mas o suporte de hardware é muito limitado.

Geralmente RTAI é um todo solução assassino para o seu problema, mas a sua suporte de hardware é muito limitado. No entanto, bons controladores CNC, como emc2, use RTAI para o seu clock, talvez 5000 Hz, mas pode ser trabalho duro para instalá-lo.

Se você pode, você pode adicionar hardware para gerar pulsos. Isso tornaria um sistema que pode ser adaptado a qualquer versão do sistema operacional.

Can-lhe pelo menos nanosleep uso em seu ciclo de sono para 1ms? Ou isso é uma coisa glibc?

Update: Não importa, eu ver a partir da página man "que pode levar até 10 ms mais longo do que o especificado até que o processo fica pronto a correr novamente"

Você não precisa de um RTOS para uma aplicação em tempo real simples. Todos os processadores modernos têm temporizadores de propósito geral. Obter uma folha de dados para qualquer CPU-alvo que você está trabalhando. Olhada no código fonte do kernel, sob o diretório arch você vai encontrar fonte específica processador de como lidar com esses temporizadores.

Existem duas abordagens que você pode tomar com este:

1) A sua aplicação só está executando o seu máquina de estado, e nada mais. Linux é simplesmente o "carregador de inicialização." Criar um objeto de kernel que instala um dispositivo de caractere. Na inserção no kernel, configurar o GP temporizador para ser executado continuamente. Você sabe que a frequência está a funcionar. Agora, no kernel, desativar explicitamente o seu cão de guarda. Agora desabilitar interrupções (hardware e software) em uma única CPU do kernel Linux, chamando spin_lock () vai conseguir isso (nunca deixar de ir la.) A CPU é SEU. circuito ocupado, verificando o valor do GPT até o # exigido de carrapatos se passaram, quando têm, definir um valor para o próximo tempo limite e digite o seu ciclo de processamento. Apenas certifique-se que o tempo de explosão para o seu código está sob 1ms

2) A segunda opção. Isso pressupõe que você está executando um kernel Linux de preferência. Configurar uma a GPT não utilizado ao lado de seu sistema operacional em execução. Agora, criar uma interrupção para disparar alguma margem configurável antes de seu 1ms tempo limite acontece (dizem 50-75 SU.) Quando os fogos de interrupção, você será imediatamente desativar interrupções e rotação de espera para a janela 1ms a ocorrer, em seguida, entrar em sua máquina de estado e, posteriormente, permitindo interrupções em seu esperar. Isso explica o fato de que você está cooperando com outras coisas no kernel que desabilitar interrupções. Isso pressupõe que não há nenhuma outra atividade kernel que bloqueia as interrupções por um longo tempo (mais de 100us.) Agora, você pode medir a precisão do seu evento de disparo e fazer a janela maior até que ele atenda sua necessidade.

Se em vez você está tentando aprender como o trabalho de RTOS ... ou se você está tentando resolver um problema de controle com mais de uma responsabilidade em tempo real ... em seguida, usar um RTOS.

Que tal usar "/ dev / rtc0" (ou "/ dev / rtc") do dispositivo e sua interface relacionada ioctl ()? Eu acho que ele oferece um contador temporizador preciso. Não é possível definir a taxa de apenas a 1 ms, mas para um valor próximo ou 1 / 1024sec (1024Hz), ou para uma frequência mais elevada, como 8192Hz.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top