2012-09-19 89 views
31

我正在尋找一種方法來獲得絕對的,始終增加的iOS系統正常運行時間。獲得iOS系統正常運行時間,在睡眠時不會暫停

它應該返回設備上次重新啓動以來的時間,並且不會受到系統日期更改的影響。當設備處於睡眠狀態

所有我能找到任一方法暫停(CACurrentMediaTime,[NSProcessInfo systemUptime],mach_absolute_time)或改變當系統日期改變(sysctl的/ KERN_BOOTTIME)。

任何想法?

回答

45

我想我已經搞清楚了。

時間()在設備處於睡眠狀態時進行遞增,但當然可以由操作系統或用戶操作。但是,系統時鐘更改時,內核boottime(系統上次啓動時的時間戳)也會更改,因此即使這兩個值都不固定,它們之間的偏移量也是如此。

#include <sys/sysctl.h> 

- (time_t)uptime 
{ 
    struct timeval boottime; 

    int mib[2] = {CTL_KERN, KERN_BOOTTIME}; 

    size_t size = sizeof(boottime); 

    time_t now; 

    time_t uptime = -1; 

    (void)time(&now); 

    if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0) 

    { 

     uptime = now - boottime.tv_sec; 

    } 



    return uptime; 

} 
+0

幹得好伴侶。在此之前,我將不得不去找一個服務器日期/本地日期偏移比較......非常感謝。 – Magoo

+1

我認爲這對模擬器不起作用。 – mkeremkeskin

+0

這是正確的。 –

-6

爲什麼不使用systemUptime

systemUptime 返回自計算機重新啓動以來的時間。

  • (NSTimeInterval)systemUptime 返回值 一個NSTimeInterval表示,因爲電腦已經重新啓動了多長時間。

可用性 適用於iOS 4.0及更高版本。 宣佈 NSProcessInfo.h

我已經測試和證明,至少在iPhone 5與iOS 7.1.1,systemUptime時不你的手機被鎖定停止。所以任何人都不相信這可以自己測試。

+0

在我的iOS 7.1.x測試中。當設備被鎖定並且屏幕關閉時,它不會停止。 –

+0

它真的受到睡眠/清醒的影響。也許你用防止睡眠的連接電纜測試它。或者其他原因阻止你的設備在測試時睡覺... – Speakus

+0

是的,我重新測試,它停下來,對不起。 –

3

如果需要更高的精度,則下面是已接受答案的修改版本。

#include <sys/sysctl.h> 

- (NSTimeInterval)uptime 
{ 
    struct timeval boottime; 
    int mib[2] = {CTL_KERN, KERN_BOOTTIME}; 
    size_t size = sizeof(boottime); 

    struct timeval now; 
    struct timezone tz; 
    gettimeofday(&now, &tz); 

    double uptime = -1; 

    if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0) 
    { 
     uptime = now.tv_sec - boottime.tv_sec; 
     uptime += (double)(now.tv_usec - boottime.tv_usec)/1000000.0; 
    } 
    return uptime; 
} 
10

有列在所有例子中的競爭條件:如果有一個時間的變化(NTP或用戶修改),代碼將比賽和時鐘不再單調。

正確的實現是:

#include <sys/sysctl.h> 

static int64_t us_since_boot() { 
    struct timeval boottime; 
    int mib[2] = {CTL_KERN, KERN_BOOTTIME}; 
    size_t size = sizeof(boottime); 
    int rc = sysctl(mib, 2, &boottime, &size, NULL, 0); 
    if (rc != 0) { 
     return 0; 
    } 
    return (int64_t)boottime.tv_sec * 1000000 + (int64_t)boottime.tv_usec; 
} 

- (int64_t)us_uptime 
{ 
    int64_t before_now; 
    int64_t after_now; 
    struct timeval now; 

    after_now = us_since_boot(); 
    do { 
     before_now = after_now; 
     gettimeofday(&now, NULL); 
     after_now = us_since_boot(); 
    } while (after_now != before_now); 

    return (int64_t)now.tv_sec * 1000000 + (int64_t)now.tv_usec - before_now; 
} 
+4

