草庐IT

linux kernel时钟获取

迪迦大凹凸曼 2023-04-20 原文

1. 概览

  你的百米记录是多少?你的千米赛跑记录是多少?你的爱车到达百公里每小时的时间是多少?在前一天晚上你就设置好了电饭煲,让其在第二天早上你醒来前就煮好粥。你的上下班点又是多少?你们又是如何和你的异性定好约会时间的?可见在人类社会中时间的概念是相当的重要。在linux的内核中也是如此,有时需要等待硬件一段时间以让其初始化完成。有时你需要在确定的几秒后来访问硬件,此时你的程序需要对比当前的时间点和开始等待的时间点间隔是否达到了要求值。

2. 每秒系统滴答次数–HZ

  HZ代表kernel的系统时钟每秒的产生的中断次数,例如HZ为250时,那么每秒系统时钟产生中断的间隔则是1/250s即4ms。其定义如下

//file:kernel/include/asm-generic/param.h
# define HZ		CONFIG_HZ	/* Internal kernel timer frequency */

  可见HZ由CONFIG_HZ定义,但是CONFIG_HZ则是编译时自动生成的,可以从.config中找到具体的值

//file:kernel/.config
CONFIG_HZ=250

  在代码中也可以直接打印出HZ的值,其代码如下

time_test_drv_init
    TIME_TEST_INFO("[HZ]%d[jiffies]%lu", HZ, jiffies);

  执行结果如下

[492257.266132] timeTest:time_test_drv_init,47:[HZ]250[jiffies]4417956623

3. 系统滴答记录–jiffies

  jiffies字面意思为时间量,其在内核中用于记录系统启动后系统滴答发生的次数,其定义如下

//file:kernel/arch/arm64/kernel/vmlinux.lds.S
jiffies = jiffies_64;
//kernel/include/linux/jiffies.h
extern unsigned long volatile __cacheline_aligned_in_smp __jiffy_arch_data jiffies;

4. jiffies时间对比API

4.1 time_before&&time_before_eq

  time_before用于对比两个时刻的jiffies值,其定义如下

//file:kernel/include/linux/jiffies.h
#define time_before(a,b)	time_after(b,a)

  作用也很简单,如果a时间点在b之前那么就返回true,否则返回false。在c中true也就是非零值,false则为零。对于 time_before_eq和time_before就是多了一层为真的条件,在a和b相等时也返回真值。示例代码如下

time_test_drv_init
    unsigned long delayTime = jiffies + HZ;//delay 1s
    time_before(jiffies, delayTime);

4.2 time_after&&time_after_eq

  time_after用于判断两个时刻jiffies值,其定义如下

//kernel/include/linux/jiffies.h
#define time_after(a,b)		\
	(typecheck(unsigned long, a) && \
	 typecheck(unsigned long, b) && \
	 ((long)((b) - (a)) < 0))

  如果a的值在b之后,就返回true,否则返回false。time_after_eq也是多了一层为真的条件,在a和b相等时也会返回true。
  下面的代码判断展示以上的接口使用

time_test_drv_init
	TIME_TEST_INFO("time_after:[delayTime]%lu, [jiffies]%lu, [timer_after]%d, [time_before]%d",
					delayTime,
					jiffies,
					time_after(jiffies, delayTime),
					time_before(jiffies, delayTime));

  执行结果如下

[492257.266142] timeTest:time_test_drv_init,56:time_after:[delayTime]4417956873, [jiffies]4417956623, [timer_after]0, [time_before]1

5.内核时间的获取

  在我们的日常生活中所说的时间一般指的就是墙时间,但对于设备驱动来说墙时间显得不必要并且表示也更复杂,设备驱动中更在意的启动时间以及过去了多久。

5.1 ktime_get

   ktime_get 实际上获取的就是CLOCK_MONOTONIC时间,其在内核中的描述如下

Useful for reliable timestamps and measuring short time intervals accurately. 
Starts at system boot time but stops during suspend.

  通过ktime_get获取的时间是不统计设备休眠时间的,其返回值类型为ktime_t,单位为纳秒,并且这个时间统计的起始点则是设备启动后。其ktime_get和ktime_t的定义如下

//kernel/include/linux/ktime.h
/* Nanosecond scalar representation for kernel time values */
typedef s64	ktime_t;
//kernel/kernel/time/timekeeping.c
ktime_t ktime_get(void)
{
    ...
    return ktime_add_ns(base, nsecs);
}

  下面是接口使用的代码片段

time_test_drv_init
    ktime_t curTime = 0;
    curTime = ktime_get();
    TIME_TEST_INFO("ktime_get:%lld ns", curTime);

  下面是上面代码执行的结果

[492257.266149] timeTest:time_test_drv_init,63:ktime_get:492257307974640 ns

5.2 ktime_get_ts64

  ktime_get_ts64和ktime_get的功能是完全一样的,区别在于对时间的表示数据类型由ktime_t变成了timespec64,timespec64的定义如下

//kernel/include/linux/time64.h
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);

  下面是上面代码执行的结果

