(L'environnement d'exécution du texte ci-dessous est ubuntu 18.04. Le code source fait référence à la version amont de git, qui est la même version que le package ubuntu 18.04, pour la liaison.)
Si vous regardez les horodatages après la mise à jour du fichier, il se peut qu'il soit légèrement passé.
$ date +%H:%M:%S.%N ; touch file ; date +%H:%M:%S.%N ; stat --printf="%y\n" file
XX:46:44.546008239 ← 1
XX:46:44.550890822 ← 2
20XX-XX-XX XX:46:44.543994035 +0900 ← 3
Puisque vous touchez le fichier entre 1 et 2, l'horodatage 3 doit être une heure entre 1 et 2, mais si vous regardez de près, 3 est environ 2 ms avant 1.
La commande date n'émet pas d'appel système pour obtenir l'heure même si strace est appliqué. C'est parce que le temps est calculé sans appeler le noyau par un mécanisme appelé vsyscall, donc on peut considérer que l'appel système clock_gettime (2)
est effectivement utilisé. Code source de la commande de dateQue,
/* Prepare to print the current date/time. */
gettime (&when);
gettime ()
est [défini] dans gnulib (Bibliothèque de portabilité GNU) (http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob;f=lib/gettime .c; h = 4ae313e78ea5b90475fa7ab1fd0c2479a3410d09; hb = a20922f10).
void
gettime (struct timespec *ts)
{
#if HAVE_NANOTIME
nanotime (ts);
#else
# if defined CLOCK_REALTIME && HAVE_CLOCK_GETTIME
if (clock_gettime (CLOCK_REALTIME, ts) == 0)
return;
# endif
{
struct timeval tv;
gettimeofday (&tv, NULL);
ts->tv_sec = tv.tv_sec;
ts->tv_nsec = tv.tv_usec * 1000;
}
#endif
}
# if
adopte la ligne du milieu clock_gettime
.
D'un autre côté, le toucher peut prendre du rythme normalement.
$ strace touch file
...
openat(AT_FDCWD, "file", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666) = 3
dup2(3, 0) = 0
close(3) = 0
utimensat(0, NULL, NULL, 0) = 0
close(0) = 0
...
J'utilise un appel système appelé ʻutimensat (2) `.
ʻUtimensat (2) est [défini] dans le fichier
fs / utimes.c` (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git /tree/fs/utimes.c?h=v4.15&id=d8a5b80568a9cb66810e75b182018e9edb68e8ff#n168).
SYSCALL_DEFINE4(utimensat, int, dfd, const char __user *, filename,
struct timespec __user *, utimes, int, flags)
{
Abréviation
}
A partir de maintenant, depuis do_utimes ()
→ ʻutimes_common () ,
notify_change () ʻin fs / attr.c est [appelé](https://git.kernel.org/pub/scm/linux/kernel/ git / torvalds / linux.git / tree / fs / attr.c? h = v4.15 & id = d8a5b80568a9cb66810e75b182018e9edb68e8ff # n205).
Lors du réglage à l'heure actuelleattr->ia_valid
EstATTR_CTIME | ATTR_MTIME | ATTR_ATIME | ATTR_TOUCH
Il est devenu.notify_change()
Estファイルの各種属性の変更をする関数だが、属性変更時はctime、mtime、atimeを現在時刻に変更する普通であるので、そのような処理Il est devenu.
L'heure actuelle est [obtenue] par la fonction current_time ()
(https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/attr .c? h = v4.15 & id = d8a5b80568a9cb66810e75b182018e9edb68e8ff # n242).
current_time ()
est défini dans fs / inode.c et [does](https: //) pour arrondir le résultat de current_kernel_time ()
à la précision de l'horodatage dépendant du système de fichiers. git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/inode.c?h=v4.15&id=d8a5b80568a9cb66810e75b182018e9edb68e8ff#n2116). Dans ext4, l'horodatage peut être conservé en unités ns, il ne sera donc pas arrondi ici.
current_kernel_time ()
passe par quelques fonctions wrapper et enfin les fonctions suivantes /tree/kernel/time/timekeeping.c?h=v4.15&id=d8a5b80568a9cb66810e75b182018e9edb68e8ff#n2190).
struct timespec64 current_kernel_time64(void)
{
struct timekeeper *tk = &tk_core.timekeeper;
struct timespec64 now;
unsigned long seq;
do {
seq = read_seqcount_begin(&tk_core.seq);
now = tk_xtime(tk);
} while (read_seqcount_retry(&tk_core.seq, seq));
return now;
}
Extension de la fonction en ligne tk_xtime ()
struct timespec64 current_kernel_time64(void)
{
struct timekeeper *tk = &tk_core.timekeeper;
struct timespec64 now;
unsigned long seq;
do {
seq = read_seqcount_begin(&tk_core.seq);
now.tv_sec = tk->xtime_sec;
now.tv_nsec = (long)(tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift);
} while (read_seqcount_retry(&tk_core.seq, seq));
return now;
}
La boucle de read_seqcount_begin ()
à read_seqcount_retry ()
est une sorte de cliché pour vous empêcher de récupérer des données à moitié finies lors de la mise à jour.
Ensuite, pour clock_gettime ()
, [kernel / time / posix-stubs.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/ tree / kernel / time / posix-stubs.c? h = v4.15 & id = d8a5b80568a9cb66810e75b182018e9edb68e8ff # n82). C'est enfin
Ce qui suit [__getnstimeofday64](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/time/timekeeping.c?h=v4.15&id=d8a5b80568a9cb66810e75b182018e9edb Venez au n713).
int __getnstimeofday64(struct timespec64 *ts)
{
struct timekeeper *tk = &tk_core.timekeeper;
unsigned long seq;
u64 nsecs;
do {
seq = read_seqcount_begin(&tk_core.seq);
ts->tv_sec = tk->xtime_sec;
nsecs = timekeeping_get_ns(&tk->tkr_mono);
} while (read_seqcount_retry(&tk_core.seq, seq));
ts->tv_nsec = 0;
timespec64_add_ns(ts, nsecs);
/*
* Do not bail out early, in case there were callers still using
* the value, even in the face of the WARN_ON.
*/
if (unlikely(timekeeping_suspended))
return -EAGAIN;
return 0;
}
En comparant les deux, vous pouvez voir qu'il y a une différence dans la partie tv_nsec. La commande tactile ʻutimensat () traite la variable
tk-> tkr_mono.xtime_nsec, tandis que la commande de date
clock_gettime (CLOCK_REALTIME) utilise le résultat de
timekeeping_get_ns ()et déborde. Si c'est le cas, je fais quelque chose comme la modification de tv_sec. Lorsque vous appelez
timekeeping_get_ns ()`, vous appelez clocksource.
Les horodatages sont uniquement des variables. L'appel horloger (matériel) est utilisé pour obtenir l'heure. C'est la différence essentielle.
Les variables référencées lors du changement d'horodatage sont mises à jour avec une coche, c'est-à-dire (en principe) un processus planifié toutes les 1 seconde de HZ. Par conséquent, l'horodatage est une unité de graduation, et puisque HZ = 250 dans ubuntu 18.04 générique, il représente l'heure passée jusqu'à 1/250 seconde = 4 millisecondes. Puisqu'il n'accède pas au matériel, l'heure peut être obtenue à grande vitesse avec n'importe quelle architecture.
Lors de l'obtention de l'heure actuelle avec la commande date, en plus de cette variable, se référer au matériel de la source d'horloge (normalement CPU TSC: compteur d'horodatage dans le x86 actuel).
TSC se trouve être rapidement accessible, mais les sources d'horloges d'autres architectures ne sont pas toujours rapidement accessibles, donc chaque fois qu'un fichier est mis à jour, ce qui est censé être fait plus souvent que d'obtenir l'heure actuelle avec une commande de date, etc. Il n'est pas rationnel d'accéder à la source d'horloge.
CLOCK_REALTIME_COARSE
En fait, vous pouvez également obtenir cette heure, qui est utilisée lors du changement de l'horodatage, clock_gettime (2)
. À ce stade, spécifiez CLOCK_REALTIME_COARSE comme premier argument.
Appelez alternativement CLOCK_REALTIME et CLOCK_REALTIME_COARSE, c'est-à-dire
clock_gettime(CLOCK_REALTIME, &ts1);
clock_gettime(CLOCK_REALTIME_COARSE, &ts2);
clock_gettime(CLOCK_REALTIME, &ts3);
clock_gettime(CLOCK_REALTIME_COARSE, &ts4);
Lorsque vous le faites, ts2 et ts4 auront la même valeur avec une probabilité relativement élevée, mais ts1, ts2 et ts3 n'auront jamais la même valeur (au moins tant que la source d'horloge est tsc).