Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1945485
  • 博文数量: 383
  • 博客积分: 10011
  • 博客等级: 上将
  • 技术积分: 4061
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-24 18:53
文章分类

全部博文(383)

文章存档

2011年(1)

2010年(9)

2009年(276)

2008年(97)

我的朋友

分类: LINUX

2009-03-21 15:44:41

/*******************************************************************************
 该模块在内核空间定义一段空间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);

阅读(975) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2009-06-02 20:53:25

读写的时候;wait_queue_head_t r_wait; //读等待队列头 wait_queue_head_t w_wait; //写等待队列头 没有使用上? songBaoHua的书上 read,write 都是 remove(w_wait), 很是不明白!!!