linux的时间管理(与驱动程序相关,非应用程序使用)内核和普通系统函数和使用c语言库的区别

驱动程序利用时间有两种,一种是延时,一种是定时。参考李学松的深入linux设备驱动程序内核机制。

因为读BDBM 代码的时候,看到了ktime_t ktime_get (void);  这个内核函数调用,感觉需要将其彻底理顺。

首先书中说在源代码的.config中配置为CONFIG_HZ,我从自己的linux中/usr/src/linux......-generic/中找到.config文件。但是里面时候CONFIG_HZ_250 配置了,且CONFIG_HZ=250.并不是1000.表示1秒内发生250次时钟中断,每次中断都是一个jiffies.1000就表示1秒中断1000次。jiffies是4ms级别,再精确jiffies也无法满足要求了。而为什么jiffies要引出新的jiffies_64?是因为原来的jiffies是32位,那么在HZ=1000的情况下大约50天就会使jiffies溢出,虽然驱动程序中时间度量会够,但是有的服务器需要知道自己运行时间,因此要引入jiffies_64位来处理,原32位在于低32位。


要使用jiffies从设备驱动程序角度看,只用jiffies变量,头文件为:#include<linux/jiffies.h>和 unsigned long timestamp_1定义就可以使用了;

而BDBM代码中同样有包含#include<linux/jiffies.h>可以看到内核的函数基本上多加一个linux即可。

而对于头文件以及链接库问题,使用echo 'main(){}' | gcc -E -v  - 注意这个命令,gcc可以换任意一种编译器,而且-E大写,-v是小写  最后一个-也是选项,与-v之间有空格的。

HZ这个变量可以直接用了,因为在系统内已经定义,自己在驱动用printk打印结果确实是250.

printk("HZ is %d\n",HZ);即可成功。而jiffies也是一个值,这个值是一直在变化的。同样在BDBM中打印可以显示jiffies值。

unsigned long j;

j=jiffies;

timestamp_1=jiffies+2*HZ;  //这句话的意思是当前时候往后推2s,

timestamp_1=jiffies+3*HZ/250  //表示timestamp_1为未来的3毫秒 .



根据自己使用的两个时间函数打印出来的值:

struct timeval tv; 

gettimeofday (&tv, NULL) ;

unsigned int temp= tv.tv_sec*1000000+tv.tv_usec;

这个是打印出来的usec值,而普通的包含jiffies是1s/250的基准,所以得到了temp值一直在变化,而jiffies值好长时间才会变化一次,显示结果如下:

temp=2424695878  jiffies=4314490130

temp=2428190755  jiffies=4314491004

所以从jiffies推出时间间隔为:(1004-130)/250=3.496s

而temp是:(8190755-4695878)/1000000=3.494877s差距不大。


实际使用jiffies的值的方法:

本来直接比较每个点的jiffies值,但是可能有溢出这样比较就不准确,所以提供了一组宏api来使用,对可能的溢出做了处理了。

分别为:time_after(a,b);    如果时间点a在时间点b之后,该宏返回true.可以如此记忆:将after这个单词放在两个参数之间。

time_before(a,b);

time_after_eq(a,b);该宏与time_after类似,但是当a=b时候也返回true.

time_before_eq(a,b);

time_in_range(a,b,c); 当   b=<a<=c的时候,返回true.

上述a, b定义的时候,使用usigned long型变量即可。  对于jiffies_64类型来说上说所有函数使用时候后面带上64例如:time_after64即可。

上述宏的应用场景如下:

当一个驱动中一个函数需要调用另外一个函数完成一个任务,但是对完成时间有要求,如果超过8ms就算是超时了,那么例子如下:

int demo_funciton()

{

usigned long timeout=jiffies+2*HZ/250;

do_time_task();

if(timeafter(timeout,jiffies))

{

printK("already time out");

return 1;

}

return 0;

}

二、时间转换,

有时候需要将jiffies转换为人能阅读的时间,例如:ms,us.但是此处精度仍然是jiffies自身精度,并不是ms,us精度。例如在驱动程序中打印出一次DMA传输花费时间。dma传输前记录一次jiffies,在结束之后记录一次jiffies,然后使用jiffies_to_msecs(end_jiffies- start_jiffies)函数即可。此时输出值单位是ms.

同样是在#include<linux/jiffies.h>中。

含有:jiffies_to_msecs    jiffies_to_usecs    msecs_to_jiffies      usecs_to_jiffies   看着简单,但是返回值类型和参数类型很重要。

但是该书籍并不讲怎么获取其他精度方法,因为jiffies是各个平台通用,所以只讲jiffies.

三、更高精度时间获取和使用

注意,用户编写的驱动代码肯定想看真正时间,ms,us因此使用上述转换方式。而如果想要更高精度时间,可以使用两个函数:

gettimeofday();

但是从网上查到的,头文件真的是各种各样,有包含#include<sys/time.h>的,还有包含#incldue<linux/time.h>的,都声称可以使用,真的是千奇百怪啊。

按道理,自己验证的时候,使用sys/time.h就是说得正确,但是自己查看/usr/include下面根本没有sys文件夹,这是 ubuntu环境普遍存在的问题,有的即使有/usr/include/sys文件夹也可能没有time.h,那么怎么办?按道理添加即可,网友也给出了答案,一个安装包就能生成/usr/include/sys问题,例如:http://blog.csdn.net/pengwangguo/article/details/52891608

这个网友在ide上添加了真实的/usr/include/x86_64-linux-gnu/sys/socket.h这个路径也不行,最后,

  1. sudo apt-get install build-essential flex libelf-dev libc6-dev-amd64 binutils-dev libdwarf-dev 

