这是一个考虑了并发与竞态的字符驱动,同样也是利用内存作为字符设备,来编写一个字符驱动,该驱动没有涉及任何具体的硬件,废话少说,先上代码。
运行环境:TQ2440开发板,内核版本2.6.30.4
开发环境:Window下的Source Insight以及PC机上的红帽企版5虚拟机
实验环境前提条件:拥有一个制作好的NFS文件系统
/************************************memdev_compet.c**********************************/
#include<linux/module.h>
#include<linux/init.h>
#include <linux/moduleparam.h>
#include<linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include<linux/types.h> /* size_t */
#include<linux/fs.h> /* everything... */
#include<linux/errno.h> /* error codes */
#include<linux/mm.h>
#include<linux/sched.h>
#include<linux/cdev.h>
#include<asm/io.h>
#include<asm/system.h>
#include<asm/uaccess.h> /* copy_*_user */
#define MEMDEV_SIZE 0x1000 /* size=4KB */
#define MEM_CLEAR 0x1 /* a command of clear memory for ioctl() */
#define MEMDEV_MAJOR 0
int memdev_major = MEMDEV_MAJOR;
module_param(memdev_major, int, S_IRUGO);
struct memdev_dev{
struct cdev cdev;/*cdev 结构体*/
unsigned char mem[MEMDEV_SIZE]; /*内存的大小*/
struct semaphore sem; /*定义并发控制用的信号量*/
};
struct memdev_dev *memdev_devp; /*设备结构体指针*/
/*文件打开函数,系统调用open()最终落实到这个memdev_open()*/
static int memdev_open(struct inode *inodep,struct file *filp)
{
/*将设备结构体指针赋值给文件私有数据指针*/
filp->private_data = memdev_devp;
return 0;
}
/*文件释放函数,系统调用close()最终落实到这个memdev_release()*/
static int memdev_release(struct inode *inodep,struct file *filp)
{
return 0;
}
/*增加并发控制后的文件读函数,系统调用read()最终落实到这个memdev_read()*/
static ssize_t memdev_read(struct file *filp,char __user *buf,size_t size,loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
/*获得设备结构体指针*/
struct memdev_dev *dev = filp->private_data;
/*分析和获取有效的读长度*/
if(p >=MEMDEV_SIZE)
return 0;
if(count > MEMDEV_SIZE - p)
count = MEMDEV_SIZE - p;
/*获取信号量,若返回 0 表示成功,非0 将进程置于可中断的睡眠队列中*/
if(down_interruptible(&dev->sem))
return -ERESTARTSYS; /*不成功立刻返回-ERESTARTSYS*/
if(copy_to_user(buf,(void*)(dev->mem + p),count))
ret = -EFAULT;
else{
*ppos +=count;
ret = count;
printk(KERN_INFO "read %u bytes from %lu\n",count,p);
}
/*释放信号量*/
up(&dev->sem);
return ret;
}
/*增加并发控制后的文件写函数,系统调用write()最终落实到这个memdev_write()*/
static ssize_t memdev_write(struct file *filp,const char __user *buf,size_t size,loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
/*获得设备结构体指针*/
struct memdev_dev *dev = filp->private_data;
/*分析和获取有效的读长度*/
if(p >=MEMDEV_SIZE)
return 0;
if(count > MEMDEV_SIZE - p)
count = MEMDEV_SIZE - p;
/*获取信号量,若返回 0 表示成功,非0 将进程置于可中断的睡眠队列中*/
if(down_interruptible(&dev->sem))
return -ERESTARTSYS; /*不成功立刻返回-ERESTARTSYS*/
if(copy_from_user(dev->mem + p,buf,count))
ret = -EFAULT;
else{
*ppos +=count;
ret = count;
printk(KERN_INFO "read %u bytes from %lu\n",count,p);
}
/*释放信号量*/
up(&dev->sem);
return ret;
}
/*增加并发控制后的设备控制函数,系统调用ioctl()最终落实到这个memdev_ioctl()*/
static int memdev_ioctl(struct inode *inodep,struct file *filp,unsigned int cmd,unsigned long arg)
{
struct memdev_dev *dev = filp->private_data;
switch(cmd){
case MEM_CLEAR:
/*获得信号量*/
if(down_interruptible(&dev->sem))
return -ERESTARTSYS;
memset(dev->mem,0,MEMDEV_SIZE); /*清零*/
/*释放信号量*/
up(&dev->sem);
printk(KERN_WARNING "memdev has set to zero.\n");
break;
default:
return -EINVAL; /*暂不支持其他命令*/
}
return 0;
}
/*文件定位函数,系统调用seek()最终落实到这个memdev_ioctl()*/
static loff_t memdev_llseek(struct file *filp,loff_t offset,int whence)
{
loff_t ret = 0;
switch(whence){
case 0: /*相对文件开始位置偏移*/
if(offset < 0){
ret = -EINVAL;
break;
}
if((unsigned int )offset > MEMDEV_SIZE){
ret = -EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
case 1: /*相对文件当前位置偏移*/
if((filp->f_pos + offset) < 0){
ret = -EINVAL;
break;
}
if((filp->f_pos + offset) > MEMDEV_SIZE){
ret = -EINVAL;
break;
}
filp->f_pos += (unsigned int)offset;
ret = filp->f_pos;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
/*文件操作结构体--file_operations */
static const struct file_operations memdev_fops = {
.owner = THIS_MODULE,
.open = memdev_open,
.release = memdev_release,
.read = memdev_read,
.write = memdev_write,
.ioctl = memdev_ioctl,
.llseek = memdev_llseek,
};
/*初始化cdev,添加注册cdev*/
static void memdev_setup_cdev(struct memdev_dev *dev,int index)
{
int err,devno = MKDEV(memdev_major,index);
cdev_init(&dev->cdev,&memdev_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &memdev_fops;
err = cdev_add(&dev->cdev,devno,1);
if(err)
printk(KERN_WARNING "Error %d adding memdev_compet %d",err,index);
}
/*设备驱动模块加载函数*/
static int __init memdev_init(void)
{
int result;
dev_t devno = MKDEV(memdev_major,0);
/*申请设备号*/
if(memdev_major) /*将设备节点名改为了memdev_compet*/
result = register_chrdev_region(devno, 1, "memdev_compet");
else{
result = alloc_chrdev_region(&devno, 0, 1, "memdev_compet");
memdev_major = MAJOR(devno);
}
if(result < 0)
return result;
/*动态申请设备结构体的内存*/
memdev_devp = kmalloc(sizeof(struct memdev_dev),GFP_KERNEL);
if(!memdev_devp){ /*申请失败*/
result = -ENOMEM;
goto fail_malloc;
}
/*申请成功后将申请的内存区全部清零*/
memset(memdev_devp,0,sizeof(struct memdev_dev));
memdev_setup_cdev(memdev_devp, 0);
/*初始化信号量为 1 */
init_MUTEX(&memdev_devp->sem);
return 0;
fail_malloc:
unregister_chrdev_region(devno, 1);
return result;
}
/*驱动模块卸载函数*/
static void __exit memdev_exit(void)
{
cdev_del(&memdev_devp->cdev); //注销cdev结构
kfree(memdev_devp); //释放设备结构体内存
unregister_chrdev_region(MKDEV(memdev_major,0), 1); //释放设备号
}
module_init(memdev_init);
module_exit(memdev_exit);
MODULE_AUTHOR("lwj<http://blog.csdn.net/lwj103862095>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("a simple char driver in memory");
Makefile与之前的基本一模一样,只需要将obj-m :=xxx.ko改其中的xxx,而xxx代表的是你驱动文件名。
实验操作步骤:
一、虚拟机上的操作
1、建立一个单独的文件夹用于存放memdev_compet模块以及Makefile(方便开发)
2、编译模块,拷贝模块到制作好的NFS文件系统。
二、开发板上的操作(SecureCRT终端)
[\u@\h \W]# insmod memdev_compet.ko
[\u@\h \W]# lsmod
memdev_compet 3464 0 - Live 0xbf00c000
[\u@\h \W]# cat /proc/devices
Character devices:
1 mem
2 pty
3 ttyp
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
6 lp
7 vcs
10 misc
13 input
14 sound
21 sg
29 fb
90 mtd
99 ppdev
116 alsa
128 ptm
136 pts
180 usb
188 ttyUSB
189 usb_device
204 tq2440_serial
252 memdev_compet
253 usb_endpoint
254 rtc
Block devices:
1 ramdisk
259 blkext
7 loop
8 sd
31 mtdblock
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
[\u@\h \W]# mknod /dev/memdev_compet c 252 0
[\u@\h \W]# ls -l /dev/memdev_compet
crw-r--r-- 1 root root 252, 0 Jan 1 00:22 /dev/memdev_compet
[\u@\h \W]# echo "This is a char driver with competition" > /dev/memdev_compet
read 39 bytes from 0
[\u@\h \W]# cat /dev/memdev_compet
read 4096 bytes from 0
This is a char driver with competition
下面是我SecureCRT终端(即TQ2440开发板上)的运行情况:
结束语:
并发与竞态最常用的锁就是信号量了,掌握了信号量的用法,这个问题就游刃而解。下一篇准备进入阻塞与非阻塞I/O,尽请关注,哈哈,最后,祝大家学习愉快。