- 论坛徽章:
- 0
|
/*******************************************************************************
该模块在内核空间定义一段空间mem,用于与用户空间进行数据交互,并具备FIFO、并发控制特性。
旨在学习信号量、等待队列以及死锁等知识点。
#insmod globalmem.ko
#mknod /dev/globalmem c 254 0
#cat /dev/globalmem &
#echo 'hello world' > /dev/globalmem
#echo 'One dream, one world' > /dev/globalmem
可用以上测试方法或其它测试程序进行测试程序本身正确性、是否存在死锁现象。
******************************************************************************/
#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 linux/cdev.h>
#include linux/poll.h>
#include asm/io.h>
#include asm/system.h>
#include asm/uaccess.h>
#define GLOBALMEM_SIZE 128
#define MEM_CLEAR 0x01
#define MEM_FILL 0x02
#define GLOBALMEM_MAJOR 254
MODULE_AUTHOR("Septem 2008.08.22 ");
MODULE_LICENSE("Dual BSD/GPL");
static globalmem_major = GLOBALMEM_MAJOR;
struct globalmem_dev
{
struct cdev cdev;
struct semaphore sem; //互斥信号量
wait_queue_head_t r_wait; //读等待队列头
wait_queue_head_t w_wait; //写等待队列头
unsigned int current_len; //当前可读数据长度
unsigned char mem[GLOBALMEM_SIZE];
};
struct globalmem_dev *globalmem_devp;
int globalmem_open(struct inode *inode, struct file *filp)
{
filp->private_data = globalmem_devp;
return 0;
}
int globalmem_release(struct inode *inode, struct file *filp)
{
return 0;
}
static unsigned int globalmem_poll(struct file *filp, poll_table *wait)
{
struct globalmem_dev *dev = filp->private_data;
unsigned int mask = 0;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
poll_wait(filp, &dev->r_wait, wait);
poll_wait(filp, &dev->w_wait, wait);
if (dev->current_len != 0)
mask |= POLLIN | POLLRDNORM;
if (dev->current_len != GLOBALMEM_SIZE)
mask |= POLLOUT | POLLWRNORM;
up(&dev->sem);
return mask;
}
static int globalmem_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
struct globalmem_dev *dev = filp->private_data;
switch (cmd)
{
case MEM_CLEAR: //将内核空间mem全填充为0
if (down_interruptible(&dev->sem))//先获得信号量,以防其他进程进入临界区代码
return -ERESTARTSYS;
memset(dev->mem, 0, GLOBALMEM_SIZE);
dev->current_len = 0;
wake_up_interruptible(&dev->w_wait);//
up(&dev->sem); //释放信号量
printk(KERN_INFO"globalmem is set to zero\n");
break;
case MEM_FILL: //将内核空间mem全填充为H
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
memset(dev->mem, 'H', GLOBALMEM_SIZE);
dev->current_len = GLOBALMEM_SIZE;
wake_up_interruptible(&dev->r_wait);
up(&dev->sem);
printk(KERN_INFO"globalmem is set to 'H'\n");
break;
default:
return -EINVAL;
}
return 0;
}
static ssize_t globalmem_read(struct file *filp, char __user *buf,
size_t size, loff_t *ppos)
{
unsigned int count = size;
int ret = 0;
struct globalmem_dev *dev = filp->private_data;
if (down_interruptible(&dev->sem)) //尝试获取信号量
return -ERESTARTSYS; //down_interruptible()返回值非0时,表明这是被某个中断信号打断
if (dev->current_len == 0) //判断可读数据长度是否为0
{
up(&dev->sem); //无可读数据,先释放信号量
if (filp->f_flags & O_NONBLOCK) //判断文件标志是否为非阻塞模式
{
ret = -EAGAIN; //为非阻塞模式,直接返回
return ret;
}
//以下为阻塞模式的处理
if (wait_event_interruptible(dev->r_wait, dev->current_len != 0))//进入休眠,直到r_wait被唤醒
return -ERESTARTSYS; //非零值表示休眠被某个中断信号打断
/*?这里我不能确定的是,在wait_event_interruptible()正确返回后,是否还要再次检查condition为真?*/
if (down_interruptible(&dev->sem)) //获取信号量
return -ERESTARTSYS;
}
if (count > dev->current_len) //比较count与可读数据长度大小
count = dev->current_len;
if (copy_to_user(buf, dev->mem, count)) //将内核空间mem的数据copy到用户空间buf
{
ret = -EFAULT; //copy_to_user()返回的是不能复制的字节数
}
else
{
memcpy(dev->mem, dev->mem + count, dev->current_len - count);//将后面的可读数据往mem头移动,实现fifo
dev->current_len -= count;
wake_up_interruptible(&dev->w_wait); //空出一定空间,唤醒等待在w_wait上的进程
ret = count;
}
up(&dev->sem); //释放信号量
return ret;
}
static ssize_t globalmem_write(struct file *filp, const char __user *buf,
size_t size, loff_t *ppos)
{
unsigned int count = size;
int ret = 0;
struct globalmem_dev *dev = filp->private_data;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
if (dev->current_len == GLOBALMEM_SIZE)
{
up(&dev->sem);
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
if (wait_event_interruptible(dev->w_wait, dev->current_len != GLOBALMEM_SIZE))
return -ERESTARTSYS;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
}
if (count > GLOBALMEM_SIZE-dev->current_len)
count = GLOBALMEM_SIZE-dev->current_len;
if (copy_from_user(dev->mem+dev->current_len, buf, count))
ret = EFAULT;
else
{
dev->current_len += count;
wake_up_interruptible(&dev->r_wait);
ret = count;
}
up(&dev->sem);
return ret;
}
static const struct file_operations globalmem_fops =
{
.owner = THIS_MODULE,
.read = globalmem_read,
.write = globalmem_write,
.ioctl = globalmem_ioctl,
.poll = globalmem_poll,
.open = globalmem_open,
.release = globalmem_release,
};
static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)
{
int err, devno = MKDEV(globalmem_major, index);
cdev_init(&dev->cdev, &globalmem_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &globalmem_fops;
err = cdev_add(&dev->cdev, devno, 1);
if (err)
printk(KERN_NOTICE"Error %d adding LED%d", err, index);
}
int globalmem_init(void)
{
int result;
dev_t devno = MKDEV(globalmem_major, 0);
if (globalmem_major)
result = register_chrdev_region(devno, 1, "globalmem");
else
{
result = alloc_chrdev_region(&devno, 0, 1, "globalmem");
globalmem_major = MAJOR(devno);
}
if (result 0)
return result;
globalmem_devp = kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL);
if (!globalmem_devp)
{
result = -ENOMEM;
goto fail_malloc;
}
memset(globalmem_devp, 0, sizeof(struct globalmem_dev));
globalmem_setup_cdev(globalmem_devp, 0);
init_MUTEX(&globalmem_devp->sem);
init_waitqueue_head(&globalmem_devp->r_wait);
init_waitqueue_head(&globalmem_devp->w_wait);
globalmem_devp->current_len = 0;
return 0;
fail_malloc:
unregister_chrdev_region(devno, 1);
return result;
}
void globalmem_exit(void)
{
cdev_del(&globalmem_devp->cdev);
kfree(globalmem_devp);
unregister_chrdev_region(MKDEV(globalmem_major, 0), 1);
}
module_param(globalmem_major, int, S_IRUGO);
module_init(globalmem_init);
module_exit(globalmem_exit);
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/67414/showart_1870826.html |
|