免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1170 | 回复: 0
打印 上一主题 下一主题

FIFO特性的globalmem模块驱动 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-03-21 15:44 |只看该作者 |倒序浏览


/*******************************************************************************
该模块在内核空间定义一段空间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
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP