為什麼我不能將我的 GNU/Linux 機器的日期設置為 Epoch?
在我可以訪問的 Raspberry Pi 和 Ubuntu 16.04 x86_64 機器上執行
# date -s @0
返回
date: cannot set date: Invalid argument thu 1 jan 1970 01:00:00 CET
我想知道:
- 不可能背後的原因是什麼,
- 是否有將系統時間重置為 Epoch 的程式方式。
我無法在舊系統上準確重現“無效參數”,但是:
# strace date -s '@-1' … clock_settime(CLOCK_REALTIME, {4294967295, 0}) = -1 EINVAL (Invalid argument) settimeofday({4294967295, 0}, NULL) = -1 EINVAL (Invalid argument) write(2, "date: ", 6date: ) = 6 write(2, "cannot set date", 15cannot set date) = 15 write(2, ": Invalid argument", 18: Invalid argument) = 18 write(2, "\n", 1
(然後
date
繼續顯示它想要設置的日期,即使它實際上並沒有設置它。)簡單來說,
date
就是呼叫核心將日期設置為紀元前一秒,核心告訴它建議的日期無效。現在在上面的跟踪中,您看到 4294967295 是秒數,而不是 -1。這個數字是 2^32-1,我在 32 位機器上執行它。這實際上不是問題的根源:它是 中的顯示問題
strace
,它不知道該值是否應該被簽名。事實上,秒數是一個有符號整數類型time_t
。Linux 上的 Glibc 2.23 定義time_t
as__time_t
in/usr/include/time.h
,__time_t
as__TIME_T_TYPE
in/usr/include/bits/types.h
__TIME_T_TYPE
as in__SYSCALL_SLONG_TYPE
,以及64 位有符號類型 in 。在核心方面,從 4.4 開始,呼叫參數類型的定義是秒是__SQUAD_TYPE``/usr/include/bits/typesizes.h``__SQUAD_TYPE``/usr/include/bits/types.h``settimeofday
struct timespec
__kernel_time_t
在 64 位機器time64_t
上是有符號的 64 位類型,在 32 位機器上是有符號的 64 位類型。所以核心確實將 -1 視為 -1 而不是一些大的正數。你不能回到紀元之前的原因是 Linux 核心明確拒絕這樣做。如果拒絕給定的時間結構,則
settimeofday
係統呼叫返回 EINVAL(“無效參數”) ,並且該函式拒絕負數秒數。timeval_valid
不同版本的核心具有不同組織的程式碼,但我認為行為沒有改變。
我看不出有任何理由拒絕正好為 0 的時間,並且它在我測試它的 VM 中工作。
進行這種完整性檢查的原因可能是那裡的程式碼將自紀元以來的時間儲存為無符號整數,因此無法處理紀元之前的日期。一些程式碼可能會將 0 解釋為“日期未知”,因此拒絕 0 也是有意義的,但 Linux 實際上並沒有這樣做,而且它只會持續一秒鐘。
時代之前的時代當然可以被表示和操縱。許多軟體需要跟踪過去事件的時間。幾乎每個作業系統都使用的時區數據庫具有可以追溯到時區被編纂時的歷史數據(大約在鐵路開始使用的時間)。但是你不能告訴 Linux 電腦目前時間是在 1970 年之前。