空间商网站ip被攻击后换ip,传奇世界官网,雅布设计平面,网站站长登录方式内核模块传参 内核模块#xff1a; int a , b; 安装内核模块时#xff1a;insmod demo.ko a 100 b 10;
1.内核模块传参的意义 在安装内核模块时给内核模块中的变量进行数值传递#xff0c;这样可以让我们的内核模块向上兼容更为复杂的应用程序#xff0c;向下适配多种硬件…内核模块传参 内核模块 int a , b; 安装内核模块时insmod demo.ko a 100 b 10;
1.内核模块传参的意义 在安装内核模块时给内核模块中的变量进行数值传递这样可以让我们的内核模块向上兼容更为复杂的应用程序向下适配多种硬件
2.内核模块传参相关API 1.函数原型module_param(name, type, perm) 功能 声明可以进行内核模块传参的变量 参数name : 变量名 type要传参的数值类型---》byte(传递char), hexint(16进制的int)、short 、int、uint、long、ulong、charp(传递字符指针)、bool(0/1 y/n Y/N) perm文件权限通过module_param声明了要传参的变量那么在sys/module/当前模块/parameters/下会生成一个以当前变量为名的文件文件的权限为perm指定的权限文件内容为变量的值 注通过modinfo查看您当前内核模块可以进行命令行传参的变量有哪些 2.函数原型MODULE_PARM_DESC(_parm, desc) 功能添加要传参的变量的描述这个描述也可以通过modinfo查看 参数_parm :传参的变量名 desc添加的描述 注给char类型的变量传参时需要传递对应的ASCII十进制形式 给字符指针传递字符串时字符串不可以有空格若有空格空格前的被当作参数传递给变量后面的会被认为时一个不认识的变量 示例代码
内核导出符号表 1.导出符号表的意义
到处符号表可以让不同模块之间实现资源的相互访问 比如模块2想要访问模块1的资源只需将模块1资源的符号表导出给模块2此时模块2就可以访问模块1的资源了
2.导出符号表的相关API EXPORT_SYMBOL(变量名|函数名) 或者 EXPORT_SYMBOL_CPL(变量名|函数名) 3.导出符号表测试实例
3.1编写代码
定义demo1.c,完成函数的定义
#include linux/init.h
#include linux/module.h
int add(int i,int j)
{return ij;
}
//生成add的符号表文件
EXPORT_SYMBOL(add);
static int __init mycdev_init(void)
{return 0;
}
static void __exit mycdev_exit(void)
{}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE(GPL); 定义dem2.c,完成调用demo1.c中的函数
#include linux/init.h
#include linux/module.hextern int add(int i,int j);
static int __init mycdev_init(void)
{printk(调用模块1函数执行结果为%d,add(3,5));return 0;
}
static void __exit mycdev_exit(void)
{}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE(GPL);
3.2编译
先编译demo1.c将生成的符号表文件Module.symvers复制到demo2的路径下再编译demo2.c
此时通过modinifo查看demo2.ko显示demo2依赖于demo1 注意 在高版本的内核中不支持符号表文件直接复制不然编译报未定义错误
解决办法在demo2的Makefile中加上demo1符号表的文件路径3.3安装流程
先安装demo1再安装demo2
3.4卸载
先卸载demo2,再卸载demo1
字符设备驱动
1.字符设备驱动的定义
字符设备是以字节流的形式进行顺序访问的设备针对字节设备设计的驱动框架叫做字符设备驱动。当前市面上绝大多数的设备都属于字符设备比如键盘、鼠标、摄像头...
2.字符设备驱动的框架 1.当在内核中注册一个字符设备驱动后会得到驱动对应的设备号设备号是设备的唯一标识。设备号分为主设备号和次设备号主设备号高12位用来标识一类设备 此设备号低20位用来标识这一类中的某一个设备。设备号主设备号20 | 次设备号 2.根据设备号可以通过某种方式在文件系统中创建设备文件相当于通过设备号将设备文件和驱动绑定 3.当设备文件被创建后应用程序中调用 open() | read() | write() | close() 4.调用以上函数时驱动中对应的操作应用方法会被回调 5.在驱动的操作方法中完成硬件的控制 3.字符设备驱动的注册和注销
3.1相关API 头文件#include linux/fs.h 注册字符设备驱动 int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops) 功能实现字符设备驱动的注册一次申请256个设备的资源0-255个此设备号 参数major主设备号 0 静态指定主设备号 0 动态申请主设备号 name注册得到的驱动名字 fops操作方法结构体指针指向操作方法结构体变量 返回值 失败返回错误码 成功若major0则返回申请得到主设备号 若major0则返回0 操作方法结构体用于保存和管理驱动的各自操作方法 struct file_operations { int (*open) (struct inode *, struct file *); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); int (*release) (struct inode *, struct file *); }; 注销字符设备驱动 void unregister_chrdev(unsigned int major, const char *name) 功能实现字符设备驱动的注销 参数major驱动对应的主设备号 name注册时填写的驱动号 返回值无 3.2字符设备驱动注册实例
1.编写代码
#include linux/init.h
#include linux/module.h
#include linux/fs.h
unsigned int major;
// 封装操作方法这些操作方法在应用层进行系统调用时被回调
int mycdev_open(struct inode *inode, struct file *file)
{printk(%s:%s:%d\n,__FILE__,__func__,__LINE__);return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{printk(%s:%s:%d\n,__FILE__,__func__,__LINE__);return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{printk(%s:%s:%d\n,__FILE__,__func__,__LINE__);return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{printk(%s:%s:%d\n,__FILE__,__func__,__LINE__);return 0;
}
// 定义操作方法结构体对象保存封装的操作方法
struct file_operations fops {.openmycdev_open,.readmycdev_read,.writemycdev_write,.releasemycdev_close,
};
//入口函数
static int __init mycdev_init(void)
{// 注册字符设备驱动major register_chrdev(0, mychrdev, fops);if (major 0){printk(字符设备驱动注册失败\n);return major;}printk(注册字符设备驱动成功major%d\n, major);return 0;
}
//出口函数
static void __exit mycdev_exit(void)
{//注销字符设备驱动unregister_chrdev(major,mychrdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE(GPL);
2.编译驱动
3.安装驱动内核模块
可以查看/proc/devices文件确定驱动是否被注册4.创建设备文件 创建设备文件的命令mknod /dev/mychrdev c 240 0 解析mknod创捷设备文件的命令码 /dev/mychrdev创建的设备文件的路径和名字 c 设备文件类型字符设备文件 d(块设备文件) 240主设备号 0次设备号 0-255都可以 5.编写应用程序代码测试是否可以关联驱动
#include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.h
int main(int argc, char const *argv[])
{char buf[128] {0};int fd open(/dev/mychrdev, O_RDWR);if (fd 0){printf(打开设备文件失败\n);return -1;}printf(打开设备文件成功\n);//调用readread(fd, buf, sizeof(buf));//调用writewrite(fd, buf, sizeof(buf));//调用closeclose(fd);return 0;
}6.现象
执行应用程序驱动中的操作方法被回调
4.用户和内核的数据传递
用户空间和内核空间之间无法直接相互访问内存二者进行数据交互需要使用数据传递函数
4.1 API 头文件 #include linux/uaccess.h 函数原型unsigned long copy_to_user(void __user *to, const void *from, unsigned long n) 功能传递内核空间的数据到用户空间 参数to用户空间保存数据的buf首地址 from内核空间保存数据的buf首地址 n传递的数据长度以字节为单位 返回值成功返回0失败返回未拷贝的字节数 函数原型unsigned log copy_from_user(void *to, const void __user *from, unsigned long n) 功能传递用户空间的数据到内核空间 参数to内核空间保存数据的buf首地址 from用户空间保存数据的buf首地址 n传递的数据长度以字节为单位 返回值成功返回0失败返回未拷贝的字节数 4.2用户和内核数据传递实例
应用程序代码
#include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.h
#includestring.h
int main(int argc, char const *argv[])
{char buf[128] {0};int fd open(/dev/mychrdev, O_RDWR);if (fd 0){printf(打开设备文件失败\n);return -1;}printf(打开设备文件成功\n);fgets(buf,sizeof(buf),stdin);//在终端读一个字符串buf[strlen(buf)-1]\0;write(fd, buf, sizeof(buf));//将数据传递给内核memset(buf,0,sizeof(buf));//清空数组read(fd, buf, sizeof(buf));//将内核空间数据传递到用户printf(buf:%s\n,buf);close(fd);return 0;
}驱动程序代码
#include linux/init.h
#include linux/module.h
#include linux/fs.h
#includelinux/uaccess.h
unsigned int major;
char kbuf[128]{};
// 封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{printk(%s:%s:%d\n,__FILE__,__func__,__LINE__);return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{printk(%s:%s:%d\n,__FILE__,__func__,__LINE__);int ret;//拷贝数据到用户空间retcopy_to_user(ubuf,kbuf,size);if(ret){printk(copy_to_user filed\n);return -EIO;}return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{printk(%s:%s:%d\n,__FILE__,__func__,__LINE__);int ret;//从用户空间拷贝数据到内核空间retcopy_from_user(kbuf,ubuf,size);if(ret){printk(copy_from_user filed\n);return -EIO;}return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{printk(%s:%s:%d\n,__FILE__,__func__,__LINE__);return 0;
}
// 定义操作方法结构体对象
struct file_operations fops {.openmycdev_open,.readmycdev_read,.writemycdev_write,.releasemycdev_close,
};
static int __init mycdev_init(void)
{// 注册字符设备驱动major register_chrdev(0, mychrdev, fops);if (major 0){printk(字符设备驱动注册失败\n);return major;}printk(注册字符设备驱动成功major%d\n, major);return 0;
}
static void __exit mycdev_exit(void)
{//注销字符设备驱动unregister_chrdev(major,mychrdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE(GPL);现象在终端输入的字符串可以正常的打印出来说明用户空间和内核空间数据传递成功
5.物理内存映射相关API
驱动控制硬件需要操作硬件的特殊功能寄存器但是特殊功能寄存器内存物属于理内存驱动加载到虚拟内存想要在驱动中操作硬件寄存器需要将硬件寄存器内存映射为虚拟内存 #include linux/io.h 函数原型void *ioremap(phys_addr_t paddr, unsigned long size) 功能映射指定大小的物理内存为虚拟内存 参数paddr要映射的物理内存首地址 size要映射的物理内存大小 返回值成功返回映射成功的虚拟内存首地址失败返回NULL 函数原型void iounmap(const void __iomem *addr) 功能取消物理内存的映射 参数addr要取消的虚拟内存首地址 返回值无 注有关字符设备驱动的示例代码目前可查看上一篇文章编写驱动代码控制LED灯亮灭http://t.csdnimg.cn/ZBsgG