[492257.266159] timeTest:time_test_drv_init,36:492257 s 307981986 ns

5.3 ktime_get_boottime

  通过ktime_get_boottime获取的时间和ktime_get最大的不同是其包含了设备进入休眠的时间,其返回值类型为ktime_t,单位为纳秒,这个时间统计的起始点也是设备启动后。其定义如下

/**
 * ktime_get_boottime - Returns monotonic time since boot in ktime_t format
 *
 * This is similar to CLOCK_MONTONIC/ktime_get, but also includes the
 * time spent in suspend.
 */
static inline ktime_t ktime_get_boottime(void)
{
	return ktime_get_with_offset(TK_OFFS_BOOT);
}

  使用代码如下

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

  执行结果如下

[492257.266168] timeTest:time_test_drv_init,73:ktime_get_boottime:581660801601637 ns

5.4 ktime_get_boottime_ts

  ktime_get_boottime_ts相对于ktime_get_boottime的功能是完全一样的,区别在于对时间的表示数据类型由ktime_t变成了timespec64,timespec64的解释见5.2。

5.5 ktime_get_real

  ktime_get_real获取的时间的起点不是设备的启动时间点了,而是相对UTC的。内核中的说明如下

Returns the time in relative to the UNIX epoch starting in 1970 using the 
Coordinated Universal Time (UTC), same as gettimeofday() user space. This 
is used for all timestamps that need to persist across a reboot, like inode times,
but should be avoided for internal uses, since it can jump backwards due to a leap
second update, NTP adjustment settimeofday() operation from user space.

  下面是该几口的使用示例

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

  执行结果如下

[492257.266278] timeTest:time_test_drv_init,83:ktime_get_real:1646007269096922216 ns

  可见1970+(1646007269096922216ns) ~= 2022。

5.6 ktime_get_real_ts

  示例代码如下

time_test_drv_init
	struct timespec64 curTimeTs;
	ktime_get_real_ts64(&curTimeTs);

  执行结果如下

[492257.266285] timeTest:time_test_drv_init,36:1646007269 s 96929639 ns

6.完整源码如下

timeTest

https://gitee.com/solo-king/linux-kernel-base-usage/blob/master/flagstaff/timeTest.c

有关linux kernel时钟获取的更多相关文章

  1. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  2. ruby - 从 Ruby 中的主机名获取 IP 地址 - 2

    我有一个存储主机名的Ruby数组server_names。如果我打印出来,它看起来像这样:["hostname.abc.com","hostname2.abc.com","hostname3.abc.com"]相当标准。我想要做的是获取这些服务器的IP(可能将它们存储在另一个变量中)。看起来IPSocket类可以做到这一点,但我不确定如何使用IPSocket类遍历它。如果它只是尝试像这样打印出IP:server_names.eachdo|name|IPSocket::getaddress(name)pnameend它提示我没有提供服务器名称。这是语法问题还是我没有正确使用类?输出:ge

  3. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  4. ruby-on-rails - 获取 inf-ruby 以使用 ruby​​ 版本管理器 (rvm) - 2

    我安装了ruby​​版本管理器,并将RVM安装的ruby​​实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby​​。有没有办法让emacs像shell一样尊重ruby​​的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el

  5. Ruby 从大范围中获取第 n 个项目 - 2

    假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit

  6. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  7. ruby - 没有类方法获取 Ruby 类名 - 2

    如何在Ruby中获取BasicObject实例的类名?例如,假设我有这个:classMyObjectSystem我怎样才能使这段代码成功?编辑:我发现Object的实例方法class被定义为returnrb_class_real(CLASS_OF(obj));。有什么方法可以从Ruby中使用它? 最佳答案 我花了一些时间研究irb并想出了这个:classBasicObjectdefclassklass=class这将为任何从BasicObject继承的对象提供一个#class您可以调用的方法。编辑评论中要求的进一步解释:假设你有对象

  8. ruby-on-rails - 如何在 Gem 中获取 Rails 应用程序的根目录 - 2

    是否可以在应用程序中包含的gem代码中知道应用程序的Rails文件系统根目录?这是gem来源的示例:moduleMyGemdefself.included(base)putsRails.root#returnnilendendActionController::Base.send:include,MyGem谢谢,抱歉我的英语不好 最佳答案 我发现解决类似问题的解决方案是使用railtie初始化程序包含我的模块。所以,在你的/lib/mygem/railtie.rbmoduleMyGemclassRailtie使用此代码,您的模块将在

  9. ruby - 如何使用 CarrierWave 从 S3 获取真实文件 - 2

    我有一个应用程序可以读取文件的内容并为其编制索引。我将它们存储在磁盘本身中,但现在我使用的是AmazonS3,因此以下方法不再适用。事情是这样的:defperform(docId)@document=Document.find(docId)if@document.file?#Youshould'tcreateanewversion@document.versionlessdo|doc|@document.file_content=Cloudoc::Extractor.new.extract(@document.file.file)@document.saveendendend@docu

  10. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

随机推荐