前一段时间自己也在为如何在用户空间调用自己编写的内核函数的方法而苦恼,正好今天下午小师弟也问到了相关的问题,便把自己关于这个问题的两种解决方法总结一下。
在用户态调用内核函数的最简单的方法就是系统调用,通过系统调用,用户进程进入内核态完成内核函数的相关工作。但是,当你无法使用现有的内核函数而需要自己重新设计内核函数功能的时候,这个方法就不能很好的使用了,于是便想到了通过以下两种方法来达到自己的目的:1)通过动态模块实现,2)通过虚拟设备实现。
1)通过动态模块实现的方法比较简单和直接,只是实现的道路比较曲折一点。Linux内核支持模块的动态装载,这也就方面了我们将自己设计的内核功能通过模块的方式编译进整个Linux内核中。普通的构建模块的方法暂且不说,这里主要用到的是模块中符号表的概念。
模块被载入后,就会动态连接到了内核。此时,与用户空间的动态连接库类似,只有当被显示导出后的外部函数才可以被动态库调用,也就是可以被其他的模块以至于其他的内核函数中所调用,而未导出的函数模块则无法被调用。在内核中,导出内核函数需要使用特殊的指令:EXPORT_SYMBOL()和EXPORT_SYMBOL_GPL()。这些导出的内核符号表被看作是导出的内核接口,甚至称为内核API,此时我们就能方便的在系统调用中使用这些导出的内核API函数来完成自己想要的功能了。
整个步骤应该是这样的:编写模块,完成内核函数功能,并通过EXPORT_SYMBOL()函数导出符号表-->编译内核,make-->编写新的系统调用(此时系统调用中能直接使用刚刚的函数)-->用户空间通过系统调用运行函数完成所需的功能。
2)通过虚拟设备驱动来实现,过程同样不复杂,主要通过设备驱动程序中的ioctl()函数实现。
ioctl在用户空间的函数原型为:
int ioctl(int fd,unsigned long cmd,char *argp)
驱动程序的函数原型有些不同:
int (*ioctl) (struct inode *inode,struct file *filp,unsigned long cmd,unsigned long arg)
cmd为传递的命令,arg为传递的参数,根据传递参数cmd的不同,可以完成不同的功能,并且ioctl做为设备驱动程序自然能够运行各种内核函数包括自定义内核函数。
首先创建虚拟的字符设备驱动,然后编写设备驱动程序,在驱动程序中不考虑设备的打开和读写问题,重点在于ioctl函数,在设备驱动程序的ioctl()函数中,通过特定参数cmd的传递来调用自己编写的内核函数,以完成所需功能。在驱动程序完成以后,就能在用户空间对通过ioctl函数对虚拟设备进行控制以完成所需函数功能了。
这样一来,整个步骤就表现为:构建虚拟字符设备-->完成设备驱动程序-->自定义内核函数的实现-->在ioctl函数中完成内核函数调用-->在用户空间生产设备节点-->通过函数实现打开设备文件,并通过ioctl函数向设备发送特定的cmd命令参数以触发自定义的内核函数完成所需功能。
以上两种方法经验证都能够正确的运行自定义的内核函数,虽然方法比较简陋一点,不过尚能凑合~
阅读(2921) | 评论(0) | 转发(1) |