Questão
Eu gostaria de poder executar um comando UNIX precisamente a cada segundo durante um longo período de tempo .
Preciso de uma solução, que não fique para trás depois de um certo tempo, devido ao tempo que o próprio comando precisa para execução. sleep , watch e um certo script python falharam comigo nesse aspecto.
No microcontrolador como o http://Arduino.cc, eu faria isso através de interrupções no relógio do hardware. Gostaria de saber se existe uma solução semelhante de script de shell com precisão de tempo. Todas as soluções que encontrei no StackExchange.com resultaram em um atraso de tempo notável, se executado durante horas. Veja os detalhes abaixo.
Finalidade prática / aplicação
Desejo testar se minha conexão de rede está continuamente ativada enviando registros de data e hora via nc
(netcat) a cada 1 segundo.
Remetente:
precise-timestamp-generator | tee netcat-sender.txt | nc $receiver $port
Receptor:
nc -l -p $port > netcat-receiver.txt
Após a conclusão, compare os dois logs:
diff netcat-sender.txt netcat-receiver.txt
As diferenças seriam os carimbos de hora não transmitidos. A partir disso, eu saberia a que horas minha LAN / WAN / ISP causa problemas.
Solução SLEEP
while [ true ]; do date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done | tee timelog-sleep.txt
Obtém um certo deslocamento ao longo do tempo, pois o comando dentro do loop também leva um pouco de tempo.
Precisão
cat timelog-sleep.txt
2012-07-16 00:45:16
[...]
2012-07-16 10:20:36
Segundos decorridos: 34520
wc -l timelog-sleep.txt
Linhas no arquivo: 34243
Precisão resumida:
- 34520-34243 = 277 problemas de temporização
- 34520/34243 = 1,008 = 0,8% de desconto
Solução REPETIR PYTHON
Encontrado em: Repita um comando Unix a cada x segundos para sempre
repeat.py 1 "date '+%Y-%m-%d %H:%M:%S'" >> timelog-repeat-py.txt
Supõe-se evitar o deslocamento de tempo, mas falha ao fazê-lo.
Precisão
wc -l timelog-repeat-py.txt
2012-07-16 13:42:44
[...]
2012-07-16 16:45:24
Segundos decorridos: 10960
wc -l timelog-repeat-py.txt
Linhas no arquivo: 10859
Precisão resumida:
- 10960-10859 = 101 problemas de temporização
- 10960/10859 = 1.009 = 0.9% de desconto
Solução RELÓGIO
watch -n 1 "date '+%Y-%m-%d %H:%M:%S' >> ~/Desktop/timelog-watch.txt"
Precisão
wc -l timelog-watch.txt
2012-07-16 11:04:08
[...]
2012-07-16 13:25:47
Segundos decorridos: 8499
wc -l timelog-watch.txt
Linhas no arquivo: 8366
Precisão resumida:
- 8499-8366 = 133 problemas de temporização.
- 8499/8366 = 1,016 = 1,6% de desconto.
nice
dorme no processo?Respostas:
Como esse script Perl que acabei de criar funciona?
Usar:
perl timer.pl 1 date '+%Y-%m-%d %H:%M:%S'
Ele está em execução 45 minutos sem um único salto, e suspeito que continuará a fazê-lo, a menos que a) a carga do sistema fique tão alta que o fork () demore mais de um segundo ou b) um segundo de salto seja inserido.
Não é possível garantir, no entanto, que o comando seja executado em intervalos precisos de segundos, pois há alguma sobrecarga, mas duvido que seja muito pior do que uma solução baseada em interrupção.
Eu o executei por cerca de uma hora com
date +%N
(nanossegundos, extensão GNU) e executei algumas estatísticas nele. O maior atraso foi de 1 155 microssegundos. Média (média aritmética) 216 µs, mediana 219 µs, desvio padrão 42 µs. Ele rodava mais rápido que 270 µs 95% do tempo. Eu não acho que você pode vencê-lo, exceto por um programa em C.fonte
GNU date
com+%N
apenas 3 minutos, ocorreu o seguinte erro:Time::HiRes::sleep(-0.00615549): negative time not invented yet at ~/bin/repeat.pl line 23.
Linha 23 no meu script salvo:sleep $start - time();
A
ualarm()
função POSIX permite agendar o kernel para sinalizar periodicamente seu processo, com precisão de microssegundos.Crie um programa simples:
Compilar
Em seguida, anexe-o ao que você precisar fazer periodicamente da seguinte maneira:
fonte
-std=c99
, você não receberá o aviso sobre o retorno ausente. Caso contrário, você não deve precisar de nada de especial. Você digitou errado um zero extra?strace ./tick
irá mostrar-lhe o que está fazendo de uma perspectiva syscallVocê já tentou
watch
com o parâmetro--precise
?Na página do manual:
O parâmetro pode não estar disponível no seu sistema.
Você também deve considerar o que deve acontecer quando a execução do seu programa precisar de mais de um segundo. A próxima execução agendada deve ser ignorada ou atrasada?
Atualização : executei o script por algum tempo e ele não perdeu uma única etapa:
Atualização: A
--precise
flag é uma adição do Debian, mas o patch é bastante simples: http://patch-tracker.debian.org/patch/series/view/procps/1:3.2.8-9squeeze1/watch_precision_time.patchfonte
watch
suporta essa opção? Não estava em nenhuma das máquinas que verifiquei.--precise
. Esta é uma adição Debian (3.2.8-9, watch_precision_time.patch)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
O material em tempo real (kernel etc.) está disponível por um motivo!crontab
tem uma resolução de 1 minuto. Se você estiver bem com o tempo de atraso acumulado por esse minuto e redefinido no próximo minuto, essa ideia básica poderá funcionar:Observe que
script.sh
também é executado em segundo plano. Isso deve ajudar a minimizar o atraso acumulado a cada iteração do loop.Dependendo de quanto atraso é
sleep
gerado, existe a chance de o segundo 59 se sobrepor ao segundo 0 do minuto seguinte.EDIT para lançar em alguns resultados, no mesmo formato da pergunta:
1 hora 52 minutos = 6720 segundos
0 problemas de tempo, 0% de desconto. Qualquer acumulação de tempo é redefinida a cada minuto.
fonte
cron
seja preciso o segundo, mas não é o caso em geral.Seu problema é que você dorme por um período fixo de tempo após a execução do programa sem levar em consideração a quantidade de tempo decorrido desde a última vez em que dormiu.
Você pode fazer isso com bash ou qualquer outra linguagem de programação, mas a chave é usar o relógio para determinar quanto tempo agendar a próxima suspensão. Antes de dormir, verifique o relógio, veja quanto tempo resta e durma a diferença.
Devido a compromissos de agendamento de processos, não é garantido que você acorde imediatamente, mas você deve estar bastante próximo (dentro de alguns ms descarregados ou dentro de algumas centenas de ms carregados). E você não acumulará erros ao longo do tempo, pois cada vez que estiver sincronizando novamente a cada ciclo de suspensão e removendo qualquer erro acumulado.
Se você precisa acertar o relógio exatamente, então o que você procura é um sistema operacional em tempo real , projetado exatamente para esse fim.
fonte
date +%S.%N
para obter o número de segundos com precisão de segundos eusleep
para dormir com precisão de segundos, mas depois disso é apenas uma questão de matemática.Eu sempre desisti de fazer algo correr exatamente no intervalo. Acho que você precisará escrever um programa em C e prestar muita atenção para não exceder a parte do intervalo de 1 segundo com seu próprio código. Você provavelmente terá que usar processos de intercomunicação ou múltiplos threads para que isso funcione. Tome cuidado para evitar sobrecarga no tempo de início do encadeamento ou do processo.
Uma referência que parece datas relevantes para 1993: A Randomized Sampling clock para utilização da CPU Estimativa e Código Profiling Você vai querer dar uma olhada no apêndice "Adversário Source Code" para ver como eles mediram com precisão intervalos de tempo, e "acordou" seu programa no momento correto. Como o código tem 19 anos, provavelmente não será portado direta ou facilmente, mas se você o ler e tentar entendê-lo, os princípios envolvidos poderão orientar seu código.
Edição: Encontrou outra referência que pode ajudar: Efeitos da resolução do relógio no agendamento de processos interativos e flexíveis em tempo real que devem ajudá-lo com qualquer embasamento teórico.
fonte
Dê uma olhada em nanosleep () (em http://linux.about.com/library/cmd/blcmdl2_nanosleep.htm ). Em vez de fazer seu programa dormir 1 segundo, faça-o dormir (1 - quanto tempo demora para ser executado) segundos. Você terá uma resolução muito melhor.
fonte
sleep
, ou sejasleep 0.99
. O problema é que a quantidade de tempo que leva para executar está longe de ser constante, até mesmo seu valor médio pode variar ao longo do tempo.Tente executar seu comando em segundo plano para que isso não afete tanto o tempo do loop, mas mesmo isso não será suficiente se você não quiser acumular por longos períodos, pois certamente há um custo de alguns milissegundos associado a ele.
Portanto, isso provavelmente é melhor, mas também provavelmente ainda não é bom o suficiente:
No meu computador, isso causou 2 erros em 20 minutos ou 0,1 por minuto, o que representa aproximadamente uma melhoria de cinco vezes na sua execução.
fonte
sleep 1
é que é garantido que você durma pelo menos um segundo - nunca menos. Portanto, o erro se acumula.Feio, mas funciona. Você provavelmente deve repensar o design do seu programa se precisar de um loop como este. Ele basicamente verifica se o segundo inteiro atual é igual ao anterior verificado e imprime o número de nanossegundos desde a mudança do segundo. A precisão é influenciada pelo sono 0,001.
A precisão está em milissegundos, desde que a 'carga útil'
date "+%N nanoseconds late"
não demore mais do que pouco menos de um segundo. Você pode diminuir a carga da CPU aumentando o período de suspensão ou, se realmente não se importa, substitua o comando de suspensão portrue
.Essa é uma prática ruim, porque você basicamente faz uma pesquisa de CPU para um evento e está desperdiçando ciclos de CPU. Você provavelmente deseja se conectar a uma interrupção do timer (não é possível a partir do bash) ou usar hardware dedicado como um microcontrolador. Um PC e seu sistema operacional não foram projetados para oferecer alta precisão de tempo.
fonte
Outro método seria usar uma suspensão em um loop e enviar SIGCONT a partir de um programa externo preciso. Enviar um sinal é muito leve e terá muito menos latência do que executar algo. Você também pode pré-enfileirar vários comandos com o comando "at", quase ninguém usa "at" mais, não tenho certeza de quão preciso é.
Se a precisão é crítica e você quer levar isso a sério, isso soa como o tipo de aplicativo em que você normalmente usaria o RTOS, o que poderia ser feito no Linux com o kernel corrigido pelo RT-Preempt, que fornecerá a precisão e alguma medida de controle de interrupção, mas pode ser mais incômodo do que vale a pena.
https://rt.wiki.kernel.org/index.php/RT_PREEMPT_HOWTO
O Xenomai também pode ser útil, é uma implementação RTOS completa e é portado para x86 e x86_64, mas há alguma programação envolvida.
http://www.xenomai.org/index.php/Main_Page
fonte
With
ksh93
(que tem um ponto flutuante$SECONDS
e um builtinsleep
)O mesmo script também funcionará
zsh
, mas invocará osleep
comando do sistema .zsh
possui umzselect
builtin, mas apenas com resolução de 1/100.fonte
Eu iria com um pequeno programa C:
Este programa espera que o programa chame com caminho completo como seu primeiro argumento e transmita os argumentos restantes. Ele não aguardará a conclusão do comando, portanto, iniciará várias instâncias.
Além disso, o estilo de codificação aqui é realmente desleixado e são feitas várias suposições que podem ou não ser garantidas pelos padrões aplicáveis, ou seja, a qualidade desse código é "funciona para mim".
Este programa terá intervalos um pouco mais longos ou mais curtos quando o relógio é ajustado pelo NTP ou manualmente. Se o programa resolver isso, o POSIX fornece o
timer_create(CLOCK_MONOTONIC, ...)
que não é afetado por isso.fonte
Você deve acompanhar a hora atual e compará-la com a hora de início. Portanto, você dorme uma quantidade calculada de tempo a cada iteração, não uma quantidade fixa. Dessa maneira, você não acumulará erros de tempo e se afastará de onde deveria estar, pois redefine seus tempos em cada loop para o tempo absoluto desde o início.
Além disso, algumas funções de sono retornam mais cedo se houver uma interrupção, portanto, nesse caso, você precisará chamar seu método de sono novamente até que todo o tempo tenha passado.
fonte
Aqui está um script bash e altamente preciso. Ele usa usleep para precisão de microssegundos.
http://wiki.junkemailfilter.com/index.php/How_to_run_a_Linux_script_every_few_seconds_under_cron
Existem 3 scripts lá. Olhe para o fundo. Realizará cerca de 2400 execuções por minuto com precisão razoável. E é realmente simples.
fonte
Este pode ser executado pelo menos 100 vezes por segundo com resolução muito precisa.
A existência do diretório do número de loops por minuto cria a programação. Esta versão suporta resolução de microssegundos, assumindo que o seu computador possa lidar com isso. O número de execuções por minuto não precisa ser igualmente divisível por 60 nem é limitado a 60. Testei para 6000 e funciona.
Esta versão pode ser instalada no diretório /etc/init.d e executada como um serviço.
fonte