我測試過這一點,它看起來像一個魅力的工作。我找不到任何缺陷。謝謝!爲了記錄,最後還是有一個OS10以上的非尷尬解決方案。 clock_gettime API最終可用,並且使用CLOCK_MONOTONIC應支持單調定時器的要求,該定時器在壁鐘速率下增加並具有相當的準確性。 –

+0

除了函數名稱'us_since_boot',這很棒。這只是啓動時間,而不是從啓動開始經過的時間。 –

+0

注意,有可能溢出做'boottime.tv_sec * 1000000'或者當'now.tv_sec * 1000000'(在32位平臺上很容易趕上)。爲了避免這個問題,你必須做一個直接的演員。 – d12frosted

2

我對萊謝克的回答發表評論,但沒有足夠的代表...萊謝克的解決方案返回秒。

以下是Leszek答案的修改版本,如果有人需要毫秒級精度。

#include <sys/sysctl.h> 

+ (long long int)uptime 
{ 
    struct timeval boottime; 
    int mib[2] = {CTL_KERN, KERN_BOOTTIME}; 
    size_t size = sizeof(boottime); 

    struct timeval now; 
    struct timezone tz; 
    gettimeofday(&now, &tz); 

    long long int uptime = -1; 

    if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0) 
    { 
     uptime = ((long long int)(now.tv_sec - boottime.tv_sec)) * 1000; 
     uptime += (now.tv_usec - boottime.tv_usec)/1000; 
    } 
    return uptime; 
} 
3

正如評論人說,POSIX'sclock_gettime()在iOS的10和MacOS 10.12實施。當與CLOCK_MONOTONIC參數一起使用時,它似乎會返回正常運行時間值。然而,這不是由文檔保證:

對於該時鐘,由clock_gettime()返回的值表示,因爲在過去的未指定的點 的 量的時間(以秒和納秒)(例如,系統啓動時間或Epoch)。系統啓動時間後,這個點不會改變。

而且從相應的MacOS手冊頁的摘錄:

CLOCK_MONOTONIC時鐘單調遞增,跟蹤時間,因爲一個任意點,並會繼續遞增,而該系統是睡着了。

CLOCK_MONOTONIC_RAW時鐘單調遞增,跟蹤時間,因爲像CLOCK_MONOTONIC的任意點。但是,此時鐘不受頻率或時間調整的影響。它不應該與其他系統時間源進行比較。

CLOCK_MONOTONIC_RAW_APPROX像CLOCK_MONOTONIC_RAW,但在讀出上下文 開關由系統高速緩存的值。這可以更快地讀取,但由於它可能會返回毫秒級的值,因此會導致精確度的降低。

CLOCK_UPTIME_RAW時鐘單調遞增,以相同的方式作爲CLOCK_MONOTONIC_RAW,但增量在系統處於睡眠狀態。在適當的mach_timebase轉換應用之後,返回的值與mach_absolute_time()的結果相同。

請注意,CLOCK_MONOTONIC以微秒精度返回,而CLOCK_MONOTONIC_RAW以納秒精度返回。

斯威夫特代碼:

func uptime() -> timespec { 

    var uptime = timespec() 
    if 0 != clock_gettime(CLOCK_MONOTONIC_RAW, &uptime) { 
     fatalError("Could not execute clock_gettime, errno: \(errno)") 
    } 

    return uptime 
} 

print(uptime()) // timespec(tv_sec: 636705, tv_nsec: 750700397) 

對於那些通過獲取內核啓動時間斯威夫特仍然有興趣:

func kernelBootTime() -> timeval { 

    var mib = [ CTL_KERN, KERN_BOOTTIME ] 
    var bootTime = timeval() 
    var bootTimeSize = MemoryLayout<timeval>.size 

    if 0 != sysctl(&mib, UInt32(mib.count), &bootTime, &bootTimeSize, nil, 0) { 
     fatalError("Could not get boot time, errno: \(errno)") 
    } 

    return bootTime 
} 

print(kernelBootTime()) // timeval(tv_sec: 1499259206, tv_usec: 122778)