事先声明,本人菜鸟一枚,文中如有不正确之处,敬请大侠指正.
(本文中举例均以4.5版本的x86_64的linux内核为例)
字符驱动算是linux驱动里面比较简单的一种。说白了,就是可以对内存读哇写哇什么的。既然是对内存读写,那为什么还要驱动呢?简单的
int a;
a = 10;
不就是对内存写吗?干嘛还要搞个驱动,这么麻烦?
哈哈,利用驱动对内存的读写优势在于,可以对内核地址空间的内存进行读写。这下就不得了了,整个操作系统(也就是内核)都是在内核空间的,当然包括线程相关的一些内核数据什么的,也统统都栖身于内核空间,从而达到操作系统对数据的保护作用,一般人是碰不到这些数据的。那么当你把内存驱动加载到内核之后,会发生什么呢?哈哈,这些内核空间所保护的数据全部都暴露在你的内核驱动之下啦,因为你的字符驱动也是运行于内核空间的,说白了,你的字符驱动和内核空间的所有数据都是一家人啦,可以随意读写。这对于喜欢玩弄内核数据的人来说,有点小激动 。
闲话扯到这,上代码吧:
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/ptrace.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("GAO");
MODULE_DESCRIPTION("HELLO");
dev_t dev;
extern struct task_struct init_task;
static inline unsigned long size_inside_page(unsigned long start,
unsigned long size)
{
unsigned long sz;
sz = PAGE_SIZE - (start & (PAGE_SIZE - 1));
return min(sz, size);
}
static loff_t my_chr_dev_seek(struct file *file, loff_t offset, int orig)
{
loff_t ret;
mutex_lock(&file_inode(file)->i_mutex);
switch (orig) {
case SEEK_CUR:
offset += file->f_pos;
case SEEK_SET:
/* to avoid userland mistaking f_pos=-9 as -EBADF=-9 */
if (IS_ERR_VALUE((unsigned long long)offset)) {
ret = -EOVERFLOW;
break;
}
file->f_pos = offset;
ret = file->f_pos;
force_successful_syscall_return();
break;
default:
ret = -EINVAL;
}
mutex_unlock(&file_inode(file)->i_mutex);
return ret;
}
static ssize_t do_write_kmem(unsigned long p, const char __user *buf,
size_t count, loff_t *ppos)
{
ssize_t written, sz;
unsigned long copied;
written = 0;
while (count > 0) {
char *ptr;
sz = size_inside_page(p, count);
/*
* On ia64 if a page has been mapped somewhere as uncached, then
* it must also be accessed uncached by the kernel or data
* corruption may occur.
*/
ptr = xlate_dev_kmem_ptr((char *)p);
copied = copy_from_user(ptr, buf, sz);
if (copied) {
written += sz - copied;
if (written)
break;
return -EFAULT;
}
buf += sz;
p += sz;
count -= sz;
written += sz;
}
*ppos += written;
return written;
}
static ssize_t my_chr_dev_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long p = *ppos;
ssize_t wrote = 0;
ssize_t virtr = 0;
char *kbuf;
int err = 0;
/* if (!capable(CAP_COMPROMISE_KERNEL))
return -EPERM;
*/
if (p < (unsigned long) high_memory) {
unsigned long to_write = min_t(uns