前言
最近的一个项目中,想要统计make整个项目的时间,并添加到Makefile中,生成镜像后,自动输出时间到屏幕。
调研了,发现可以使用以下两种方式来实现。
一、time工具
最简单的方式,使用time
工具来统计运行脚本时间,百度百科对time命令的解释:
time命令常用于测量一个命令的运行时间
测试环境是ubuntu20.04
, 在终端输入time
命令
time
real 0m0.000s
user 0m0.000s
sys 0m0.000s
- real : 整个执行过程所用时间,单位秒
- user: 用户空间运行程序所消耗的时间,单位秒
- sys: 内核空间运行程序所消耗的时间, 单位秒
以一个驱动demo编译过程为例。
在编译目录下,运行Makefile的make动作。
make
make -C /lib/modules/`uname -r`/build M=/home/xxx/Desktop/xxx/test/driver modules
make[1]: Entering directory '/usr/src/linux-headers-5.13.0-41-generic'
CC [M] /home/xxx/Desktop/xxx/test/driver/msg_test.o
MODPOST /home/xxx/Desktop/xxx/test/driver/Module.symvers
CC [M] /home/xxx/Desktop/xxx/test/driver/msg_test.mod.o
LD [M] /home/xxx/Desktop/xxx/test/driver/msg_test.ko
BTF [M] /home/xxx/Desktop/xxx/test/driver/msg_test.ko
Skipping BTF generation for /home//home/xxx/Desktop/xxx/test/driver/msg_test.ko due to unavailability of vmlinux
make[1]: Leaving directory '/usr/src/linux-headers-5.13.0-41-generic'
real 0m14.638s
user 0m0.663s
sys 0m0.560s
可以看到整个过程,耗时14.638
秒, 用户态与内核态消耗的时间分别是0.663秒、0.560秒。
二、自行实现
思路
我们只需要在Makefile开始运行时,记录一个时间戳。Makefile执行完成时记录一个时间戳,在进行时间的相减,就可以获取总的运行时间。
1、获取开始时间戳。
可以采用linux系统的/proc/uptime
来获取当前时间。
可以先分析一下/proc/uptime
里面的内容,里面记录了Linux系统运行时间。
cat /proc/uptime
4643.14 34329.17
- 第一列 4643.14, 是系统启动,到
cat /proc/uptime
命令运行时, 系统已经运行的时间,单位是秒。 - 第二列 34329.17, 是系统空闲时间,与系统核数有关系,这个时间,是系统启动至今,所有核数,空闲的时间之和,作者的ubuntu是8核数,该参数不太关心,与本次实现的内容无关。
2、提取时间戳的思路
只需要用awk
命令,通过.
分割/proc/uptime
里面的内容,就可以获取到秒的时间戳。
cat /proc/uptime | awk -F "." '{print $1}'
4643
只要在Makefile运行结束时,再获取一次/proc/time
里面的秒数,前后数值相减,便可以得到时间差,即项目的运行时间。
3、date命令转换时间戳为 年月日时分秒 格式
可以通过data --help
命令来查看data的用法。
data -d @时间戳 [format]
format用到以下的参数,获取日、时、分、秒
%j day of year (001..366)
%H hour (00..23)
%M minute (00..59)
%S second (00..60)
此时输入的时间戳是以1970年开始作为运算。如上面获取的4643
, 由于博主时UTC-8的时间,所以时间+了8小时,4643对应是9点17分23秒。
date -d @4643
1970年 01月 01日 星期四 09:17:23 CST
\- u选项
加上-u
选项,可以让时间以UTC为参考,不加时区偏移。
date -u -d@4643
1970年 01月 01日 星期四 01:17:23 UTC
就可以看到时间是1点17分23秒,正好是我们的系统运行时间,4643秒。
格式化截取时分秒
date -u -d @4643 +%Hh:%Mm:%Ss
01h:17m:23s
+%Hh:%Mm:%Ss
命令中的%H、%M、%S分别获取了时分秒,并以h:m:s
这样的格式输出。与printf函数类似。至此便可以获取到运行时间的时分秒。
实现
原来的Makefile
DIR = $(shell pwd)
KER_DIR = /lib/modules/`uname -r`/build
obj-m = msg_netlink.o
all:
make -C $(KER_DIR) M=$(PWD) modules
clean:
make -C $(KER_DIR) M=$(PWD) clean
改动后的Makefile
DIR = $(shell pwd)
KER_DIR = /lib/modules/`uname -r`/build
START_TIME = $(shell cat /proc/uptime | awk -F "." '{print $$1}') # Makefile进入,获取时间戳
obj-m = msg_netlink.o
all: module showruntime
module:
make -C $(KER_DIR) M=$(PWD) modules
clean:
make -C $(KER_DIR) M=$(PWD) clean
showruntime:
@current_time=`cat /proc/uptime | awk -F "." '{print $$1}'`; \
time_interval=`expr $${current_time} - $(START_TIME)`; \
runtime=`date -u -d @$${time_interval} +%Hh:%Mm:%Ss`; \
echo "######## runtime: $${runtime} ########"
分析
小知识:
- Makefile中,调用Makefile自身的变量,用
$
符号,比如Makefile中的START_TIME
。 - Makefile中,target执行的shell命令,定义的变量,需要用
$$
符号调用,比如showruntime中的current_time
,因为是shell运行过程生成的变量,需要用$$current_time
来调用. - 同样
START_TIME = $(shell cat /proc/uptime | awk -F "." '{print $$1}')
中,在终端命令中,获取awk参数用$1
即可,但由于是在Makefile中,调用shell,所以需要用$$1
才可以获取到分割数值。 - Makefile运行的target中,如showruntime,里面的shell命令,如果需要做多个shell命令,需要用
; \
符号来连续整个shell命令,否则会报错,详细查看showruntime
里面的内容。 - Makefile中
@字符
shell命令可以隐藏shell命令运行打印。
如
showruntime:
current_time=`cat /proc/uptime | awk -F "." '{print $$1}'`; \
time_interval=`expr $${current_time} - $(START_TIME)`; \
runtime=`date -u -d @$${time_interval} +%Hh:%Mm:%Ss`; \
echo "######## runtime: $${runtime} ########"
输出如下, 可以看到整个shell命令都打印出来了:
make -C /lib/modules/`uname -r`/build M=/home/xxx/Desktop/xxx/test/driver modules
make[1]: Entering directory '/usr/src/linux-headers-5.13.0-41-generic'
make[1]: Leaving directory '/usr/src/linux-headers-5.13.0-41-generic'
current_time=`cat /proc/uptime | awk -F "." '{print $1}'`; \
time_interval=`expr ${current_time} - 6710 `; \
runtime=`date -u -d @${time_interval} +%Hh:%Mm:%Ss`; \
echo "######## runtime: ${runtime} ########"
######## runtime: 00h:00m:00s ########
加上@符号后, 只会打印echo出来的字符。
showruntime:
@current_time=`cat /proc/uptime | awk -F "." '{print $$1}'`; \
time_interval=`expr $${current_time} - $(START_TIME)`; \
runtime=`date -u -d @$${time_interval} +%Hh:%Mm:%Ss`; \
echo "######## runtime: $${runtime} ########"
输出如下:
make -C /lib/modules/`uname -r`/build M=/home/xxx/Desktop/xxx/test/driver modules
make[1]: Entering directory '/usr/src/linux-headers-5.13.0-41-generic'
make[1]: Leaving directory '/usr/src/linux-headers-5.13.0-41-generic'
######## runtime: 00h:00m:00s ########
这里运行时间为0,是因为驱动已经编译过,没有改动,make clean后,再重新编译,即可以看到运行时间。
make clean
ake -C /lib/modules/`uname -r`/build M=/home/xxx/Desktop/xxx/test/driver clean
make[1]: Entering directory '/usr/src/linux-headers-5.13.0-41-generic'
CLEAN /home/xxx/Desktop/xxx/test/driver/Module.symvers
make[1]: Leaving directory '/usr/src/linux-headers-5.13.0-41-generic'
time make # 用了time一起统计时间
make -C /lib/modules/`uname -r`/build M=/home/xxx/Desktop/xxx/test/driver modules
make[1]: Entering directory '/usr/src/linux-headers-5.13.0-41-generic'
CC [M] /home/xxx/Desktop/xxx/test/driver/msg_test.o
MODPOST /home/xxx/Desktop/xxx/test/driver/Module.symvers
CC [M] /home/xxx/Desktop/xxx/test/driver/msg_test.mod.o
LD [M] /home/xxx/Desktop/xxx/test/driver/msg_test.ko
BTF [M] /home/xxx/Desktop/xxx/test/driver/msg_test.ko
Skipping BTF generation for /home/xxx/Desktop/xxx/test/driver/msg_test.ko due to unavailability of vmlinux
make[1]: Leaving directory '/usr/src/linux-headers-5.13.0-41-generic'
######## runtime: 00h:00m:02s ########
real 0m2.107s
user 0m0.821s
sys 0m0.301s
可以看到 time make
统计到的时间,与showruntime
计算得到的时间都是2秒左右。实验结果ok。
总结
- 熟悉/proc/uptime里面参数的含义。
- 可以不用/proc/uptime来获取时间戳,可以用
date +%s
获取当前时间戳,代替cat /proc/uptime
- 熟悉date命令的时候,获取时间戳、格式化获取日期。
- 仔细观看Makefile实现的小知识,这里面涉及到很多细节内容,建议反复观看。
- Makefile中的target依赖问题,仔细观看
showruntime
target所在位置。 - Makefile中
$`符号
与$$符号
的区别