技术标签: Linux内核模块开发套路
在32位的操作系统上,Linux内核将4G的空间分为了0-3G
的用户空间和3-4G
的内核空间,具体可以自己画出内存的模型来分析。其中用户程序是运行在用户空间的,用户空间通过中断
或者系统调用
的方式进入内核空间。在Linux中很多功能或者外设都可以编译成模块,在系统运行时动态地注册
或者卸载
,在此过程无需重启整个系统。
内核模块需要包含内核相关的头文件,根据功能不同,所需要的头文件也会有所不一样,但是这两个头文件是所有的内核模块都必须要包含的:
#include <linux/module.h>
#include <linux/init.h>
模块的初始化负责注册模块本身。只有已注册的模块,其内部方法才可以被应用程序锁使用。模块初始化的功能相当于模块和内核之间衔接的桥梁,告知内核:我注册好了,已经做好准备随时为你服务
。
通常的模块的初始化函数如下所示:
/*
1. 初始化代码一般都声明为static
2. __init 标识初始化函数仅在初始化期间使用,一旦初始化完毕,将释放初始化函数所占用的内存。
3. module_init是必须的,当使用insmod将内核加载进模块的时候,初始化函数将会被执行。
4. 模块初始化只与内核模块管理子系统打交道,不与应用程序交互。
*/
static int __init module_init_func(void)
{
/*TODO: 初始化代码*/
}
module_init(module_init_func);
当系统不再需要某个模块时,可以卸载这个模块释放该模块所占用的资源。模块的退出相当于告知内核:我要退出了,不能在为你服务了
。通常的模块的退出函数如下所示:
/*
1. 模块退出代码没有返回值。
2. __exit标志这段代码用于模块卸载。
3. module_exit不是必须的,但是没有module_exit定义的模块无法被卸载。
4. 使用rmmod卸载模块时,退出函数将被执行。
5. 模块初始化只与内核模块管理子系统打交道,不与应用程序交互。
*/
static viod __exit module_exit_func(void)
{
/*TODO: 模块退出代码*/
}
module_exit(module_exit_func);
非必须,主要是与应用程序交互(其实就是填充file_operations结构体),常见的模块各种方法有:
xxx_open()
xxx_release()
xxx_read()
xxx_write()
xxx_ioctl()
file_operations结构体在内核源码include/linux/fs.h中定义,源码如下所示:
struct file_operations
{
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
};
用户程序通过系统调用(open/release/read/write/ioctl
等),进入到内核模式,使用这些方法。常见的file_operations结构体定义如下,根据实际情况进行填充:
static struct file_operations xxx_fops =
{
.owner = THIS_MODULE,/*固定的*/
.open = xxx_open,
.write = xxx_write,
.read = xxx_read,
.release = xxx_release,
.ioctl = xxx_ioctl,
};
Linux内核源码是开源的,遵守GPL协议,所以我们编写出来的模块一定要指定相关的开源协议,否则是不能被静态编译进内核模块。使用MODULE_LINCENSE
来指定遵守的协议:
MODULE_LINCENSE("GPL");
模块的编写者可以为所编写的模块增加一些描述的信息,比如作者、模块描述、以及版本号等(非必须的):
MODULE_AUTHOR("wxb");
MODULE_DESCRIPTION("hello world");
MODULE_VERSION("V1.01");
所有的内核符号默认都是不导出的。如果希望一个模块的符号能被其它模块使用,则必须显式的用 EXPORT_SYMBOL 将符号导出:
EXPORT_SYMBOL(module_symbol);
Linux内核允许模块在加载时指定参数。模块接收UC桉树传入能够实现一个模块在多个系统上运行,可以根据传入的参数提供不同的服务。模块参数必须使用module_param来声明,通常放在文件的头部,module_param需要三个参数:变量名称,变量类型,访问掩码。如:
static int num = 5;
module_param(num,int,S_IRUGO);/* 传入int类型数据 */
static char str_buff[] = "hello world";
module_param(str_buff,charp,S_IRUGO);/* 传入char指针类型数据 */
一张图进行总结,内核模块开发的套路
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
static int __init hello_init(void)
{
printk(KERN_INFO"hello init!\r\n");
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_INFO"hello exit!\r\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wxb");
MODULE_DESCRIPTION("hello module example");
MODULE_VERSION("V1.00");
# 获取内核版本信息
KERN_VER = $(shell uname -r)
# 获取内核源码树位置信息
KERN_DIR = /lib/modules/$(KERN_VER)/build
# 将hello.c编译成一个模块(m—模块,y-静态链接到zImage中,-n表示不编译)
# 这一行是必不可少的,实际上Makefile也只需要这一行就能搞定。
# 假如有个新的模块module.ko,它依赖于两个文件file1.c和file2.c的话,这一行应该改为:
# obj-m +=module.o
# module-objs := file1.o file2.o
obj-m += hello.o
# all 目标为 make modules 表示编译为模块
# -C 表示进入到指定目录下借用内核源码的体系进行编译链接
# M=`pwd`把当前路径记录到M中,编译完成之后拷贝回M
all:
make -C $(KERN_DIR) M=`pwd` modules
# 清除目标文件
clean:
rm -rf *.ko *.mod.c *.mod.o *.o *.order *.symvers
.PHONY: clean
使用root身份加载和卸载模块
sudo insmod hello.ko //加载模块
sudo rmmod hello.ko //卸载模块
lsmod //查看已有的模块
modinfo hello.ko //查看模块相关描述
#include <linux/module.h>
#include <linux/init.h>
static int num = 1;
static char *str = "hello world";
/*指定模块传入参数的类型*/
module_param(num,int,S_IRUGO);
module_param(str,charp,S_IRUGO);
static int __init hello_init(void)
{
printk(KERN_INFO"%s,str=%s,num=%d\r\n",__FUNCTION__,str,num);
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_INFO"hello_exit\r\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wxb");
MODULE_DESCRIPTION("this is hello param example,param int num,charp str");
MODULE_VERSION("V1.01");
KERN_VER = $(shell uname -r)
KERN_DIR = /lib/modules/$(KERN_VER)/build
obj-m += hello_param.o
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
rm -rf *.ko *.mod.c *.mod.o *.o *.order *.symvers
.PHONY:clean
执行结果如下所示:
文章浏览阅读2k次。照样画葫芦,用python编写了一段小程序,可以使用ipad的web浏览器远程监控远端iMac主机界面(每秒截屏,非流控),与大家分享。1. 首先介绍一下需要下载的第三方工具:Flask,PyscreenshotFlask用来做web服务器,Pyscreenshot是用来截屏的。用pip install 分别安装即可2. 介绍程序文件架构如下,需要simplesvr_mac 监控截屏
文章浏览阅读4.7k次,点赞3次,收藏7次。安装完centos7后进入时一直不停出现pcieport 0000:00:1c.5字样,这个的具体原因尚不完全清楚,解决方法查到的都是一种,就是在/etc/default/grub中的GRUB_CMDLINE_LINUX的内容最后添加pci=nomsi或者pci=noaer或者pcie_aspm=off,这样的确可以,但是更新的步骤需要grub2-mkconfig -o /boot/efi/EFI..._dpc:error containment capabilities
文章浏览阅读735次。原文:http://colah.github.io/posts/2015-08-Understanding-LSTMs/Recurrent Neural NetworksHumans don’t start their thinking from scratch every second. As you read this essay, yo_humans don鈥檛 start their thinking from scratch every second.
文章浏览阅读1.4k次。<?xml version="1.0" encoding="UTF-8"?><!--Licensed to the Apache Software Foundation (ASF) under oneor more contributor license agreements. See the NOTICE filedistributed with this work for additional informationregarding copyright ownersh._apache-maven-3.9.2\conf\settings.xml
文章浏览阅读6.4k次。当解决复杂的逆向问题时,我们常使用radare2或IDA等成熟工具进行反汇编和调试。但有时也需要深入挖掘并了解它们是如何运作的。编写一些反汇编脚本对于自动化某些流程非常有用,并且可以形成自己的逆向工具链。至少,这是我现在正在尝试的事情。配置环境如标题所说的那样,你需要先安装Python 3。如果你无法确定是否安装了Python 3,可以运行如下命令:其中capstone是..._逆向工程 python
文章浏览阅读2.6k次。//先排序然后查找void FindNumsAppearOnce1(vector<int> data, int* num1, int *num2) { if (data.size() < 2) return; sort(data.begin(), data.end()); vector<int> res; for (int i = 0; i <..._一个数组中找出出现一次的2个数字
文章浏览阅读333次。本文是 Piasy 原创,发表于 https://blog.piasy.com,请阅读原文支持原创 https://blog.piasy.com/2017/06/17/out-of-the-box-webrtc-dev-env/在刚刚落幕的 WWDC17 上,苹果为我们带来了一个不小的惊喜 —— 其浏览器内核WebKit将正式支持 WebRTC,而未来基于 WebKit 内核的苹果浏览器,比如m..._xujianzhu webrtc开箱即用
文章浏览阅读3.3w次,点赞5次,收藏21次。一直用VGG训练,几天前想看下ResNet的效果如何,因为SSD源码中有python实现的ResNet网络结构实现代码,包含ResNet101和ResNet152,直接拿ResNet101来训练,GTX1060配置,batchsize竟然只降到2才跑的起来,果然一直收敛不了。看了下model_libs.py里面的实现代码:def ResNet101Body(net, from_layer, u_resnet50 使用什么代替
文章浏览阅读1.1w次,点赞12次,收藏131次。目录:1、在线逻辑分析仪简介2、HDL 实例化调试探针流程(实验-闪烁灯)3、Hardware Manager中观察调试信号4、网表插入调试探针流程(实验-闪烁灯)1、在线逻辑分析仪简介在线逻辑分析仪借用了传统逻辑分析仪的理念以及大部分的功能,并利用 FPGA 中的逻辑资源,将这些功能植入到 FPGA 的设计当中。一般地,在线逻辑分析仪的应用原理框图如下图所示: 待测设计(Design Under Test,DUT)就是用户逻辑,它和片内的在线逻辑分析仪都位于 FPGA中。在线逻辑分_vivado ila 下一触发沿
文章浏览阅读3.5k次。今天发现一个问题,问题大概是这样的,查询interface的信息,在本地使用本地的数据库访问没有问题,但是发布到服务器上以后访问速度就特别的忙,需要5分钟左右才能返回数据,这肯定是无法让人接受的,刚开始以为是服务器性能的问题,为了验证就把服务器上的数据库备份到本地,发现本地的速度也马上慢了下来,到底是什么问题的。看了一下查询interface的sql语句不禁吓了一跳: _db2数据库索引的使用
文章浏览阅读3.1k次。一 , 当前mysql的最新版本是5.5.25a。到http://dev.mysql.com/downloads/mysql/下载mysql安装文件 。我们这里下载mysql-5.5.25a-win32.msi就可以了,下载完,直接点击安装。mysql有好几个版本,稍微了解下各个版本之间的区别: MySQL Community Server :社区版本 不提供官方技术支持,是免费的_[root@gaojiao ~]# mysql -uroot error 1045 (28000): access denied for user 'r
文章浏览阅读2.3k次。群发消息接口订阅号:每天一条的群发权限服务号:每月(自然月)4条群发权限实例<?php/** * 群发接口 * PS:群发之前调用“预览接口”进行测试 * PS:通过第三方后台调用微信上传图片素材接口,获取图片url,如:{"url":"http:\/\/mmbiz.qpic.cn\/mmbiz_jpg\/BdxWN2kspVgJOFpRHJojlWmbl0pM..._微信公众号根据标签群发接口支持数组传参吗