初试linux内核混杂设备驱动开发

来源:互联网 发布:abb离线编程软件 编辑:程序博客网 时间:2024/05/20 14:40

说在前面:  code by code,自古没有天道不酬勤,前一阵子看内核的书和内核源码,总感觉浩如海洋,突然想试试直接上手写一些关于内核的代码,算是一个切入点,希望有同样学习方法的coder可以交流,本文代码参考某黑客作品,特此声明。

1.编写内核模块程序,首先保证不能使用基础c库,而应该全部使用linux源码中的头文件:

(1):

//向内核表明使用的许可证, linux内核坚定支持GPL,如果是private等,则可能不能使用内核的某些功能

MODULE_LICENSE( "GPL" );

//作者

MODULE_AUTHOR( "author" );

//模块介绍, 可通过modinfo命令查看介绍

MODULE_DESCRIPTION( "description" );

注:头文件<linux/module.h>

(2):

//向内核注册启动函数

static int __init reverse_init( void ){}

module_init( reverse_init );

//向内核注册注销函数

static void __exit reverse_exit( void ){}

module_exit( reverse_exit );

注:头文件<linux/init.h>

(3):

//混杂设备,即普通字符设备结构体,混杂设备的主设备号都是10, 次设备号不同;

//设备操作
static struct file_operations reverse_fops = {
        .owner = THIS_MODULE,
        .open = reverse_open,
        .read = reverse_read,
        .write = reverse_write,
        .release = reverse_close,
        .llseek = noop_llseek
};
//设备
static struct miscdevice reverse_misc_device ={
        .minor = MISC_DYNAMIC_MINOR,
        .name = "reverse",
        .fops = &reverse_fops
};

//注册混杂设备函数

misc_register( struct miscdeivce* misc_device );

//注销混杂设备函数

misc_deregister( struct miscdevice* misc_device );

注:头文件<linux/miscdevice.h> <linux/fs.h>


(4)直接贴代码了,代码做了很详尽的注释,包括驱动源码 Makefile 和测试程序


驱动:reverse.c

