Obrigado por seus comentários! Eu gostaria de atualizar a hora atual para conectar-se como ngx_time_update(). Eu mudei meu exemplo para usar apenas CLOCK_REALTIME, mas sempre cerca de 400 microssegundos atrasados. Github.com/hnakamur/iorn/commit / … Isso significa que este clock_gettime leva cerca de 400 nanossegundos na minha máquina?

Sim, apenas soa, de alguma forma. Mas, se você estiver em um x86 PC no Linux, 400 NS para o clock_gettime Overheads pode ser um pouco alto (ordem de grandeza superior – ver abaixo). Se você estiver usando um processador arm (por exemplo, Raspberry Pi, nvidia jetson), isso pode estar correto.

Eu não sei como você recebe 400 microssegundos. Mas, eu tive que fazer muitas coisas em tempo real no Linux, e 400 é semelhante ao que eu medi como uma sobrecarga para fazer uma mudança de contexto e / ou acordar um processo / linha após um syscall a suspensão.

Eu uso gettimeofday mais. Eu estou usando apenas clock_gettime(CLOCK_REALTIME,...) porque é o mesmo, exceto que você recebe nanosegundos em vez de microssegundos.

só para você saber, embora seja clock_gettime é uma chamada do sistema, hoje em dia, na maioria dos sistemas, usa a camada VDSO. O núcleo injetar o código especial na aplicação do espaço do usuário, para que ele possa acessar diretamente no prazo sem a sobrecarga de um syscall.

Se você é interessado, você pode ser executado sob gdb e desmonte o código para ver que ele simplesmente acessa alguns locais de memória especiais em vez de fazer uma chamada de sistema.

Eu não acho Você tem muito a se preocupar com isso. Use o clock_gettime(CLOCK_MONOTONIC,...) e defina-o flags Fora de 0. As despesas gerais não são levadas em conta, para fins da Ligam, porque o seu iorn Layer usa.

Quando eu faço esse tipo de coisa, e que eu quero / preciso Calcule o custo extra de clock_gettime Chame clock_gettime loop (por exemplo 1000 vezes), e tento manter o tempo total abaixo de uma fatia de tempo. Eu uso a diferença mínima entre os tempos em cada iteração. Isso compensa a qualquer momento o corte.

O mínimo é o custo extra da própria chamada.

Existem outras dicas que você pode fazer para minimizar a latência no espaço do usuário (por exemplo, Aumentar a prioridade do processo, bloqueia a afinidade da afinidade da CPU e a interrupção de E / S), mas eles podem envolver algumas outras coisas, e se você não tiver muito cuidado, eles podem produzir resultados piores.

antes de começar a tomar Ação extraordinária, você deve ter uma metodologia sólida para o cronograma de análise de medição / comparativa para provar que seus resultados podem não responder aos seus requisitos de calendário / débito / latência. Caso contrário, você faz coisas complicadas sem qualquer vantagem real / mensurável / necessária.

abaixo, um código que acabei de criar, simplificado, mas com base no código que eu já / usado para calibrar sobrecarga:

#include <stdio.h>#include <time.h>#define ITERMAX 10000typedef long long tsc_t;// tscget -- get time in nanosecondsstatic inline tsc_ttscget(void){ struct timespec ts; tsc_t tsc; clock_gettime(CLOCK_MONOTONIC,&ts); tsc = ts.tv_sec; tsc *= 1000000000; tsc += ts.tv_nsec; return tsc;}// tscsec -- convert nanoseconds to fractional secondsdoubletscsec(tsc_t tsc){ double sec; sec = tsc; sec /= 1e9; return sec;}tsc_tcalibrate(void){ tsc_t tscbeg; tsc_t tscold; tsc_t tscnow; tsc_t tscdif; tsc_t tscmin; int iter; tscmin = 1LL << 62; tscbeg = tscget(); tscold = tscbeg; for (iter = ITERMAX; iter > 0; --iter) { tscnow = tscget(); tscdif = tscnow - tscold; if (tscdif < tscmin) tscmin = tscdif; tscold = tscnow; } tscdif = tscnow - tscbeg; printf("MIN:%.9f TOT:%.9f AVG:%.9f\n", tscsec(tscmin),tscsec(tscdif),tscsec(tscnow - tscbeg) / ITERMAX); return tscmin;}intmain(void){ calibrate(); return 0;}

no meu sistema, um núcleo i7 a 2,67 GHz, a saída é:

