说在前面: 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_size
static unsigned long buffer_size = 8192;
//类似用户空间main函数的argv
module_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表示将申请到的内存都重置0
buf->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) modules
clean:
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] );
}
大家有问题可以互相交流。