第一个驱动模块 __init __exit printk函数分析

第一个驱动模块 __init __exit printk函数分析
代码:#include <linux/module.h>
#include <linux/init.h>

// 模块安装函数
static int __init chrdev_init(void)
{
printk(KERN_INFO “chrdev_init helloworld init\n”);
return 0;
}

// 模块下载函数
static void __exit chrdev_exit(void)
{
printk(KERN_INFO “chrdev_exit helloworld exit\n”);
}

module_init(chrdev_init);
module_exit(chrdev_exit);

// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE(“GPL”); // 描述模块的许可证
MODULE_AUTHOR(“Mark”); // 描述模块的作者
MODULE_DESCRIPTION(“test for driver”); // 描述模块的介绍信息
MODULE_ALIAS(“alias test”); // 描述模块的别名信息


分析:
默认情况下函数是放在.text段里面的,有了下面的宏就不一样了。
__init :在linux/init.h里面
#define __init __section(.init.text) __cold notrace
往下分析:
define __section(S) attribute ((section(#S)))
意思就是把chrdev_init函数放到了.init.text段。整个内核中的所有的这类函数都会被链接器链接放入.init.text段中,所以所有的内核模块的__init修饰的函数其实是被统一放在一起的。内核启动时统一会加载.init.text段中的这些模块安装函数,加载完后就会把这个段给释放掉以节省内存


__exit :在linux/init.h里面
#define __exit __section(.exit.text) __exitused __cold
把chrdev_exit函数放到了.exit.text段


lsmod 显示的时候会把最后面安装的模块显示在最前面。
printk函数分析:
printk的打印级别是用来控制printk打印的这条信息是否在终端上显示的。从0-7的紧急程度依次下降,0是打印级别最高的
#define KERN_EMERG “<0>” /* system is unusable /
#define KERN_ALERT “<1>” /
action must be taken immediately /
#define KERN_CRIT “<2>” /
critical conditions /
#define KERN_ERR “<3>” /
error conditions /
#define KERN_WARNING “<4>” /
warning conditions /
#define KERN_NOTICE “<5>” /
normal but significant condition /
#define KERN_INFO “<6>” /
informational /
#define KERN_DEBUG “<7>” /
debug-level messages */

操作系统的命令行中也有一个打印信息级别属性,值为0-7。
当前操作系统中执行printk的时候会去对比printk中的打印级别和我的命令行中设置的打印级别,小于我的命令行设置级别的信息会被放行打印出来,大于的就被拦截的。譬如我的ubuntu中的打印级别默认是4,那么printk中设置的级别比4小的就能打印出来,比4大的就不能打印出来。
查看操作系统的命令行的printk的级别:
cat /proc/sys/kernel/printk
设置操作系统的命令行的printk的级别:
echo 7 > /proc/sys/kernel/printk

所以这里总结一下就是:
操作系统命令行可以设置打印信息级别的:0-7
printk也有8种打印级别,printk的打印级别小于命令行设置的,就可以打印出来。
例如:命令行设置打印界别为3,那么printk中就只有0 1 2可以打印出来。


头文件说明:
驱动源代码中包含的头文件和原来应用编程程序中包含的头文件不是一回事。应用编程中包含的头文件是应用层的头文件,是应用程序的编译器带来的(譬如gcc的头文件路径在 /usr/include下,这些东西是和操作系统无关的)。驱动源码属于内核源码的一部分,驱动源码中的头文件其实就是内核源代码目录下的include目录下的头文件


发现了一个很有意思的现象:
我们编译模块的时候,可以不用指定编译器,指定内核为ubuntu的时候,它是使用的gcc。指定内核为ARM的时候,是使用的arm-linux-gcc,猜想与内核中的Makefile有关,它可能指定了。


将程序放到ARM板子上面去运行:
insmod module_test.ko,安装ok,printk(KERN_INFO “chrdev_init helloworld init\n”);,,打印出现。我们终端的打印级别设置的7,printk使用的6就打印出来了。当我把终端设置为6的时候,再次加载就没有了打印信息。
modinfo查看模块信息:
modinfo: can’t open ‘/lib/modules/2.6.35.7/modules.dep’: No such file or directory
解决方法:
1.进入内核源码目录kernel
1.1.执行模块编译命令,编译模块(指定为-M的)
make modules
1.2.执行模块安装命令 (把指定为-M的模块安装到某个目录下面去)
make modules_install INSTALL_MOD_PATH=/opt/
结果是在/opt目录下面会新生成一个新目录lib
2.将生成的lib下所有的东西拷贝到开发板使用的rootfs/lib下即可
mv /opt/lib/* /opt/rootfs/lib
3.在用modinfo查看模块信息,ok

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`remap_pfn_range`函数是用来将物理页面映射到用户空间的函数。以下是一个使用`remap_pfn_range`函数的例子: ```c #include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/miscdevice.h> #include <linux/uaccess.h> #include <linux/mm.h> MODULE_LICENSE("GPL"); #define DEVICE_NAME "mydev" // 映射的物理页面 #define PHYSICAL_PAGE (0x1234) // 映射的大小 #define MAPPING_SIZE (PAGE_SIZE) // 映射的起始地址 #define MAPPING_ADDRESS (0x80000000UL) // 打开设备 static int mydev_open(struct inode *inode, struct file *file) { return 0; } // 读取设备 static ssize_t mydev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { return 0; } // 写入设备 static ssize_t mydev_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { return 0; } // 映射物理页面到用户空间 static int mydev_mmap(struct file *file, struct vm_area_struct *vma) { int ret; unsigned long pfn = PHYSICAL_PAGE >> PAGE_SHIFT; unsigned long size = vma->vm_end - vma->vm_start; // 确保映射大小不超过规定大小 if (size > MAPPING_SIZE) { return -EINVAL; } // 映射页面 ret = remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot); if (ret < 0) { return ret; } return 0; } // 设备操作结构体 static const struct file_operations mydev_fops = { .owner = THIS_MODULE, .open = mydev_open, .read = mydev_read, .write = mydev_write, .mmap = mydev_mmap, }; // 设备信息结构体 static struct miscdevice mydev_miscdev = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &mydev_fops, }; // 模块加载函数 static int __init mydev_init(void) { int ret; // 注册设备 ret = misc_register(&mydev_miscdev); if (ret < 0) { printk(KERN_ERR "Failed to register misc device: %d\n", ret); return ret; } printk(KERN_INFO "Registered misc device: %s\n", DEVICE_NAME); return 0; } // 模块卸载函数 static void __exit mydev_exit(void) { // 注销设备 misc_deregister(&mydev_miscdev); printk(KERN_INFO "Unregistered misc device: %s\n", DEVICE_NAME); } module_init(mydev_init); module_exit(mydev_exit); ``` 这个例子创建了一个名为`mydev`的杂项设备,并将一个物理页面`0x1234`映射到用户空间的`0x80000000UL`地址处。当用户空间程序打开设备并执行`mmap`操作时,`mydev_mmap`函数会被调用,其中调用了`remap_pfn_range`函数来将物理页面映射到用户空间。用户空间程序可以通过访问`0x80000000UL`地址来访问这个物理页面。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值