O tempo UNIX é medido no seu computador, executando o UNIX.
Esta resposta espera que você saiba o que são o Tempo Universal Co-ordenado (UTC), o Tempo Atômico Internacional (TAI) e o segundo SI. Explicá-los está muito além do escopo do Unix e Linux Stack Exchange. Esta não é a troca de pilhas de física ou astronomia.
O hardware
Seu computador contém vários osciladores que acionam relógios e cronômetros. Exatamente o que tem varia de computador para computador, dependendo de sua arquitetura. Mas geralmente, e em termos muito gerais:
- Existe um temporizador de intervalo programável (PIT) em algum lugar, que pode ser programado para contar um determinado número de oscilações e acionar uma interrupção na unidade central de processamento.
- Existe um contador de ciclos no processador central que simplesmente conta 1 para cada ciclo de instruções executado.
A teoria da operação, em termos muito amplos
O kernel do sistema operacional utiliza o PIT para gerar ticks . Ele configura o PIT para execução livre, contando o número certo de oscilações por um intervalo de tempo de, digamos, um centésimo de segundo, gerando uma interrupção e redefinindo automaticamente a contagem para continuar novamente. Existem variações nisso, mas, em essência, isso faz com que uma interrupção de escala seja aumentada com uma frequência fixa.
No software, o kernel incrementa um contador a cada tick. Ele conhece a frequência do tick, porque programou o PIT em primeiro lugar. Portanto, ele sabe quantos ticks compõem um segundo. Pode ser usado para saber quando incrementar um contador que conta segundos. Este último é a idéia do kernel de "UNIX Time". De fato, ele simplesmente conta para cima na taxa de um por segundo de SI, se for deixado para seus próprios dispositivos.
Quatro coisas complicam isso, que vou apresentar em termos muito gerais.
O hardware não é perfeito. Um PIT cuja folha de dados diz que possui uma frequência de oscilador de N Hertz pode ter uma frequência de (digamos) N .00002 Hertz, com as conseqüências óbvias.
Esse esquema interage muito mal com o gerenciamento de energia, porque a CPU está acordando centenas de vezes por segundo para fazer pouco mais do que incrementar um número em uma variável. Portanto, alguns sistemas operacionais têm o que chamamos de design "sem marcação". Em vez de fazer com que o PIT envie uma interrupção para cada tick, o kernel calcula (a partir do agendador de baixo nível) quantos ticks vão passar sem quanta de threads esgotando e programa o PIT para contar para esse ticks no futuro antes de emitir uma interrupção de escala. Ele sabe que precisa registrar a passagem de N ticks na próxima interrupção, em vez de 1 tick.
O software de aplicativo tem a capacidade de alterar o horário atual do kernel. Ele pode aumentar o valor ou reduzir o valor. O giro envolve o ajuste do número de ticks que precisam passar para aumentar o contador de segundos. Assim, os segundos contador não necessariamente contar à taxa de um por segundo SI qualquer maneira , mesmo assumindo osciladores perfeitos. O passo envolve simplesmente escrever um novo número no contador de segundos, o que geralmente não acontece até 1 segundo de SI desde o último segundo.
Os kernels modernos não apenas contam segundos, mas também nanossegundos. Mas é ridículo e muitas vezes completamente inviável ter uma interrupção de uma vez por nanossegundo. É aqui que coisas como o contador de ciclos entram em cena. O kernel lembra o valor do contador de ciclos a cada segundo (ou a cada tick) e pode calcular, a partir do valor atual do contador, quando algo quer saber o tempo em nanossegundos, quantos nanossegundos devem ter decorrido desde o último segundo (ou Carraça). Mais uma vez, porém, o gerenciamento de energia e térmica causa estragos nisso, pois a frequência do ciclo de instruções pode mudar, de modo que os kernels fazem coisas como confiar em hardware adicional, como (digamos) um HPET (Temporizador de Eventos de Alta Precisão).
A linguagem C e POSIX
A biblioteca padrão da linguagem C descreve o tempo em termos de um tipo, opaco time_t
, um tipo de estrutura tm
com vários campos especificados, e várias funções da biblioteca como time()
, mktime()
e localtime()
.
Em resumo: a própria linguagem C apenas garante que esse time_t
é um dos tipos de dados numéricos disponíveis e que a única maneira confiável de calcular diferenças de tempo é a difftime()
função. É o padrão POSIX que fornece garantias mais rigorosas, que time_t
é de fato um dos tipos inteiros e conta em segundos desde a época . É também o padrão POSIX que especifica o timespec
tipo de estrutura.
A time()
função é por vezes descrito como uma chamada de sistema. De fato, não é a chamada subjacente ao sistema em muitos sistemas há bastante tempo, atualmente. No FreeBSD, por exemplo, a chamada do sistema subjacente é clock_gettime()
, que possui vários "relógios" disponíveis que medem em segundos ou segundos + nanossegundos de várias maneiras. É essa chamada de sistema pela qual o software de aplicativos lê o UNIX Time no kernel. (Uma clock_settime()
chamada do sistema correspondente permite que eles o sigam e uma adjtime()
chamada do sistema permite que eles o matem.)
Muitas pessoas acenam com o padrão POSIX com afirmações muito definidas e exatas sobre o que ele prescreve. Essas pessoas, na maioria das vezes, na verdade não leem o padrão POSIX. Como sua lógica expõe, a idéia de contar "segundos desde a época", que é a frase que o padrão usa, intencionalmente não especifica que os segundos POSIX têm a mesma duração que os segundos SI, nem que o resultado de gmtime()
"é necessariamente" UTC, apesar de sua aparência ". O padrão POSIX é intencionalmentesolte o suficiente para permitir (digamos) um sistema UNIX para o qual o administrador vá e conserte manualmente os ajustes de segundos após o reajuste do relógio na semana seguinte. De fato, o raciocínio indica que ele é intencionalmente frouxo o suficiente para acomodar sistemas em que o relógio foi deliberadamente acertado em algum momento diferente do horário atual do UTC.
UTC e TAI
A interpretação do UNIX Time obtida do kernel depende das rotinas da biblioteca em execução nos aplicativos. POSIX especifica uma identidade entre o tempo do kernel e um "tempo de quebra" em um arquivo struct tm
. Mas, como Daniel J. Bernstein apontou certa vez, a edição de 1997 da norma errou embaraçosamente essa identidade, atrapalhando a regra do ano bissexto do calendário gregoriano (algo que as crianças em idade escolar aprendem) para que o cálculo estivesse errado a partir do ano 2100 em diante. "Mais honrado na violação do que na observância" é uma frase que vem prontamente à mente.
E de fato é. Atualmente, vários sistemas baseiam essa interpretação em rotinas de bibliotecas escritas por Arthur David Olson, que consultam o infame "banco de dados de fuso horário Olson", geralmente codificado nos arquivos de banco de dados abaixo /usr/share/zoneinfo/
. O sistema Olson tinha dois modos:
- Os "segundos desde a época" do kernel são considerados como segundos UTC desde 1970-01-01 00:00:00 UTC, exceto por segundos bissextos. Isso usa o
posix/
conjunto de arquivos de banco de dados do fuso horário da Olson. Todos os dias têm 86400 segundos de kernel e nunca há 61 segundos em um minuto, mas eles nem sempre têm a duração de um segundo de SI e o relógio do kernel precisa ser girado ou pisado quando ocorrem segundos bissextos.
- Considera-se que os "segundos desde a época" do kernel contam segundos TAI desde TAI 1970-01-01 00:00:10. Isso usa o
right/
conjunto de arquivos de banco de dados do fuso horário da Olson. Os segundos do kernel têm 1 SI segundo de duração e o relógio do kernel nunca precisa ser girado ou pisado para ajustar os segundos bissextos, mas os tempos de quebra podem ter valores como 23:59:60 e os dias nem sempre são 86400 segundos do kernel.
M. Bernstein escreveu várias ferramentas, incluindo o seu daemontools
conjunto de ferramentas, que right/
eram necessárias porque simplesmente adicionavam 10 time_t
para obter segundos TAI desde 1970-01-01 00:00:00 TAI. Ele documentou isso na página de manual.
Esse requisito foi (talvez sem o saber) herdado por conjuntos de ferramentas como daemontools-encore
e runit
e por Felix von Leitner libowfat
. Use Bernsteinmultilog
, Guentermultilog
ou Papesvlogd
com uma posix/
configuração Olson , por exemplo, e todos os carimbos de data e hora TAI64N estarão (no momento em que foram escritos) 26 segundos atrás da contagem real de segundos TAI desde 1970-01-01 00:00:10 TAI.
Laurent Bercot e eu abordamos isso em s6 e nosh, embora tenhamos adotado abordagens diferentes. M. Bercot tai_from_sysclock()
depende de um sinalizador em tempo de compilação. as ferramentas de nosh que tratam do TAI64N examinam as variáveis de ambiente TZ
e TZDIR
para detectar automaticamente posix/
e right/
se puderem.
Curiosamente, documentos time2posix()
e posix2time()
funções do FreeBSD que permitem o equivalente ao right/
modo Olson com time_t
segundos TAI. Aparentemente, eles não estão ativados.
De novo…
O tempo UNIX é medido no seu computador executando o UNIX, por osciladores contidos no hardware do seu computador. Ele não usa segundos SI; não é UTC, embora possa parecer superficialmente; e intencionalmente permite que seu relógio esteja errado.
Leitura adicional