(Die Ausführungsumgebung des folgenden Textes ist Ubuntu 18.04. Der Quellcode bezieht sich zum Verknüpfen auf die Upstream-Version von git, die dieselbe Version wie das Ubuntu 18.04-Paket ist.)
Wenn Sie sich die Zeitstempel nach dem Aktualisieren der Datei ansehen, ist sie möglicherweise etwas vergangen.
$ 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
Da Sie die Datei zwischen 1 und 2 berühren, muss der Zeitstempel 3 eine Zeit zwischen 1 und 2 sein. Wenn Sie jedoch genau hinschauen, liegt 3 etwa 2 ms vor 1.
Der Befehl date gibt keinen Systemaufruf aus, um die Uhrzeit abzurufen, selbst wenn strace angewendet wird. Dies liegt daran, dass die Zeit berechnet wird, ohne den Kernel durch einen Mechanismus namens vsyscall aufzurufen. Daher kann davon ausgegangen werden, dass der Systemaufruf "clock_gettime (2)" tatsächlich verwendet wird. Befehl für den Quellcode des DatumsAls,
/* Prepare to print the current date/time. */
gettime (&when);
gettime ()
ist [definiert] in gnulib (GNU Portability Library) (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
}
In # if
wird die mittlere clock_gettime
Zeile übernommen.
Auf der anderen Seite kann die Berührung normal sein.
$ 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
...
Ich verwende einen Systemaufruf namens "utimensat (2)".
utimensat (2)
ist [definiert] in einer Datei namens 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)
{
Abkürzung
}
Von nun an wird ab do_utimes ()
→ utimes_common ()
notify_change ()
in fs / attr.c aufgerufen. git / torvalds / linux.git / tree / fs / attr.c? h = v4.15 & id = d8a5b80568a9cb66810e75b182018e9edb68e8ff # n205).
Bei Einstellung auf die aktuelle Zeitattr->ia_valid
IstATTR_CTIME | ATTR_MTIME | ATTR_ATIME | ATTR_TOUCH
Es ist geworden.notify_change()
Istファイルの各種属性の変更をする関数だが、属性変更時はctime、mtime、atimeを現在時刻に変更する普通であるので、そのような処理Es ist geworden.
Die aktuelle Zeit wird durch die Funktion current_time ()
(https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/attr] erhalten .c? h = v4.15 & id = d8a5b80568a9cb66810e75b182018e9edb68e8ff # n242).
current_time ()
ist in fs / inode.c und [does](https: //) definiert, um das Ergebnis von current_kernel_time ()
auf die dateisystemabhängige Zeitstempelgenauigkeit zu runden. git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/inode.c?h=v4.15&id=d8a5b80568a9cb66810e75b182018e9edb68e8ff#n2116). In ext4 kann der Zeitstempel in ns-Einheiten beibehalten werden, sodass er hier nicht gerundet wird.
current_kernel_time ()
durchläuft einige Wrapper-Funktionen und schließlich die folgenden Funktionen /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;
}
Erweiterung der Inline-Funktion 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;
}
Die Schleife von "read_seqcount_begin ()" zu "read_seqcount_retry ()" ist eine Art Klischee, um zu verhindern, dass Sie während des Updates halbfertige Daten abfangen.
Als nächstes für 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). Das ist endlich
Die folgenden [__getnstimeofday64](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/time/timekeeping.c?h=v4.15&id=d8a5b80568a9cb66810e75b182018e9 Komm zu 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;
}
Wenn Sie die beiden vergleichen, sehen Sie, dass es einen Unterschied im Teil tv_nsec gibt. Der Berührungsbefehl utimensat ()
verarbeitet die Variable tk-> tkr_mono.xtime_nsec
, während der Datumsbefehlclock_gettime (CLOCK_REALTIME)
das Ergebnis von timekeeping_get_ns ()
verwendet und überläuft. Wenn ja, mache ich so etwas wie das Ändern von tv_sec. Wenn Sie "timekeeping_get_ns ()" aufrufen, rufen Sie clocksource auf.
Zeitstempel sind nur Variablen. Der Clocksource-Aufruf (Hardware-Aufruf) wird verwendet, um die Zeit abzurufen. Dies ist der wesentliche Unterschied.
Die Variablen, auf die beim Ändern des Zeitstempels verwiesen wird, werden mit einem Häkchen aktualisiert, dh (im Prinzip) einem geplanten Prozess alle 1 Sekunde von HZ. Daher ist der Zeitstempel eine Tick-Einheit, und da HZ = 250 in Ubuntu 18.04 generisch ist, repräsentiert er die vergangene Zeit bis zu 1/250 Sekunde = 4 Millisekunden. Da nicht auf die Hardware zugegriffen wird, kann die Zeit mit jeder Architektur mit hoher Geschwindigkeit abgerufen werden.
Wenn Sie die aktuelle Uhrzeit mit dem Datumsbefehl abrufen, beziehen Sie sich zusätzlich zu dieser Variablen auf die Hardware der Taktquelle (normalerweise CPU TSC: Zeitstempelzähler im aktuellen x86).
TSC ist zwar schnell zugänglich, aber Taktquellen von anderen Architekturen sind nicht immer schnell zugänglich. Daher wird jedes Mal, wenn eine Datei aktualisiert wird, davon ausgegangen, dass dies häufiger erfolgt als das Abrufen der aktuellen Uhrzeit mit einem Datumsbefehl usw. Es ist nicht rational, auf die Uhrenquelle zuzugreifen.
CLOCK_REALTIME_COARSE
Tatsächlich können Sie auch diese Zeit "clock_gettime (2)" abrufen, die beim Ändern des Zeitstempels verwendet wird. Geben Sie zu diesem Zeitpunkt CLOCK_REALTIME_COARSE als erstes Argument an.
Rufen Sie abwechselnd CLOCK_REALTIME und CLOCK_REALTIME_COARSE auf.
clock_gettime(CLOCK_REALTIME, &ts1);
clock_gettime(CLOCK_REALTIME_COARSE, &ts2);
clock_gettime(CLOCK_REALTIME, &ts3);
clock_gettime(CLOCK_REALTIME_COARSE, &ts4);
Wenn Sie dies tun, haben ts2 und ts4 mit relativ hoher Wahrscheinlichkeit denselben Wert, aber ts1, ts2 und ts3 haben niemals denselben Wert (zumindest solange die Taktquelle tsc ist).