Linux C プログラミングの基礎: 時間の取得

1 はじめに

Linux でのプログラミングでは、ユーザー モードでもカーネル モードでも、時間の取得がよく使用されます。コードを記述するときに簡単に参照できるように、ユーザー モードとカーネル モードのそれぞれで一般的に使用されるいくつかの時刻取得インターフェイスを以下に示します。

Linux 時間サブシステムの開発履歴と詳細な紹介については、「Linux 時間サブシステムの詳細な理解」を参照してください。

2. ユーザーモード取得時間

  • 2.1クロック_gettime()
#include <time.h>

int clock_gettime (clockid_t __clock_id, struct timespec *__tp);
  • 作用: システムクロックの種類に基づいて現在時刻を取得します。
  • __clock_id: システムクロックの種類。一般的に使用される値:
    • CLOCK_REALTIME: UTC 1970-1-1 0:0:0 から始まるシステム相対時間。システム時間を変更すると、取得される値も変わります。
    • CLOCK_MONOTONIC: システム絶対時間/単調時間 (システムが再起動してからの時間) システム時間を変更しても影響はありません。
    • CLOCK_PROCESS_CPUTIME_ID: このプロセスが現在のコード システム CPU に到達するまでにかかる時間。
    • CLOCK_THREAD_CPUTIME_ID: このスレッドが現在のコード システム CPU に到達するまでにかかる時間。
  • __tp:現在時刻を保存します。
  • 返回值: 成功した場合は 0、失敗した場合は -1 を返します。

timespec構造:

struct timespec
{
    
    
  __time_t tv_sec;  /* Seconds. 秒 */
  __syscall_slong_t tv_nsec; /* Nanoseconds.  纳秒*/
};

例:

#include <stdio.h>
#include <string.h>
#include <time.h>

long long get_clock_sys_time_ns(void)
{
    
    
    struct timespec tp;
    long long time_ns = 0;

    clock_gettime(CLOCK_MONOTONIC, &tp);
    time_ns = (long long)tp.tv_sec * 1000000000 + tp.tv_nsec;

    return time_ns;
}

int main(void)
{
    
    
    struct timespec tp;

    ///< 获取从1970年1月1日到目前的时间
    memset(&tp, 0, sizeof(struct timespec));
    clock_gettime(CLOCK_REALTIME, &tp);
    printf("clock_id = CLOCK_REALTIME, sec = %ld, nsec = %ld\n", tp.tv_sec, tp.tv_nsec);

    ///< 获取系统启动时间
    memset(&tp, 0, sizeof(struct timespec));
    clock_gettime(CLOCK_MONOTONIC, &tp);
    printf("clock_id = CLOCK_MONOTONIC, sec = %ld, nsec = %ld, sys_time = %lld ns\n", tp.tv_sec, tp.tv_nsec, get_clock_sys_time_ns());

    ///< 获取本进程运行时间
    memset(&tp, 0, sizeof(struct timespec));
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tp);
    printf("clock_id = CLOCK_PROCESS_CPUTIME_ID, sec = %ld, nsec = %ld\n", tp.tv_sec, tp.tv_nsec);

    ///< 获取本线程运行时间
    memset(&tp, 0, sizeof(struct timespec));
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp);
    printf("clock_id = CLOCK_THREAD_CPUTIME_ID, sec = %ld, nsec = %ld\n", tp.tv_sec, tp.tv_nsec);

    return 0;
}
  • 2.2. gettimeofday()
#include <sys/time.h>

int gettimeofday(struct timeval *tv, struct timezone *tz);
  • 作用:現在時刻を取得(1970年1月1日から現在時刻まで)
  • tv:現在の UTC 時間
  • tz:現在のタイムゾーン情報
  • 返回值: 成功した場合は 0、失敗した場合は -1 を返します。

タイムバル構造:

struct timeval
{
    
    
  __time_t tv_sec;  /* Seconds.  秒*/
  __suseconds_t tv_usec; /* Microseconds.  微秒*/
};

タイムゾーン構造:

struct timezone
  {
    
    
    int tz_minuteswest;  /* Minutes west of GMT. 和Greenwich时间差了多少分钟 */
    int tz_dsttime;  /* Nonzero if DST is ever in effect. 日光节约时间的状态  */
  };

例:

