建立linux内核机制学习的模板

1:概述

打算总结一下linux内核中的一些机制,所以建立了一个字符设备的模板,本驱动中申请一个1k的内存当作全局变量,应用程序操作的对象都是这个1k内存;

2:驱动分析

2.1:驱动的私有数据

buff指针指向1k内存的起始地址,current_len描述内存中的有效数据长度,写进程写数据,current_len增加,读进程读数据,current_len减小,当current_len为正,有数据可读,当current_len为0,内存中无数据更新,读进程返回0;

//申明驱动数据结构
struct hello_dev
{
	char *buff;                    //内存的起始地址
	int current_len;               //内存中的有效数据长度
	struct semaphore sem;          //用于防止并发的信号量
	struct cdev cdev;              //字符设备结构
};
2.2:文件操作集合

/*字符设备文件操作结构体*/
static struct file_operations hello_device_fops =
{
	.owner = THIS_MODULE,
	.llseek = hello_llseek,
	.read = hello_read,
	.write = hello_write,
	.compat_ioctl = hello_ioctl,
	.open = hello_open,
	.release = hello_release,
};
2.3:代码分享

hello.c驱动文件

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <asm/io.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/semaphore.h>
#include <linux/cdev.h>

#define HELLO_MEM_LENGTH 1024
#define CLEAR_MEM 0xff
//申明驱动数据结构
struct hello_dev
{
	char *buff;                    //内存的起始地址
	int current_len;               //内存中的有效数据长度
	struct semaphore sem;          //用于防止并发的信号量
	struct cdev cdev;              //字符设备结构
};

/*主设备号和次设备号变量*/
static int hello_major = 0;
static int hello_minor = 0;

/*定义设备私有结构体指针*/
static struct hello_dev *hello_dev = NULL;
static struct class *hello_class = NULL;


static loff_t hello_llseek (struct file *filp, loff_t offset, int whence)
{
	struct hello_dev *dev = filp->private_data;
	loff_t newpos = 0;
	
	switch(whence)
	{
		case 0:    //SEEK_SET
			newpos = offset;
			break;
		case 1:    //SEEK_CUR
			newpos = filp->f_pos + offset;
			break;
		case 2:    //SEEK_END
			newpos = HELLO_MEM_LENGTH + offset;
			break;
		default:   //can't happen
			return -EINVAL;
			break;
	}

	filp->f_pos = newpos;
	if(newpos < 0)
		return -EINVAL;
	return newpos;
}

static int hello_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	struct hello_dev *dev = filp->private_data;

	switch(cmd)
	{
		case CLEAR_MEM:
			memset(dev->buff,0,HELLO_MEM_LENGTH);
			break;
		default:
			return -EINVAL;
			break;
	}
	return 0;
}

static ssize_t hello_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
	ssize_t result = 0;

	/*取出用户自定义设备结构体*/
	struct hello_dev *dev = filp->private_data;

	/*信号量使用,用于多个写进程之间互斥以及读进程同步*/
	if(down_interruptible(&(dev->sem)))
	{
		return -ERESTARTSYS;
	}

	/*写界限控制*/
	if(*f_pos >= HELLO_MEM_LENGTH)
	{
		result = 0;
		goto out;
	}

	/*写数据字节数控制*/
	if(count > HELLO_MEM_LENGTH - *f_pos)
	{
		count = HELLO_MEM_LENGTH - *f_pos;
	}

	/*从用户空间读取count个字节的数据给val变量*/
	if(copy_from_user(dev->buff + *f_pos,buf,count))
	{
		result = -EFAULT;
		goto out;
	}

	/*每次跟新pos指针*/
	*f_pos += count;
	dev->current_len += count;
	if(dev->current_len > HELLO_MEM_LENGTH)
	{
		dev->current_len = HELLO_MEM_LENGTH;
	}
	result = count;

out:
	up(&(dev->sem));
	return result; 
}

static ssize_t hello_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos)
{
	ssize_t result = 0;
	struct hello_dev *dev = filp->private_data;

	 if(down_interruptible(&(dev->sem)))
	 {
		 return -ERESTARTSYS;
	 }
	
	 if(*f_pos > dev->current_len)
		  goto out;

	 if(count > dev->current_len - *f_pos)
	 {
		count = dev->current_len - *f_pos;
	 }

	 if(copy_to_user(buf,dev->buff + *f_pos,count))
     {
		 result = -EFAULT;
		 goto out;
	 }
	
	 *f_pos += count;
	 dev->current_len -= count;
	 result = count;
	
out:
	up(&(dev->sem));

	return result;
}

static int hello_open(struct inode *inode, struct file *filp)
{
	struct hello_dev *dev;

	/*将文件节点的私有数据指针指向用户自定义的数据结构体,方便read和write方法中使用*/
	dev = container_of(inode->i_cdev, struct hello_dev, cdev);
	filp->private_data = dev;

	if(down_interruptible(&(dev->sem)))
	{
		return -ERESTARTSYS;
	}

	//如果内存为申请,申请内存
	if(!dev->buff)
	{
		dev->buff = kmalloc(HELLO_MEM_LENGTH,GFP_KERNEL);
		if(!dev->buff)
		{
			up(&dev->sem);
			return -ENOMEM;
		}
	}

	up(&dev->sem);
	return 0;
}

/*设备文件释放时调用*/
int hello_release(struct inode *inode, struct file *filp)
{
	printk(KERN_ALERT "Release dev->buff source\r\n");
	return 0;
}