这个是将所有缺失的包全部安装了,然后/usr/include/sys就出现了,成功了。只是里面都是链接文件,指向仍然是/usr/include/x86_64-linux-gnu/sys的内容。

所以按照上述解决问题,是正确的,再往上推,直接使用#include<sys/time.h>也是正确的,只要这个路径里面time.h指向/usr/include/x86_64-linux-gnu/sys里的time.h并且time.h可以找到gettimeofday这个函数声明就对了。

还有个知识点:include<time.h>是c库的include<sys/time.h>是linux系统自带的,就像里面有gettimeofday这样的函数。

(到这个地方已经错的不能再错了。下面总结处给出详细解释,请跳跃。)

但是我的问题更奇怪,就是使用的BDBM竟然可以使用gettimeofday函数。就是在我没有/usr/include/sys文件目录的情况下。真是不知道BDBM 是怎么处理的。搞的我都想从简单的时间函数学习上重新跳到makefile文件中去了。。。。。。!!!!!!好烦。

那就按照上篇博客进行学习吧,下载Managing Projects with GNU Make这本书,开始阅读,用于解决看BDBM是怎么可以直接使用gettimeofday函数的。因为这个函数涉及到的头文件我都实验过了,自己编写的代码中直接抄它的头文件都不可行,一定是makefile中的设定,但是CFLAGS也没有见到明确的指向/usr/include/x86_64-linux-gnu/sys的 路径,所以好奇怪。因此学习gnu make这本书。早晚要学习。




真的糊涂了,什么叫做用户态编程?什么叫做内核态编程,为什么gcc -o这种普通的编译使用#include<sys/time.h>就可以用到gettimeofday这个函数?而使用内核模块的编码却不能。实在是不知道啊。怎么回事

有人说:http://blog.csdn.net/macrossdzh/article/details/4488677

/usr/include/linux是用来编译当前系统的程序的; 

/usr/src/linux/include/linux/是用来编译内核的,

而怎么识别编译内核模块,是看kbuild的,普通的用户态是usr/include/linux里面调用即可,是普通的gcc即可。

http://blog.csdn.net/creator_ly/article/details/53097053

看了几十篇网上回答,只有上述是正确或者说理解很清晰的。

最终总结如下:其实看编程目的了,如果是纯粹使用c函数库,那么直接使用#include<time.h>即可。但是与系统完全无关,虽然会间接调用,但是完全隔离的。并且c库中的time.h只有很粗糙的颗粒,最小到毫秒,有struct tm{}结构体,等。c语言中最精密的是到毫秒,一个简单代码为:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main(void)
{
long i=10000000L;
clock_t start,finish;
double duration;
start=clock();
while(i--);
finish=clock();
duration=(double)(finish-start)/CLOCKS_PER_SEC;
printf("%f seconds\n",duration);

}

编译就是普通的gcc编译即可。

第二、如果使用linux的系统函数来做虽然是使用的#include<sys/time.h>但是用户应用程序使用linux系统函数的有各种,#include<linux/xxx>

第三、如果使用kbuild编译的那种内核驱动代码的编写,同样使用#include<linux/xxx>但是根据makefile文件识别出来二、三区别就应该马上进入不同的路径找看似相同的#include<linux/xxx>  第二种的路径是在/usr/inlude/下面接linux或者x86_64-linux-gnu表示sys文件夹。如上sys/time.h是在x86_64-linux-gnu中的sys文件夹下面。 第三种路径是在/usr/src/linux-xxxx下面的include/linux下面,为什么看着这么远,因为kbuild编译器直接在/usr/src/linux-xxx中,所以相对 路径都是include/linux/xxx.h这种


只是最后发现是内核驱动使用的获取struct timeval值是do_gettimeofday()函数,虽然是在/usr/src/linux-xx/include/linux/time.h中,这么远,写在头文件中是#include<linux/time.h>.,而linux系统给用户使用的是/usr/include/x86_64-linux-gnu/sys/time.h中,写在头文件中是#include<sys/time.h>

在第三种kbuild编译中其实已经出现了do_gettimeofday()这个定义是在linux/timekeeping.h中,而BDBM因为kenelmodle和usermodle合在一起使用的,其中#define do_gettimeofday() gettimeofday()这句话最有迷惑性,是作用在usermodle中的,对第二种的/sys/time.h中的gettimeofday()定义名称的,然后可以做到与kernelmodle同时使用do_gettimeofday()这种假象害死人。。。。并且 do_gettimeofday()函数是参数只有一个struct timeval tv;传输地址作为指针。而gettimeofday()内部两个参数,一个也是&tv,一个是时区timezone.一般使用成0即可。




从这个地方接上面书本知识,时间管理,内核函数还有delay分别是三种:  void mdelay(usigned long msecs);

void udelay (usigned long usecs);  void ndelay(usigned long nsecs);


http://blog.csdn.net/Dreaming_My_Dreams/article/details/7745148

这个是专门对内核编程中的头文件说明,其中有几个很好:

#include <linux/***.h> 是在linux-2.6.29/include/linux下面寻找源文件。

#include <asm/***.h> 是在linux-2.6.29/arch/arm/include/asm下面寻找源文件。

#include <mach/***.h> 是在linux-2.6.29/arch/arm/mach-s3c2410/include/mach下面寻找源文件。

以及各种头文件的作用。



还有:struct timeval 结构是s和us  是do_gettimeofday(&tv)获得。

struct timeval {
        time_t tv_sec;
        suseconds_t tv_usec;

  };


struct timespec是  void do_gettimeofday_nsec(struct timespec *tv)函数获得

struct timespec
{
    time_t tv_sec;       
    long int tv_nsec;      
};











  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值