#include <stdio.h>
#include <string.h>
#include <sys/time.h>

long long get_sys_time_ms(void)
{
    
    
    long long time_ms = 0;
    struct timeval tv;

    gettimeofday(&tv, NULL);
    time_ms = ((long long)tv.tv_sec*1000000 + tv.tv_usec) / 1000;

    return time_ms;
}

int main(void)
{
    
    
    ///< 获取系统时间
    printf("sys_time = %lld ms\n", get_sys_time_ms());

    return 0;
}
  • 2.3. 時間()
#include <time.h>

time_t time(time_t *tloc);
  • 作用: 1970-01-01 00:00:00 +0000 (UTC) からの秒数を取得します
  • tloc: 返された秒ストレージ ポインタ
  • 返回值: 成功した場合は秒数を返し、失敗した場合は -1 を返し、エラーの理由は errno に格納されます。

time_tのタイプ:

typedef long time_t;

例:

#include <stdio.h>
#include <time.h>

time_t get_utc_time(void)
{
    
    
    return time(NULL);
}

int main(int argc, char **argv)
{
    
    
    time_t utc_time = get_utc_time();
    printf("utc_time = %ld s\n", utc_time);

    return 0;
}
  • 2.4. 現地時間()
#include <time.h>

struct tm *localtime(const time_t *timep);
  • 作用: time_t 型の時刻を struct tm 型の時刻に変換します
  • timep: 現在の UTC 秒
  • 返回值:現地時間に戻る

TM構造:

struct tm
{
    
    
  int tm_sec;   /* Seconds. [0-60] (1 leap second) */
  int tm_min;   /* Minutes. [0-59] */
  int tm_hour;   /* Hours. [0-23] */
  int tm_mday;   /* Day.  [1-31] */
  int tm_mon;   /* Month. [0-11] 注意:0代表1月,以此类推*/
  int tm_year;   /* Year - 1900.  该值为实际年份减去1900*/
  int tm_wday;   /* Day of week. [0-6] 注意:0代表星期一,以此类推*/
  int tm_yday;   /* Days in year.[0-365] 从每年的1月1日开始的天数,其中0代表1月1日,以此类推*/
  int tm_isdst;   /* DST.  [-1/0/1] 夏玲时标识符*/
};

例:

#include <stdio.h>
#include <time.h>

time_t get_utc_time(void)
{
    
    
    return time(NULL);
}

int main(int argc, char **argv)
{
    
    
    time_t utc_time = get_utc_time();
    printf("utc_time = %ld s\n", utc_time);

    struct tm *local_tm = localtime(&utc_time); 
    printf("local time = %.4d-%.2d-%.2d %.2d:%.2d:%.2d\n", local_tm->tm_year + 1900,
                                                           local_tm->tm_mon + 1,
                                                           local_tm->tm_mday,
                                                           local_tm->tm_hour,
                                                           local_tm->tm_min,
                                                           local_tm->tm_sec);

    return 0;
}
  • 2.5. ローカルタイム_r()
#include <time.h>

struct tm *localtime_r(const time_t *timep, struct tm *result);
  • 作用: time_t 型の時刻を struct tm 型の時刻に変換します
  • timep: 現在の UTC 秒
  • result: 現地時間
  • 返回值:現地時間に戻る

注:
localtime はスレッドセーフな関数ではありません。リアルタイム要件が高いシステムの場合、複数のスレッドが同時に localtime を呼び出すと、データが上書きされる可能性があります。代わりに localtime_r を使用してください。

例:

#include <stdio.h>
#include <time.h>

time_t get_utc_time(void)
{
    
    
    return time(NULL);
}

int main(int argc, char **argv)
{
    
    
    time_t utc_time = get_utc_time();
    printf("utc_time = %ld s\n", utc_time);
    struct tm result;
    struct tm *local_tm = localtime_r(&utc_time, &result); 
    printf("local time = %.4d-%.2d-%.2d %.2d:%.2d:%.2d\n", local_tm->tm_year + 1900,
                                                           local_tm->tm_mon + 1,
                                                           local_tm->tm_mday,
                                                           local_tm->tm_hour,
                                                           local_tm->tm_min,
                                                           local_tm->tm_sec);

    printf("result time = %.4d-%.2d-%.2d %.2d:%.2d:%.2d\n", result.tm_year + 1900,
                                                            result.tm_mon + 1,
                                                            result.tm_mday,
                                                            result.tm_hour,
                                                            result.tm_min,
                                                            result.tm_sec);

    return 0;
}
  • 2.6. gmtime()
