linux HZ 值_操作系统 - Linux 内核模块的介绍&实践

通过此文,你会了解到怎么创建一个内核模块,并且加载到Linux内核中。然后修改这个内核模块,以便在/proc文件系统中创建一个条目。

开发内核模块的优势就是,这是一个与内核交互的相对而言比较容易的方法,因为可以让你写程序直接调用内核函数。很重要的是,你必须记住,你实际是在写直接于内核交互的内核代码。也就是说,你代码中的任何错误都会有可能导致系统的崩溃。

内核模块概览

首先是创建模块并把模块插入Linux内核中。你可以通过如下命令来列出当前加载的所有内核模块:

2e208c35124f5dc53c0f44b7a29e15bd.png

这个命令会把当前的内核模块列为3列,分别是名字,大小,以及在哪里被使用。

下面的代码是一个非常基本的内核模块,当它被加载或卸载时,会打印出适当的信息。

3402c277965b0f58293c2da0d49f35b9.png

函数simple_init()是模块入口点module entry point,代表当模块被加载时会调用的函数。同样的,函数simple_exit()模块出口点module entry point,当模块被从内核中移除时,这个函数会被调用。

模块入口点函数必须返回一个整数,0代表成功,其他任何值表示失败。模块出口点函数返回空。模块入口点和出口点函数都不能传递任何参数。下面两个宏是在内核中注册模块的入口和出口点的。

8f7b1cc8c7f5cbae9daa9df6d2c34616.png

这里需要注意的是之前代码途中,模块入口点和出口点的函数都调用了函数printk()。printk是内核中相当于printf()功能的函数,但它会把输出发送到内核日志缓存中,可以通过dmesg命令读取。printf()与printk()的一个区别是,printk()指定了优先级,这些值在中声明。在上文中,优先级是KERN_INFO,这是定义一条informational消息。

最后几行的MODULE_LICENSE(),MODULE_DESSCRIPTION(),以及MODULE_AUTHOR(),代表了有关软件许可证,模块的描述以及作者的相信信息。对于我们来说,其实是不需要的,但我们这么做是因为这是开发内核模块时的标准实际操作之一。

因为是在ubuntu中演示,首先下载Linux源代码:

sudo apt-get install linux-headers-$(uname -r)

sudo apt-get install linux-source

以下是测试目录结构:

d977c493e423c51301ff6bfae42f42fc.png

下面是Makefile中的内容:

899f997ae857e47e3769ffd48155bde6.png

在命令行中输入:

b3c49839bd6e450b4124e650a86cd115.png

编译会产生几个文件,其中simple.ko就是编译出的内核模块。下面是怎么插入到内核中的步骤:

加载和移除内核模块

内核模块的加载是通过使用insmod命令实现的,如下运行:

6e90c19d00f3230db85b0c48b5aeb1b1.png

为了验证模块是否被加载,可以运行lsmod命令,然后查找simple。

4a1463d2272f1578fae0e6c5d1dcd006.png

因为我们在模块的入口点调用的函数会打印信息。信息,通过如下核实在内核日志缓存中的消息:

56582063132a68859dca051203afeeb2.png

移除内核模块是通过调用rmmod命令实现的,这里需要注意的是后缀.ko不是必须的:

cfcaa2846fca8b09bc33d85dd267e156.png

再次通过dmesg确认模块已经被移除:

a85b5334c8e8d8802d1dff6e6d6d9e91.png

因为内核日志缓存很容易被填满,所以定期性的清空缓存是有意义的,命令如下:

bec7d4674445f89d8e1f07d3a3c78922.png

由于内核模块是在内核中运行的,所以获取一些只在内核而不在用户应用中的值和函数调用是可能的。比如说,Linux中的头文件定义了几个哈希函数,这个文件也定义了一个常数GOLDEN_RATIO_PRIME(这被定义为一个unsigned long)。这个值可以被如下打印:

d9a458a3c665bc32c44efc00e02dd524.png

另一个例子是头文件定义了如下还是函数:

unsigned long gcd(unsigned long a, unsigned b);

这个函数会返回参数a和b的最大公约数。

下面是在simple_init()打印GOLDEN_RATION_PRIME以及在simple_exit()打印出3300和24的最大公约数的试验步骤及结果:

8416205d18153bb03840e7fabd43f4cd.png
5c37f849756da41a5b767004c10f24d3.png
571c2fcab99c59148809ea14b4bd6a0f.png

在Linux中,计时器timer的滴答速度(tick rate)为中定义的HZ值。 HZ的值决定了定时器中断的频率,其值因机器类型和体系结构而异。例如,如果HZ的值为100,则定时器中断每秒发生100次,也就是每10ms一次。另外,内核跟踪全局变量jiffies,该变量维护自系统启动以来发生的计时器中断数。 jiffies变量在文件中声明。

下面是在simple_init()函数打印出jiffies和HZ以及在simple_exit()再次打印jiffies的试验步骤:

cc15e8b9c401adc77f2b255215e196a5.png
c6141aace8963960d9f3fa2ff6cf30b7.png

这里需要注意的是,使用simple_init()以及simple_exit()中的jiffies可以知道从内核模块加载到益处一共过去了多少秒。

/proc文件系统

/proc文件系统是一个只存在与内核内存中的伪文件系统,主要用来查询各种内核和单个进程的统计信息。

下面我们实现的内核模块是在/proc中创建额外的条目,其中包含内核统计和指定进程的信息。代码实现如下:

df52ab874b6be54ff33c684bdfc61389.png

Makefile如下:

551d9d64ba3cd4412c4eed3db229184f.png

我们首先描述如何在/ proc文件系统中创建新条目。上述程序示例(名为hello.c)创建一个名为/ proc / hello的/ proc条目。如果用户输入如下命令:

69a5b6e1c6cd29ccb083cd0be51a81e3.png

则会返回Hello World。

在模块进入点proc_init(),我们创建了通过使用proc_create()函数创建了/proc/hello条目。此函数通过proc_ops传递,其中包含对struct文件操作的引用。这个结构体初始化成员owner和read。read的值是函数proc_read()的函数名,当/proc_hello被读取时会被调用。

通过检视proc_read() 函数,我们可以看到字符串“Hello World”被写入变量buffer,而buffer存在于内核空间。因此/proc/hello可以用用户空间访问,我们必须通过copy_to_user函数把buffer中的内容复制到用户空间。因此,这个函数就是把内核内存buffer的内容复制到用户空间中的usr_buf。

每次/proc/hello读取时,函数proc_read会被重复调用,直到它返回0,所以必须确保这个函数在一旦收集到数据后,返回0,以便返回到/proc/hello文件中。

最后,需要注意的是/proc/hello会被proc_exit()调用后被删除,因为其中调用了remove_proc_entry()。

f0c3fbb0ce1de3058cd586dec423fd4d.png

下面是两个练习,这里只贴出了关键代码:

  1. 设计一个内核模块,它创建了一个proc文件/proc/jiffies,当/proc/jiffies文件被读取时,报告当前jiffies的值
a04e998d14ae8a679c113e65f5990cca.png
6256754c4d9ad8e7013da3d5a6bc5947.png
  1. 设计一个内核模块,它创建了一个proc文件/proc/seconds,当/proc/seconds文件被读取时,报告自内核模块装载后过去了多少秒。
258495959c167411800d1d03ffbec9fd.png
cb0888a2206772d8d65488cbad0f15c3.png
0137c33082729ddc8c9f55adabd1288c.png
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值