MIN:0.000000019 TOT:0.000254999 AVG:0.000000025

Então, recebo 25 ns de despesas gerais. Mas, novamente, cada sistema pode ser diferente de certa medida.

Observe que os processadores x86 têm uma “velocidade” . O sistema operacional pode aumentar ou diminuir a frequência do processador semi-automaticamente. Velocidades mais baixas economizam energia. Velocidades mais altas são o desempenho máximo.

Isso é feito com uma heurística (por exemplo, se o sistema operacional detectar que o processo é um usuário grande do processador, ele aumentará a velocidade).

Para forçar a velocidade máxima, Linux a este diretório:

/sys/devices/system/cpu/cpuN/cpufreq

Onde N é o número do CPU (por exemplo 0-7)

Neste diretório, há vários arquivos de interesse. Eles devem ser explícitos.

Em particular, olhe para scaling_governor. Ele tem ondemand ou performance

para forçar a velocidade máxima, como root, definir isso em performance (por exemplo):

echo "performance" > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

faça isso para todos os processadores.

No entanto, eu acabei de fazer isso no meu sistema, e tinha pouco efeito. Assim, o kernel heurístico pode ter melhorado.

Quanto ao 400US, quando um processo espera algo, quando é “desperto”, é um processo em duas etapas.

O processo é Marcado “executável”.

Em um determinado momento, o sistema / CPU executa uma replanização. O processo será executado de acordo com a política de planejamento e prioridade no processo.

Para muitas chamadas do sistema, a replanagem ocorre durante o próximo temporizador / relógio / interrupção do sistema. Assim, para alguns, pode haver um atraso que pode subir a um relógio completo (isto é) para um valor de 1000, isso pode subir até 1 ms (1000 nós) mais tarde.

Em média, isso representa metade ou 500 US.

Para algumas chamadas do sistema, quando o processo é marcado como executável, uma replanning é executada imediatamente. Se o processo tiver uma prioridade mais alta, ele será executado imediatamente.

Quando eu olhei para isso pela primeira vez, assisti a todos os caminhos de código no kernel, e a única chamada do sistema que fez o imediato Replanning foi SYSV IPC, para msgsnd/msgrcv. Em outras palavras, quando o processo fez msgsnd, qualquer processo B espera que a mensagem determinada seja executada.

Mas outros não (por exemplo, futex). Eles esperariam pelo temporizador. Muitas coisas mudaram desde então, e agora, mais syscalls farão adiamento imediato. Por exemplo, eu medi recentemente futex e parecia fazer a rápida replanning.

Além disso, o planejador do kernel foi alterado. O novo agendador pode acordar / executar certas coisas em uma fração do relógio. Então, para você, os 400 nós, é o alinhamento no próximo relógio.

Mas, pode simplesmente sobrecarregar para fazer a chamada do sistema. Para testar isso, eu mudei meu programa de teste para abrir /dev/null e adicionado read(fd,buf,1) no loop de teste.

Eu tenho um valor MIN: de 529 EUA. Assim, o atraso que você pode ser simplesmente ser o tempo necessário para fazer a mudança de emprego.

Isso é o que eu chamaria de “muito bem por enquanto”.

para obter a resposta “Razor “Você provavelmente terá que escrever um driver principal personalizado e fazê-lo. Isto é o que os sistemas embarcados fariam se eles deviam (por exemplo) para alternar um pino GPIO em cada intervalo.

Mas, se tudo o que você fizer é , a sobrecarga e o subjacente tem write(1,...) tendência para submergir o atraso real.

também note que quando você faz , ele cria o buffer de saída e quando o buffer FILE *stdout está cheio, É esvaziado via write

para melhor desempenho, é melhor fazer int len = sprintf(buf,"current time is ..."); write(1,buf,len); Além disso, quando você Faça isso, se os buffers do kernel para I / O tty são preenchidos, o processo será suspenso até que a E / S foi enviada para o dispositivo TTY.

Para fazer isso, você deve monitorar o espaço disponível e ignorar algumas mensagens se não houver espaço suficiente para contê-los.

você deve fazer : ioctl(1,TIOCOUTQ,...) para obter o espaço disponível e ignorar algumas mensagens, se for menor do que o tamanho da mensagem que você deseja gerar (por exemplo, o len Valor acima).

Para o seu uso, você provavelmente está mais interessado na mensagem da última hora, em vez de obter todas as mensagens

Leave a comment

O seu endereço de email não será publicado. Campos obrigatórios marcados com *