#include <time.h>

struct tm *gmtime(const time_t *timep);
  • 作用: tm 構造体の GMT 時刻 (UTC 時刻) を返します。
  • timep: 現在の UTC 秒
  • 返回值:現地時間に戻る

例:

#include <stdio.h>
#include <time.h>

time_t get_utc_time(void)
{
    
    
    return time(NULL);
}

int main(int argc, char **argv)
{
    
    
    time_t utc_time = get_utc_time();
    printf("utc_time = %ld s\n", utc_time);

    struct tm *gmt_tm = gmtime(&utc_time); 
    printf("gmt time = %.4d-%.2d-%.2d %.2d:%.2d:%.2d\n", gmt_tm->tm_year + 1900,
                                                         gmt_tm->tm_mon + 1,
                                                         gmt_tm->tm_mday,
                                                         gmt_tm->tm_hour,
                                                         gmt_tm->tm_min,
                                                         gmt_tm->tm_sec);

    return 0;
}

localtime と gmtime の違い:
localtime と gmtime はどちらも C 言語の関数で、time_t 型の時刻を struct tm 型の時刻に変換するために使用されます。それらの違いは、gmtime は time_t を協定世界時である UTC 時間に変換するのに対し、localtime は time_t を現地時間に変換することです。
例: gmtime インターフェイスと localtime インターフェイスによって返された時間を使用して、ローカル タイム ゾーンを計算します。

#include <stdio.h>
#include <time.h>

time_t get_utc_time(void)
{
    
    
    return time(NULL);
}

int main(int argc, char **argv)
{
    
    
    time_t utc_time = get_utc_time();
    printf("utc_time = %ld s\n", utc_time);

    struct tm *gmt_tm = gmtime(&utc_time); 
    printf("gmt time = %.4d-%.2d-%.2d %.2d:%.2d:%.2d\n", gmt_tm->tm_year + 1900,
                                                         gmt_tm->tm_mon + 1,
                                                         gmt_tm->tm_mday,
                                                         gmt_tm->tm_hour,
                                                         gmt_tm->tm_min,
                                                         gmt_tm->tm_sec);
    int gmt_hour = gmt_tm->tm_hour;

    struct tm *local_tm = localtime(&utc_time); 
    printf("local time = %.4d-%.2d-%.2d %.2d:%.2d:%.2d\n", local_tm->tm_year + 1900,
                                                           local_tm->tm_mon + 1,
                                                           local_tm->tm_mday,
                                                           local_tm->tm_hour,
                                                           local_tm->tm_min,
                                                           local_tm->tm_sec);
    int local_hour = local_tm->tm_hour;


    int local_time_zone = local_hour - gmt_hour;
    if (local_time_zone < -12) 
    {
    
    
        local_time_zone += 24; 
    } 
    else if (local_time_zone > 12) 
    {
    
    
        local_time_zone -= 24;
    }else{
    
    }

    printf("local_time_zone = %d\n", local_time_zone);

    return 0;
}

3. カーネル状態取得時間

  • 3.1. do_gettimeofday() (新しいカーネルには存在しない可能性がある古い関数)
#include <linux/time.h> 

void do_gettimeofday(struct timeval *tv);
  • 作用: C 標準ライブラリの gettimeofday() と同じ使用法
  • tv:現在の UTC 時間

タイムバル構造:

struct timeval
{
    
    
  __time_t tv_sec;  /* Seconds.  秒*/
  __suseconds_t tv_usec; /* Microseconds.  微秒*/
};

例:

#include <linux/module.h>
#include<linux/time.h>
MODULE_LICENSE("GPL");

int __init do_gettimeofday_init(void)
{
    
    
    printk("do_gettimeofday test begin.\n");
    struct timeval now=
    {
    
    
        .tv_sec=0,
        .tv_usec=0
    };            //声明一个变量
    do_gettimeofday(&now); //调用函数获取时间,此时间是距离1970-01-01 00:00:00的时间
    /*显示当前时间差*/
    printk("the seconds of the day is: %ld\n", now.tv_sec);       //秒数
    printk("the microseconds of the day is: %ld\n", now.tv_usec); //微秒数
    printk("do_gettimeofday test over.\n");
    return 0;
}

