![](/style/images/good.png)
![](/style/images/bad.png)
Linux内核模块开发(笔记)
source link: https://blogread.cn/it/article/4630?f=hot1
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Linux内核模块开发(笔记)
个人笔记。。在不放过来都快找不到了。有空还得好好整理一下了。
kernel oops messages
内核模块简单介绍
最简单的内核模块
注:如果是 redhat 安装的话,需要安装 kernel-devel 才能写内核模块,如果是自己编译内核,记的不要删除源码,不然没法开发模块。
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
/* __init 的标记是内核模块的入口,这个函数加载完后就会释放内存空间 */
static
int
__init hello_init(
void
)
{
printk(KERN_INFO
"Hello world"
);
/* 打印的信息会出现在 dmesg 中 释放*/
return
0;
/* 返回 0 是正常 */
}
/* __exit 的标记是退出内核模块,当这个模块卸载时会执行 */
static
void
__exit hello_exit(
void
)
{
printk(KERN_INFO
"Goodbye world"
);
}
/* 下面这二个是宏,初始化和消除函数时使用和上面的装载卸载模块没关系。 */
module_init(hello_init);
module_exit(hello_exit);
放个编译上面模块的 Makefile
obj-m := hello.o
all:
make
-C
/lib/modules/$(shell uname
-r
)/build M=$(shell pwd) modules
clean:
make
-C
/lib/modules/$(shell uname
-r
)/build M=$(shell pwd) clean
给 Makefile 放到上面 hello.c 的相同的目录中(如果上面写的模块代码叫 hello.c 的话)。然后使用 make 就能编译了。
insmod lsmod rmmod
调用 insmod 时会给需要的模块加载进内核,会给 ko 的文件以目标代码加载。装载时会调用 module_init 指定的函数。退出也调用相应的 module_exit.
lsmod 可以显示你写的模块,其实是读 /proc/modules 。接下来我写写怎么样自己通过内核来建 proc 文件。
模块加载参数
如果在模块加载时,想指定参数,也提供了相应的头文件
#include <linux/moduleparam.h>
static
int
test;
module_param(test,
int
, 0644);
这样以后,直接在内核模块内使用 test 的变量就行了。
模块的信息
在程序中可以为模块加一些描述,发行版权声明,和作者。
MODULE_LICENSE(
"GPL"
);
MODULE_DESCRIPTION(
"Test"
);
MODULE_AUTHOR(
"xxx"
);
模块的符号导出
在 Perl 中,模块是可以导出变量和方法到其它的模块中的。在 Linux 内核中也有这样的方法。
EXPORT_SYMBOL(name);
EXPORT_SYMBOL_GPL(name);
这二个可以导出指定的全局变量,也可以是方法。这个要加载 <linux/module.h> 的头文件,不要忘记了。
其它的模块要使用这个,直接使用 extern void name(void); 就可以使用了。这些导出的函数只能内核和内核模块使用。不能用户调用,可以由 /proc/kallsyms 来查看导出的变量和方法
写个内核模块,通过 proc 可以见到一些信息,通过 proc 的读和写的功能。来实现设置和读取信息。
proc 介绍
proc 是一个非常方便的用来动态的向 Linux 内核加入和禁用代码的一个方法。
proc/sys 中是用来配置内核的参数,可以通过 sysctl -w key=value
象普通文件可以支持 open,read,write,close
例如
读
cat /proc/cpuinfo
echo fukei-name > /proc/sys/kernel/hostname
proc 的功能实现
proc 在 c 中是一个结构体来实现的,是 struct proc_dir_entry 。它可以给读写绑定到特定的函数上。然后通过别人对 proc 中文件的操作来触发和回调相应的绑定的函数。
read_proc 和 write_proc 是这个结构体的成员,也是一种结构体。函数就注册在这个上面。有兴趣的同学可以看看 include/linux/proc_fs.h 中的 read_proc_t 和 write_proc_t 的定义。
实现起来也简单。
struct
proc_dir_entry *proc_entry = create_proc_entery(....);
int
my_read_proc()
{
}
proc_entry->read_proc = my_read_proc();
在这的 create_proc_entery 会返回一个 proc_dir_entry 的结构体的引用。失败就是 NULL 。
这样,当用户空间进行 read 的系统调用时,如使用 cat proc 中的内容时。内核会调用注册到 read_proc 上的这个 my_read_proc 来实现的.
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/mm.h>
#define MODULE_NAME "Memory"
int
my_read_proc(
char
*page,
char
**start, off_t off,
int
count,
int
*eof,
void
*data)
{
struct
task_struct *tsk = current;
int
len;
len =
sprintf
( page,
"This module info: task %s pid %d\n"
,tsk->comm, tsk->pid );
return
len;
}
struct
proc_dir_entry *proc_entry;
int
init_module(
void
)
{
proc_entry = create_proc_entry(MODULE_NAME, 0644, NULL);
if
(proc_entry==NULL){
remove_proc_entry(MODULE_NAME, NULL);
}
proc_entry->read_proc = my_read_proc;
return
0;
}
void
cleanup_module(
void
)
{
remove_proc_entry(MODULE_NAME, NULL);
// 退出和出错记的删除
}
MODULE_LICENSE(
"GPL"
);
建议继续学习:
扫一扫订阅我的微信号:IT技术博客大学习
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK