(The execution environment of the following text is ubuntu 18.04. The source code refers to the upstream version of git which is the same version as the ubuntu 18.04 package for linking.)
If you look at the timestamp after updating the file, it may be slightly past.
$ 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
Since you are touching the file between 1 and 2, timestamp 3 must be the time between 1 and 2, but if you look closely, 3 is about 2ms before 1.
The date command does not issue a system call to get the time even if strace is applied. This is because the time is calculated without calling the kernel by a mechanism called vsyscall, so it can be considered that the system call clock_gettime (2)
is actually used. Source code of date commandThan,
/* Prepare to print the current date/time. */
gettime (&when);
gettime ()
is [defined] 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
}
With #if
, the middle clock_gettime
line is adopted.
On the other hand, touch can take strace normally.
$ 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
...
I am using a system call called ʻutimensat (2)`.
ʻUtimensat (2)is [defined] in the file
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)
{
Abbreviation
}
From now on, from do_utimes ()
→ ʻutimes_common () ,
notify_change () `in fs / attr.c is [called](https://git.kernel.org/pub/scm/linux/kernel/ git / torvalds / linux.git/tree/fs/attr.c?h=v4.15&id=d8a5b80568a9cb66810e75b182018e9edb68e8ff#n205).
When setting to the current timeattr->ia_valid
IsATTR_CTIME | ATTR_MTIME | ATTR_ATIME | ATTR_TOUCH
It has become.notify_change()
Isファイルの各種属性の変更をする関数だが、属性変更時はctime、mtime、atimeを現在時刻に変更する普通であるので、そのような処理It has become.
The current time is [obtained] by a function called 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 ()
is defined in fs / inode.c and [does](https: //) rounds the result of current_kernel_time ()
to filesystem dependent timestamp precision. git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/inode.c?h=v4.15&id=d8a5b80568a9cb66810e75b182018e9edb68e8ff#n2116). In ext4, the time stamp can be retained in ns units, so it will not be rounded here.
current_kernel_time ()
goes through some wrapper functions and finally the following functions /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;
}
Expanding the inline function 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;
}
The loop of read_seqcount_begin ()
~ read_seqcount_retry ()
is a kind of cliché to prevent you from catching half-finished data during the update.
Next, for clock_gettime ()
, [kernel / time / posix-stubs.c](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/ It is located at tree / kernel / time / posix-stubs.c? H = v4.15 & id = d8a5b80568a9cb66810e75b182018e9edb68e8ff # n82). This is finally
The following [__getnstimeofday64](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/time/timekeeping.c?h=v4.15&id=d8a5b80568a9cb66810e75b182018e9edb68e8ff# Come to 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;
}
Comparing the two, you can see that there is a difference in the tv_nsec part. While the touch command ʻutimensat ()processes the variable
tk-> tkr_mono.xtime_nsec, the date command
clock_gettime (CLOCK_REALTIME) uses the result of
timekeeping_get_ns () and overflows. If so, I am doing something like modifying tv_sec. When you call
timekeeping_get_ns ()`, you are calling clocksource.
Timestamp is variable only. The clocksource (hardware) call is used to get the time. This is the essential difference.
The variables referenced when changing the timestamp are updated with a tick, that is, (in principle) a scheduled process every 1 second of HZ. Therefore, the time stamp is a tick unit, and since HZ = 250 in ubuntu 18.04 generic, it represents the past time up to 1/250 second = 4 milliseconds. Since it does not access the hardware, the time can be obtained at high speed on any architecture.
When getting the current time with the date command, in addition to this variable, refer to the clocksource hardware (usually CPU TSC: time stamp counter on current x86).
TSC happens to be fast accessible, but clocksources from other architectures are not always fast accessible, so every time a file is updated, which is thought to be done more often than getting the current time with a date command etc. It is not rational to access the clocksource.
CLOCK_REALTIME_COARSE
Actually, you can also get this time, clock_gettime (2)
, which is used when changing the time stamp. At this time, specify CLOCK_REALTIME_COARSE as the first argument.
Call CLOCK_REALTIME and CLOCK_REALTIME_COARSE alternately, that is,
clock_gettime(CLOCK_REALTIME, &ts1);
clock_gettime(CLOCK_REALTIME_COARSE, &ts2);
clock_gettime(CLOCK_REALTIME, &ts3);
clock_gettime(CLOCK_REALTIME_COARSE, &ts4);
When you do, ts2 and ts4 have the same value with a relatively high probability, but ts1, ts2, and ts3 do not have the same value (at least as long as the clock source is tsc).
Recommended Posts