void __exit do_gettimeofday_exit(void)
{
    
    
    printk("Goodbye do_gettimeofday test\n");
}

module_init(do_gettimeofday_init);
module_exit(do_gettimeofday_exit);
  • 3.2. ktime_t形式に基づく時刻
    参考:Linux カーネルの時計取得

    1. ktime_get()
    #include "linux/ktime.h"
    
    ktime_t ktime_get(void);
    

    作用: 取得されるのは CLOCK_MONOTONIC 時間です。ktime_get によって取得された時間にはデバイスのスリープ時間はカウントされません。この時間統計の開始点はデバイスの起動後です。
    返回值: データ型を ktime_t 形式でナノ秒単位で返します。

    ktime_tの定義:

    typedef s64	ktime_t;
    

    例:

    time_test_drv_init
    ktime_t curTime = 0;
    curTime = ktime_get();
    TIME_TEST_INFO("ktime_get:%lld ns", curTime);
    
    1. ktime_get_ts64()
    #include "linux/time64.h"
    
    void ktime_get_ts64(struct timespec64 *ts)

    作用: 関数は ktime_get と全く同じですが、時間を表すデータ型が ktime_t から timespec64 に変わっている点が異なります。

    timespec64は次のように定義されます。

    struct timespec64 {
          
          
    	time64_t	tv_sec;			/* seconds */
    	long		tv_nsec;		/* nanoseconds */
    };
    

    timespec64 には秒とナノ秒が含まれており、ktime_t ナノ秒と比較して、この時間表現は人間が見るのに適しています。

    例:

    static void show_time_ts64(const char* caller, const int line, const struct timespec64 *curTimeTs)
    {
          
          
    	pr_info("%s,%d:%lld s %ld ns\n", caller, __LINE__, curTimeTs->tv_sec, curTimeTs->tv_nsec);
    }
    
    time_test_drv_init
    struct timespec64 curTimeTs;
    ktime_get_boottime_ts64(&curTimeTs);
    show_time_ts64(__func__, __LINE__, &curTimeTs);
    
  • 3. ktime_get_boottime()

    static inline ktime_t ktime_get_boottime(void);
    

    作用:ktime_get_boottime と ktime_get で取得される時間の最大の違いは、デバイスがスリープに入る時間が含まれていることと、この時間統計の開始点もデバイスの起動後であることです。
    返回值: 戻り値の型はktime_t、単位はナノ秒です。

    例:

    time_test_drv_init
    ktime_t curTime = 0;
    curTime = ktime_get_boottime();
    TIME_TEST_INFO("ktime_get_boottime:%lld ns", curTime);
    
  • 4. ktime_get_boottime_ts()

    void ktime_get_boottime_ts64(struct timespec64 *)

    作用: ktime_get_boottime_ts の機能は ktime_get_boottime と全く同じですが、時間を表すデータ型が ktime_t から timespec64 に変わっている点が異なります。

  • 5. ktime_get_real()

    ktime_t ktime_get_real(void);
    

    作用: ktime_get_real で取得される時刻の開始点は、デバイスの起動時刻ではなく、UTC を基準とした相対値、つまり 1970 年からの時刻です。
    例:

    time_test_drv_init
    ktime_t curTime = 0;
    curTime = ktime_get_real();
    TIME_TEST_INFO("ktime_get_real:%lld ns", curTime);
    
  • 6. ktime_get_real_ts()

    void ktime_get_real_ts(struct timespec64 *)

    作用: ktime_get_real_ts の機能は ktime_get_real とまったく同じですが、時間を表すデータ型が ktime_t から timespec64 に変更される点が異なります。
    例:

    time_test_drv_init
    struct timespec64 curTimeTs;
    ktime_get_real_ts64(&curTimeTs);
    
  • 7.まとめ

    • 7.1. ktime_tの時間インターフェースを返します。
    インターフェース 時間システム 説明する
    ktime_t ktime_get( void ); クロック_モノトニック システム起動時刻を時刻の原点とする時刻制度
    ktime_t ktime_get_boottime( void ); クロック_ブートタイム システム起動時刻を時刻の原点とする時刻制度
    ktime_t ktime_get_real( void ); CLOCK_REALTIME 自然時系
    ktime_t ktime_get_locktai( void ); CLOCK_TAI 自然時系
    ktime_t ktime_get_raw( void ); CLOCK_MONOTONIC_RAW システム起動時刻を時刻の原点とする時刻制度
    • CLOCK_REALTIMECLOCK_TAI はどちらも自然時刻系ですが、後者はうるう秒の影響を受けないという違いがあります。ただし、ユーザー空間が時刻を設定し、NTP も自然時刻を調整するため、両方の時刻システムがリバウンドを引き起こす可能性があります。

    • CLOCK_BOOTTIMECLOCK_MONOTONICCLOCK_MONOTONIC_RAW、これら 3 つはシステム起動時点を時間の原点とする時間系です。CLOCK_BOOTTIME と CLOCK_MONOTONIC の違いは、前者はシステムのスリープ時に実行時間が一時停止されないのに対し、後者は一時停止されることですが、どちらも NTP の影響を受ける点は同じです。CLOCK_MONOTONIC_RAW と CLOCK_MONOTONIC は基本的に同じですが、前者は NTP の影響を受けません。

    • 7.2. u64 (ナノ秒) の時間インターフェイスを返します。

    インターフェース 時間システム 説明する
    u64 ktime_get_ns( void ); クロック_モノトニック システム起動時刻を時刻の原点とする時刻制度
    u64 ktime_get_boottime_ns( void ); クロック_ブートタイム システム起動時刻を時刻の原点とする時刻制度
    u64 ktime_get_real_ns( void ); CLOCK_REALTIME 自然時系
    u64 ktime_get_locktai_ns( void ); CLOCK_TAI 自然時系
    u64 ktime_get_raw_ns( void ); CLOCK_MONOTONIC_RAW システム起動時刻を時刻の原点とする時刻制度
    • 7.3. timespec64の時間インターフェースを返します。
    インターフェース 時間システム 説明する
    void ktime_get_ts64( struct timespec64 * ); クロック_モノトニック システム起動時刻を時刻の原点とする時刻制度
    void ktime_get_boottime_ts64( struct timespec64 * ); クロック_ブートタイム システム起動時刻を時刻の原点とする時刻制度
    void ktime_get_real_ts64( struct timespec64 * ); CLOCK_REALTIME 自然時系
    void ktime_get_ Clocktai_ts64( struct timespec64 * ); CLOCK_TAI 自然時系
    void ktime_get_raw_ts64( struct timespec64 * ); CLOCK_MONOTONIC_RAW システム起動時刻を時刻の原点とする時刻制度
    #include "linux/time64.h"
    
    struct timespec64 {
          
          
    	time64_t    tv_sec;         /* seconds */
    	long        tv_nsec;        /* nanoseconds */
    };
    
    • 7.4. time64_t (秒)を返す時間インターフェース:
    インターフェース 時間システム 説明する
    time64_t ktime_get_秒( void ); クロック_モノトニック システム起動時刻を時刻の原点とする時刻制度
    time64_t ktime_get_boottime_秒( void ); クロック_ブートタイム システム起動時刻を時刻の原点とする時刻制度
    time64_t ktime_get_real_秒( void ); CLOCK_REALTIME 自然時系
    time64_t ktime_get_locktai_秒( void ); CLOCK_TAI 自然時系
    time64_t ktime_get_raw_秒( void ); CLOCK_MONOTONIC_RAW システム起動時刻を時刻の原点とする時刻制度
    • 7.5. 大まかな ktime_t を返す時間インターフェース:
    インターフェース 時間システム 説明する
    ktime_t ktime_get_coarse( void ); クロック_モノトニック システム起動時刻を時刻の原点とする時刻制度
    ktime_t ktime_get_coarse_boottime( void ); クロック_ブートタイム システム起動時刻を時刻の原点とする時刻制度
    ktime_t ktime_get_coarse_real( void ); CLOCK_REALTIME 自然時系
    ktime_t ktime_get_coarse_locktai( void ); CLOCK_TAI 自然時系
    • 7.6. 大まかな u64 (ナノ秒) を返す時間インターフェイス:
    インターフェース 時間システム 説明する
    u64 ktime_get_coarse_ns( void ); クロック_モノトニック システム起動時刻を時刻の原点とする時刻制度
    u64 ktime_get_coarse_boottime_ns( void ); クロック_ブートタイム システム起動時刻を時刻の原点とする時刻制度
    u64 ktime_get_coarse_real_ns( void ); CLOCK_REALTIME 自然時系
    u64 ktime_get_coarse_ Clocktai_ns( void ); CLOCK_TAI 自然時系
    • 7.7. 大まかな timespec64 を返す時間インターフェース:
    インターフェース 時間システム 説明する
    void ktime_get_coarse_ts64( struct timespec64 * ); クロック_モノトニック システム起動時刻を時刻の原点とする時刻制度
    void ktime_get_coarse_boottime_ts64( struct timespec64 * ); クロック_ブートタイム システム起動時刻を時刻の原点とする時刻制度
    void ktime_get_coarse_real_ts64( struct timespec64 * ); CLOCK_REALTIME 自然時系
    void ktime_get_coarse_ Clocktai_ts64( struct timespec64 * ); CLOCK_TAI 自然時系
    • 7.8.高速な u64 (ナノ秒)を返す時間インターフェイス:
    インターフェース 時間システム 説明する
    u64 ktime_get_mono_fast_ns( void ); クロック_モノトニック システム起動時刻を時刻の原点とする時刻制度
    u64 ktime_get_boot_fast_ns( void); CLOCK_BOOTTIME 以系统启动的时间点为时间原点的时间体系
    u64 ktime_get_real_fast_ns( void ); CLOCK_REALTIME 自然时间体系
    u64 ktime_get_raw_fast_ns( void ); CLOCK_MONOTONIC_RAW 以系统启动的时间点为时间原点的时间体系