//包含__init和__exit宏#include <linux/init.h>//包含KERN_INFO宏#include <linux/kernel.h>//每个内核模块都需要#include <linux/module.h>//包含module_param和MODULE_PARM_DESC宏#include <linux/moduleparam.h>//混杂设备struct miscdevice#include <linux/miscdevice.h>//文件操作结构体struct file_operation#include <linux/fs.h>//等待队列#include <linux/sched.h>//kzalloc函数#include <linux/slab.h>//锁#include <linux/mutex.h>//memchr等函数#include <linux/string.h>//用户空间和内核之间内存的交互//copy_{to, from }_user()#include <linux/uaccess.h>//模块参数buffer_sizestatic unsigned long buffer_size = 8192;//类似用户空间main函数的argvmodule_param( buffer_size, ulong, ( S_IRUSR | S_IRGRP | S_IROTH ) );//参数描述,可通过modinfo查看MODULE_PARM_DESC( buffer_size, "my buffer size" );//文件缓冲区struct buffer{//等待队列,用来实现进程阻塞wait_queue_head_t read_queue;//进程互斥锁,注意在linux内核没有线程的概念,线程只是特殊的进程struct mutex lock;//内核缓冲区char* data, *end, *read_ptr;unsigned long size;};//内核缓冲区申请函数static struct buffer *buffer_alloc( unsigned long size ){struct buffer *buf = NULL;buf = kzalloc( sizeof( *buf ), GFP_KERNEL );if( unlikely( buf == NULL ) )goto out;//kzalloc表示将申请到的内存都重置0buf->data = kzalloc( size, GFP_KERNEL );if( unlikely( buf->data == NULL ) )goto out_free;init_waitqueue_head( &buf->read_queue );mutex_init( &buf->lock );buf->size = size;out:return buf;out_free:if( buf ){kfree( buf );buf = NULL;}return buf;}//内核缓冲区释放函数static void buffer_free( struct buffer *buffer ){if( buffer->data ){kfree( buffer->data );buffer->data = NULL;}if( buffer ){kfree( buffer );buffer = NULL;}}//设备操作函数//__user表示用户空间地址,需检查;loff_t long long//off 为偏移,代表读的位置相对文件开头的偏移static ssize_t reverse_read( struct file *file, char __user *out, size_t size, loff_t *off ){struct buffer *buf = file->private_data;ssize_t ret;//尝试获取进程锁,如果获取则返回成功,否则进程进入可打断睡眠状态if( mutex_lock_interruptible( &buf->lock ) != 0 ){ret = -ERESTARTSYS;goto out;}while( buf->read_ptr == buf->end ){//内核缓冲区没有数据,解锁互斥锁mutex_unlock( &buf->lock );if( file->f_flags & O_NONBLOCK ){ret = -EAGAIN;goto out;}//等待内核缓冲区数据到来if( wait_event_interruptible( buf->read_queue, buf->read_ptr != buf->end ) != 0 ){ret = -ERESTARTSYS;goto out;}//加锁互斥锁,进行读操作if( mutex_lock_interruptible( &buf->lock ) != 0 ){ret = -ERESTARTSYS;goto out;}}size = min( size, ( size_t )( buf->end - buf->read_ptr ) );//将内核缓冲区数据copy到用户空间if( copy_to_user( out, buf->read_ptr, size ) != 0 ){ret = -EFAULT;goto out_unlock;}buf->read_ptr += size;ret = size;out_unlock://读完毕,解锁互斥锁mutex_unlock( &buf->lock );out:return ret;}//反转字符static inline char *reverse_word( char *start, char *end ){char *orig_start = start, tmp;for( ; start < end; start ++, end -- ){tmp = *start;*start = *end;*end = tmp;}return orig_start;}//反转字符串函数static char *reverse_phrase( char *start, char *end ){char *word_start = start, *word_end = NULL;while( ( word_end = memchr( word_start, ' ', end - word_start ) ) != NULL ){reverse_word( word_start, word_end - 1 );word_start = word_end + 1;}reverse_word( word_start, end );return reverse_word( start, end );}//off为当前的偏移,通常用来判断写文件是否越界static ssize_t reverse_write( struct file* file, const char __user *in, size_t size, loff_t *off ){struct buffer *buf = file->private_data;ssize_t ret;if( size > buffer_size ){ret = -EFBIG;goto out;}//获取进程互斥锁if( mutex_lock_interruptible( &buf->lock ) != 0 ){ret = -ERESTARTSYS;goto out;}//将用户空间数据copy到内核缓冲区if( copy_from_user( buf->data, in, size ) != 0 ){ret = -EFAULT;goto out_unlock;}buf->end = buf->data + size;buf->read_ptr = buf->data;if( buf->end > buf->data )reverse_phrase( buf->data, buf->end - 1 );//唤醒等待队列中的进程wake_up_interruptible( &buf->read_queue );ret = size;out_unlock://解锁互斥锁mutex_unlock( &buf->lock );out:return ret;}//inode为文件节点,节点只有一个static int reverse_open( struct inode *inode, struct file *file ){int ret = 0;struct buffer *buf = NULL;buf = buffer_alloc( buffer_size );if( unlikely( buf == NULL ) ){ret = -ENOMEM;goto out;}file->private_data = buf;out:return ret;}static int reverse_close( struct inode *inode, struct file *file ){struct buffer *buf = file->private_data;if( buf ){buffer_free( buf );buf = NULL;file->private_data = NULL;}return 0;}//设备操作static struct file_operations reverse_fops = {.owner = THIS_MODULE,.open = reverse_open,.read = reverse_read,.write = reverse_write,.release = reverse_close,.llseek = noop_llseek};//设备static struct miscdevice reverse_misc_device ={//表示将动态获取次设备号.minor = MISC_DYNAMIC_MINOR,///dev目录下的设备名.name = "reverse",//文件操作集合.fops = &reverse_fops};static int __init reverse_init( void ){if( buffer_size == 0 ){//KERN_INFO是log等级,会将信息打印到dmesg中,最新消息可通过dmesg | tail -1查看printk( KERN_INFO"reverse device: param error." );return -1;}//注册混杂设备if( misc_register( &reverse_misc_device ) != 0 ){printk( KERN_INFO"reverse device: registe failed" );return -1;}printk( KERN_INFO"reverse device has been registered, buffer_size=%lu\n", buffer_size );return 0;}static void __exit reverse_exit( void ){//注销混杂设备if( misc_deregister( &reverse_misc_device ) != 0 ){printk( KERN_INFO"reverse device: deregiste failed, but still exit." );return;}printk( KERN_INFO"reverse device has been unregistered\n" );}module_init( reverse_init );module_exit( reverse_exit );//许可证MODULE_LICENSE( "GPL" );//作者MODULE_AUTHOR( "sunny" );//模块注释MODULE_DESCRIPTION( "This is one test kernel module." );

Makefile:

#obj-m 表示以内核模块的形式编译,reverse为模块名obj-m += reverse.o#uname -r内核版本,这是内核源码位置KERNEL_PATH := /lib/modules/$(shell uname -r)/build#当前目录CODE_PATH := $(shell pwd)  all:make -C $(KERNEL_PATH) M=$(CODE_PATH) modulesclean:make -C $(KERNEL_PATH) M=$(CODE_PATH) clean

test.c:

#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <string.h>int main( int argc, char** argv ){int fd = 0;fd = open( "/dev/reverse", O_RDWR );if( fd < 0 ){printf( "error\n" );return -1;}printf( "write:%s\n", argv[1] );write( fd, argv[1], strlen( argv[1] ) );read( fd, argv[1], strlen( argv[1] ) );printf( "Read:%s\n", argv[1] );}

大家有问题可以互相交流。




原创粉丝点击