linux内核结构以及内核模块编程(笔记)

先从最简单的开始,Linux内核中想要打印一个hello world,如何实现?
区别于传统的C++编程,Linux内核中没有常用的库函数,因此需要做出修改

内核的Hello,World模块

内核没有显示终端的,因此输出会输出到日志文件中
创建一个example_example.c的源文件

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
// #include<linux/inotify.h>

/*  
    模块的初始化函数lkp_init()
    __init用于初始化的修饰符, 告诉编译程序, 执行完之后需要回收内存
*/
static int __init lkp_init(void){
    printk("<1> Hello, World: from the kernel space...\n");
    return 0;
}
/*  
    模块的退出和清理函数lkp_exit()
*/
static void __exit lkp_exit(void){
    printk("<1> Goodbye, World: leaving kernel space...\n");
}

module_init(lkp_init);
module_exit(lkp_exit);
/*
    模块的许可证声明GPL
*/
MODULE_LICENSE("GPL");
  • printf修改为printk,在kernel.h中
  • module在module.h中

之后通过Makefile进行编译,同目录下创建一个Makefile文件,之后使用make命令编译即可

obj-m:=module_example.o # 这里是指明使用module_example.o建立一个模块,生成一个可加载模块module_example.ko的模块
CURRENT_PATH := $(shell pwd) # 当前路径
LINUX_KERNEL := $(shell uname -r)
LINUX_KERNEL_PATH := /usr/src/kernels/$(LINUX_KERNEL)

all:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules 
	# 编译模块 -C表示在指定内核源码位置编译,-M表示编译的模块源文件地址
clean:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean

为什么要用make编译,而不是gcc,因为内核编程和用户态编程不一样,内核使用的是kbuild编译系统,需要用内核可加载模块的makefile。

  • 通过insmod module_example.ko命令加载模块
  • 通过rmmod module_example命令卸载模块
  • 通过lsmod查看加载的模块
  • 通过cat /var/log/messages命令查看日志记录,输出结果会保存在日志中

注意事项

  • 关于代码提示
    • 我是在windows通过vscode的remote-ssh链接远程centos7的,因此通过vscode进行代码提示。
    • 可能识别不到一些头文件,可以手动找到所在库目录添加进去即可,参考之前我写的vscode配置教程:https://blog.csdn.net/qq_40482358/article/details/130745861,
    • 简单来说,就是在.vscode中的c_cpp_properties.json中的includePath中补充进去
  • 关于编译问题
    • 如果编译的时候,gcc找不到库(例如linux/module.h),可以通过find -name module.h命令找文件的位置。再通过
    `gcc -print-prog-name=cc1plus` -v
    
    命令查看gcc的编译路径,如果没有包含,则在环境变量中将gcc的编译路径加进去
    vim ~/.bashrc
    export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/usr/src/kernels/3.10.0-1160.88.1.el7.x86_64/include
    source ~/.bashrc
    
    • 如果出现了好几个目录,比如我之前安装GPU驱动的时候升级过内核(3.10.0-1062升级到了3.10.0.1160以匹配kernel-devel和kernel-headers)所以我的/usr/src/kernels/目录下就有两个文件夹,这两个文件夹中都有module.h,我们只需要使用当前内核版本的就行了,可以通过uname -r命令查看当前内核版本。
  • 关于内核版本问题
    • 内核版本不一致可能在一些地方不太一样, centos7默认是3.10.0, 可以看到我上面的内核版本是3.10.0-1160.88.1.el7.x86_64,可以通过yum distro-sync命令升级内核版本.然后安装与内核版本一样的devel和headeryum install "kernel-devel-uname-r == $(uname-r).

Linux内核模块和C应用的对比

Linux内核编程和普通的C语言编程不太一样:

c语言应用程序内核模块程序
使用函数LibC库内核函数
运行空间用户空间内核空间
运行权限普通用户超级用户
入口函数main()module_init()
出口函数exit()module_cleanup()
编译gcc -cmake
连接gccinsmod
运行直接运行insmod
调试gdbkgdb

参考资料

  • 学堂在线-Linux内核分析与应用:https://next.xuetangx.com/course/XIYOU08091001441/14767915
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux中,可以使用C语言编写内核模块,实现对内核功能的扩展。以下是Linux内核模块的基本编程方法: 1. 编写模块源代码:首先需要编写C语言代码,实现所需功能。 2. 编写Makefile文件:Makefile文件用于编译模块源代码,生成模块二进制文件。 3. 编译模块:使用make命令编译模块。 4. 加载模块:使用insmod命令将模块加载到内核中。 5. 卸载模块:使用rmmod命令将模块从内核中卸载。 以下是一个简单的内核模块示例,实现了对内核信息的输出: ```c #include <linux/module.h> #include <linux/kernel.h> int init_module(void) { printk(KERN_INFO "Hello world!\n"); return 0; } void cleanup_module(void) { printk(KERN_INFO "Goodbye world!\n"); } ``` 其中,init_module函数是模块的入口函数,在模块加载时被调用;cleanup_module函数是模块的出口函数,在模块卸载时被调用。可以使用printk函数输出信息。 编写完模块源代码和Makefile文件后,使用make命令编译模块,生成模块二进制文件。然后使用insmod命令将模块加载到内核中,使用rmmod命令将模块从内核中卸载。 例如,将上述示例保存为hello.c和Makefile文件,执行以下命令进行编译和加载: ```shell make sudo insmod hello.ko ``` 加载成功后,可以使用dmesg命令查看内核信息输出。卸载模块可以使用以下命令: ```shell sudo rmmod hello ``` 需要注意的是,内核模块编程需要了解Linux内核的相关知识,并且需要谨慎操作,避免对系统造成影响。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值