4.自己实现的延时函数示例

void delay_us(uint32_t nus)
{
    
    
    volatile uint32_t startts, endts, ts;

    ts = nus;

    startts = get_time_us();
    endts = startts + ts;

    if (endts > startts)
    {
    
    
        while (get_time_us() < endts);
    }
    else
    {
    
    
        while (get_time_us() > endts);
        while (get_time_us() < endts);
    }
}

5.有关__clock_id的详细说明

clockid的取值常用的有以下:

  • CLOCK_REALTIME,就是我们所说的自然时间,由于计算机上的时间有可能不准,所以是可以设置的,所以有可能会产生跳跃。后面所有的时间体系都是不可设置的,下面不再一一说明了。CLOCK_REALTIME_ALARM、CLOCK_REALTIME_COARSE、CLOCK_TAI虽然本身是不可设置的,但是都会受到CLOCK_REALTIME设置的影响,有可能会产生跳跃。

  • CLOCK_REALTIME_ALARM,和CLOCK_REALTIME相同,在定时器设置时才有用,ALARM代表的是定时设置,如果目标定时时间到了的时候系统在休眠,会唤醒系统。

  • CLOCK_REALTIME_COARSE,和CLOCK_REALTIME相同,精度不高但是获取比较快。

  • CLOCK_TAI,和CLOCK_REALTIME相同,但是不考虑闰秒问题,TAI是International Atomic Time的反向简称。

  • CLOCK_MONOTONIC,由于前面几个时间体系都有可能会产生回跳,计算机中需要有一个单调递增的时间体系。此时间体系的时间原点并不重要,在Linux中是以系统启动的时间点作为时间原点,在计算机休眠时会暂停走时,受adjtime和NTP的影响可能会向前跳跃。

  • CLOCK_MONOTONIC_COARSE,同上,但是精度降低,访问更快。

  • CLOCK_MONOTONIC_RAW,同CLOCK_MONOTONIC,但是不受adjtime和NTP的影响。

  • CLOCK_BOOTTIME,以系统启动时间为时间原点的时间体系,不受其它因素的影响,计算机休眠时依然走时。

  • CLOCK_BOOTTIME_ALARM,同上,在定时器设置时才有用,ALARM代表的是定时设置,如果目标定时时间到了的时候系统在休眠,会唤醒系统。

  • CLOCK_PROCESS_CPUTIME_ID,以进程创建时间为时间原点,进程运行时走时,进程休眠时暂停走时。

  • CLOCK_THREAD_CPUTIME_ID,以线程创建时间为时间原点,线程运行时走时,线程休眠时暂停走时。

おすすめ

転載: blog.csdn.net/weixin_40837318/article/details/131150265