/*字符设备文件操作结构体*/
static struct file_operations hello_device_fops =
{
	.owner = THIS_MODULE,
	.llseek = hello_llseek,
	.read = hello_read,
	.write = hello_write,
	.compat_ioctl = hello_ioctl,
	.open = hello_open,
	.release = hello_release,
};

/*注册字符设备和初始化信号量*/
static int hello_setup_chardev(void)
{
	int err;
	dev_t devno = 0;
	devno = MKDEV(hello_major,hello_minor);

	memset(hello_dev,0,sizeof(struct hello_dev));

	cdev_init(&(hello_dev->cdev),&hello_device_fops);
	hello_dev->cdev.owner = THIS_MODULE;
	hello_dev->cdev.ops = &hello_device_fops;	
	err = cdev_add(&(hello_dev->cdev),devno,1); 
	if(err)
	{
		return err;
	}

	sema_init(&(hello_dev->sem),1);

	return 0;
}

static int __init hello_init(void)
{
	int result = 0;
	dev_t devno = 0;
	struct device *dev = NULL;

	/*申请字符设备号*/
	if(hello_major)
	{
		devno = MKDEV(hello_major,hello_minor);
		result = register_chrdev_region(devno,1,"hello_device");  /*hello_device是字符设备名字,在proc/devices下可见*/
	}
	else
	{
		result = alloc_chrdev_region(&devno,0,1,"hello_device"); /*动态申请一个字符设备,从设备号为0*/
	}

	if(result < 0)
	{
		printk(KERN_ALERT "Failed register char dev region\n");
		goto fail;
	}

	hello_major = MAJOR(devno);
	hello_minor = MINOR(devno);

	/*动态分配hello_dev结构体*/
	hello_dev = kmalloc(sizeof(struct hello_dev),GFP_KERNEL);
	if(hello_dev == NULL)
	{
		result = -ENOMEM;
		printk(KERN_ALERT "Failed alloc hello_dev\n");
		goto unregister;
	}

	/*注册字符设备,初始化信号量*/
	result = hello_setup_chardev();
	if(result)
	{
		printk(KERN_ALERT "Failed setup char dev\n");
		goto free;
	}

	/*在sys/class/目录下创建hello目录*/
	hello_class = class_create(THIS_MODULE,"hello");
	if(IS_ERR(hello_class))
	{
		result = PTR_ERR(hello_class);
		printk(KERN_ALERT "Failed to create hello class.\n");
		goto cdev_destroy;
	}

	/*在/dev/目录和/sys/class/hello目录下分别创建设备文件hello*/
	dev = device_create(hello_class,NULL,devno,NULL,"hello");
	if(IS_ERR(dev))
	{
		result = PTR_ERR(dev);
		printk(KERN_ALERT "Failed to create hello device.\n");
		goto class_destroy;
	}

	printk(KERN_ALERT "hello char device init ok\n");
	return 0;

class_destroy:
	class_destroy(hello_class);
	
cdev_destroy:
	cdev_del(&(hello_dev->cdev));

free:
	kfree(hello_dev);

unregister:
	unregister_chrdev_region(devno,1);

fail:
	return result;
}

static void __exit hello_exit(void)
{
	dev_t devno = 0;
	printk(KERN_ALERT "hello char device exit\n");
	devno = MKDEV(hello_major,hello_minor);

	device_destroy(hello_class,devno);
	class_destroy(hello_class);
	cdev_del(&(hello_dev->cdev));
	kfree(hello_dev->buff);
	kfree(hello_dev);
	unregister_chrdev_region(devno,1);
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("GOLF/FXB,<1029930509@qq.com>");
MODULE_DESCRIPTION("A SIMPLE CHAR DEVICE DRIVER");
Makefile

#Kbuild Makefile

#Kbuild的生成规则,编译对象chardev依赖文件char_device.o文件
#将编译对象hello_word编译成模块
obj-m := hello.o
#hello-objs := hello.o   

#ubuntu使用的内核源代码,build是链接文件
KERNEL_DIR := /lib/modules/$(shell uname -r)/build   


#当前目录
PWD := $(shell pwd)                               

#首先执行内核源代码中的TOP Makefile文件,设置内核编译环境,再执行用户定义的Kbuile Makefile文件
all:
	make -C $(KERNEL_DIR) M=$(PWD) modules   

#make clean命令
clean:                                                 
	rm *.o *.ko *.mod.c *.order *.symvers 

#执行"make clean"会无视"clean"文件存在与否。
.PHONY :clean                                        


3:编写测试程序

3.1:加载insmod  hello.ko模块,在/dev目录 出现hello节点

3.2:测试程序代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define LENGTH 1024
char buff[LENGTH+1];

int main(int  argc,char **argv)
{
	int cnt = 0;
	int fd = 0;
	fd = open("/dev/hello",O_RDWR);
	if(fd < 0)
		perror("file is not found\r\n");

	while(1)
	{
		cnt = read(fd,buff,LENGTH);
		if(cnt > 0)
		{
			buff[cnt] = '\0';
			printf("%s",buff);
			lseek(fd,0,SEEK_SET);
		}
	}
}
此应用程序一直去读节点,程序中判断是否有数据读出,如果有数据更新,打印读到的数据;

3.3:编译上述代码,然后运行,此时应用程序相当于一个读进程,然后开启一个写进程,比如:cat demo.c > /dev/hello,此时,观察读进程,窗口会打印出demo.c文件;

4:总结

驱动代码中没有实现阻塞机制,所以应用程序的执行很浪费cpu资源,因为进程一直在while循环去读节点,下一篇博文,将在此驱动模板的基础上,增加read和write的阻塞机制;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值