推荐一个零声学院免费教程,个人觉得老师讲得不错,
分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:
https://ke.qq.com/course/4032547?flowToken=1042705
前言
原本计算时间差,没想到碰到64位除法的问题,所以啊,就单独拎出来研究研究。
一 简介
这个内核模块会在卸载时显示从驱动被加载以来经过的时间(以秒为单位)。你将使用位于kernel/time/keeping.c文件中的do_gettimeofday()函数来实现该任务。该函数在被调用时会在timeval数据结构中填充秒和微秒信息。timeval数据结构定义如下:
struct timeval {
__kernel_time_t tv_sec; /* seconds */
__kernel_suseconds_t tv_usec; /* microseconds */
};
本测试例程主要使用了do_gettimeofday()函数,原型和头文件如下:
#include <linux/time.h>
extern void do_gettimeofday(struct timeval *tv);
代码
/*
* helloworld.c
*
* Created on: 2022年9月14日
* Author: lkmao
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/time.h>
#include <linux/types.h>
static int num = 5;
module_param(num,int, S_IRUGO);
static struct timeval start_time;
static struct timeval end_time;
typedef unsigned long long u64;
static int __init hello_init(void)
{
int a = 5;
int b = 10;
int mod = 0;
mod = do_div(b,a);
printk("mod = %d a = %d,b =%d\n",mod,a,b);
printk(KERN_ERR"hello world num = %d\n",num);
pr_info("hello");
do_gettimeofday(&start_time);
return 0;
}
static void __exit hello_exit(void)
{
u64 diff_us;
u64 ms = 0;
printk("goodbye\n");
do_gettimeofday(&end_time);
diff_us = (((u64)end_time.tv_sec - (u64)start_time.tv_sec)*1000000) + \
((u64)end_time.tv_usec - (u64)start_time.tv_usec);
ms = diff_us;
do_div(ms,1000);
printk("diff_us = %llu \n",diff_us);
printk("ms = %llu\n",ms);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
Makefile
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
KERN_DIR = /home/lkmao/imx/linux/linux-imx
$(info $(KERN_VER))
obj-m += helloworld.o
all:
rm helloworld.ko -f
$(MAKE) -C $(KERN_DIR) M=`pwd` modules
cp helloworld.ko /home/lkmao/nfsroot/server/ -f
ls /home/lkmao/nfsroot/server/ -ls
cp:
sudo cp *.ko /home/lkmao/nfsroot/buildrootfs/driver_test/
sudo cp app /home/lkmao/nfsroot/buildrootfs/driver_test/
sudo cp *.ko /home/lkmao/nfsroot/buildrootfs_udev/driver_test/
sudo cp app /home/lkmao/nfsroot/buildrootfs_udev/driver_test/
#scp *.ko root@192.168.0.3:/
# arm-linux-gnueabihf-gcc test.c -o app
#$(MAKE) -C $(KERN_DIR) M=$(shell pwd) modules
app:
arm-linux-gnueabihf-gcc test.c -o app
.PHONY: clean
clean:
$(MAKE) -C$(KERN_DIR) M=$(shell pwd) clean
rm -rf app
测试
挂载nfs
在开发板中挂载ubuntu主机的nfs目录:
mount -t nfs -o nolock 192.168.0.11:/home/lkmao/nfsroot/server /server
运行结果
rmmod的时候,就可以看到这个结果。
[ 3885.409188] goodbye
[ 3885.411513] diff_us = 8043635
[ 3885.415345] ms = 8043
__aeabi_uldivmod报错的问题
编译的时候警告
WARNING: "__aeabi_uldivmod" [/home/lkmao/workspace/hello_driver/helloworld.ko] undefined!
insmod的时候报错:
root@host:/server# insmod helloworld.ko
[ 141.481187] helloworld: Unknown symbol __aeabi_uldivmod (err 0)
[ 141.514301] helloworld: Unknown symbol __aeabi_uldivmod (err 0)
insmod: can't insert 'helloworld.ko': unknown symbol in module, or unknown parameter
root@host:/server#
是编译器优化的锅吗?
解决方案:内核的顶层Makefile文件中,查找KBUILD_CFLAGS 变量,并在其中添加
-fno-tree-scev-cprop
修改完成后的值如下所示:
重新编译内核。
然后再重新编译模块。重新测试,并没有解决问题。去掉上面的修改。
其实是64位除法的问题
然后看下代码中出问题的地方,把这行代码注释掉,编译通过了,它能有什么问题呢?
printk("diff_us = %llu second = %llu ms = %llu",diff_us,diff_us/1000000,diff_us/1000);
经过反复折腾,发现原来问题出在除法上了,原来32bit的系统对64bit的变量的触发操作是有要求的。
是要使用do_div函数。
写一段代码测试这个宏函数,它的返回值是余数。关键是除数怎么得到的
int a = 5;
int b = 10;
int mod = 0;
mod = do_div(b,a);
printk("mod = %d a = %d,b =%d\n",mod,a,b);
运行结果如下
[ 2738.585889] mod = 0 a = 5,b =2
[ 2738.588970] hello world num = 5
从结果可以,第一个参数变成了计算值。
do_div宏的定义
#define do_div(n, base) __do_div_asm(n, base)
在找__do_div_asm,作为汇编小白,看不懂啊。n = __res 这行明白,就是结果赋值给了第一个参数。
#define __do_div_asm(n, base) \
({ \
register unsigned int __base asm("r4") = base; \
register unsigned long long __n asm("r0") = n; \
register unsigned long long __res asm("r2"); \
register unsigned int __rem asm(__xh); \
asm( __asmeq("%0", __xh) \
__asmeq("%1", "r2") \
__asmeq("%2", "r0") \
__asmeq("%3", "r4") \
"bl __do_div64" \
: "=r" (__rem), "=r" (__res) \
: "r" (__n), "r" (__base) \
: "ip", "lr", "cc"); \
n = __res; \
__rem; \
})
总结
不错,今天又遇到一个问题,并解决了这个问题。