`
dawuafang
  • 浏览: 1099810 次
文章分类
社区版块
存档分类
最新评论

Linux驱动学习5(详细分析字符设备驱动信号量实现互斥)

 
阅读更多
/*======================================================================
    A mem_pool driver as an example of char device drivers  

======================================================================*/
#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 <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include "mem_pool.h"

static mem_pool_major = mem_pool_MAJOR;
/*设备结构体指针*/
struct mem_pool_dev *mem_pool_devp; 
/*文件打开函数*/
int mem_pool_open(struct inode *inode, struct file *filp)
{
	struct mem_pool_dev *dev;
	dev = container_of(inode->i_cdev,struct mem_pool_dev,cdev);
  /*将设备结构体指针赋值给文件私有数据指针*/
  filp->private_data = dev;
  
	printk("mem_pool_open func \n");
	printk("dev->sem.count = %d \n",dev->sem.count);
  return 0;
}
/*文件释放函数*/
int mem_pool_release(struct inode *inode, struct file *filp)
{
  return 0;
}

/* ioctl设备控制函数 */
static int mem_pool_ioctl(struct inode *inodep, struct file *filp, unsigned
  int cmd, unsigned long arg)
{
	/*获得设备结构体指针*/
  struct mem_pool_dev *dev = filp->private_data;

  switch (cmd)
  {
    case MEM_CLEAR:
   		/* 增加并发机制 由信号量控制*/
			if(down_interruptible(&dev->sem))
			{	
			return -ERESTARTSYS;	
			}
      memset(dev->mem, 0, mem_pool_SIZE);      
      printk( "mem_pool is set to zero\n");
     	/* 释放信号量 */
  		up(&dev->sem);   
      break;

    default:
      return  - EINVAL;
  }
  return 0;
}

/*读函数*/
static ssize_t mem_pool_read(struct file *filp, char __user *buf, size_t size,
  loff_t *ppos)
{
  unsigned long p =  *ppos;
  unsigned int count = size;
  int ret = 0;
  /*1. 获得设备结构体指针*/
  struct mem_pool_dev *dev = filp->private_data; 

  /*2. 分析和获取有效的写长度*/
  if (p >= mem_pool_SIZE)
    return count ?  - ENXIO: 0;
  if (count > mem_pool_SIZE - p)
    count = mem_pool_SIZE - p;
	/* 增加并发机制 由信号量控制*/
	printk("read dev->sem = %d \n",dev->sem.count);
	if(down_interruptible(&dev->sem))
	{
		return -ERESTARTSYS;	
	}
  /*3. 内核空间->用户空间 */
  if (copy_to_user(buf, (void*)(dev->mem + p), count))
  {
    ret =  - EFAULT;
  }
  else
  {
    *ppos += count;
    ret = count;
    
    printk( "read %d bytes(s) from %d\n", count, p);
  }
  /* 释放信号量 */
  up(&dev->sem);
  
  return ret;
}

/*写函数*/
static ssize_t mem_pool_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;
  /*1. 获得设备结构体指针*/
  struct mem_pool_dev *dev = filp->private_data; 

  /*2. 分析和获取有效的写长度*/
  if (p >= mem_pool_SIZE)
    return count ?  - ENXIO: 0;

  if (count > mem_pool_SIZE - p)
    count = mem_pool_SIZE - p;
    
	/* 增加并发机制 由信号量控制*/
	printk("write dev->sem = %d \n",dev->sem.count);
	/* 正常情况下(获取到信号量,sem > 0),down_interruptible返回0 */
	if(down_interruptible(&dev->sem))
	{
		return -ERESTARTSYS;	
	}    
  /*3. 用户空间->内核空间*/
  printk("copy_from_user  \n");
  printk("dev->mem + p = %x  \n",dev->mem + p);
  if (copy_from_user(dev->mem + p, buf, count))
    ret =  - EFAULT;
  else
  {
    *ppos += count;
    ret = count;
    
    printk( "written %d bytes(s) from %d\n", count, p);
  }
  printk("dev->sem = %d \n",dev->sem.count);
  /* 释放信号量 */
  up(&dev->sem);
  printk("  up(&dev->sem);  \n");
  printk("dev->sem = %d \n",dev->sem.count);
  return ret;
}

/* seek文件定位函数 */
static loff_t mem_pool_llseek(struct file *filp, loff_t offset, int orig)
{
  loff_t ret = 0;
  switch (orig)
  {
    case 0:   /*相对文件开始位置偏移*/
      if (offset < 0)
      {
        ret =  - EINVAL;
        break;
      }
      if ((unsigned int)offset > mem_pool_SIZE)
      {
        ret =  - EINVAL;
        break;
      }
      filp->f_pos = (unsigned int)offset;
      ret = filp->f_pos;
      break;
    case 1:   /*相对文件当前位置偏移*/
      if ((filp->f_pos + offset) > mem_pool_SIZE)
      {
        ret =  - EINVAL;
        break;
      }
      if ((filp->f_pos + offset) < 0)
      {
        ret =  - EINVAL;
        break;
      }
      filp->f_pos += offset;
      ret = filp->f_pos;
      break;
    default:
      ret =  - EINVAL;
      break;
  }
  return ret;
}

/*文件操作结构体*/
static const struct file_operations mem_pool_fops =
{
  .owner = THIS_MODULE,
  .llseek = mem_pool_llseek,
  .read = mem_pool_read,
  .write = mem_pool_write,
  .ioctl = mem_pool_ioctl,
  .open = mem_pool_open,
  .release = mem_pool_release,
};

/*初始化并注册cdev*/
static void mem_pool_setup_cdev(struct mem_pool_dev *dev, int index)
{
  int err, devno = MKDEV(mem_pool_MAJOR, index);
  /*1. 初始化cdev,绑定设备和文件操作函数*/
  cdev_init(&dev->cdev, &mem_pool_fops);
  dev->cdev.owner = THIS_MODULE;
  /*2. 为文件操作提供具体实现方法*/
  dev->cdev.ops = &mem_pool_fops;
  /*3. 添加该cdev至内核*/
  err = cdev_add(&dev->cdev, devno, 1);
  if (err)
    printk( "Error %d adding LED%d", err, devno);
}

/*设备驱动模块加载函数*/
int mem_pool_init(void)
{
  int result;
  dev_t devno = MKDEV(mem_pool_major, 0);

  printk( "mem_pool_init !\n");
  /*1. 申请设备号*/
  if (mem_pool_major)
    result = register_chrdev_region(devno, 2, "mem_pool");		//申请两个设备号
  else  
  {
	/*2. 动态申请设备号 */  	
    result = alloc_chrdev_region(&devno, 0, 2, "mem_pool");		//申请两个设备号
    mem_pool_major = MAJOR(devno);
  }  
  if (result < 0)
    return result;
    
  /*3. 动态申请设备结构体的内存*/
  mem_pool_devp = kmalloc(2 * sizeof(struct mem_pool_dev), GFP_KERNEL);
  /*4. 申请失败*/
  if (!mem_pool_devp)    
  {
    result =  - ENOMEM;
    goto fail_malloc;
  }
  /*5. 内存初始化*/
  memset(mem_pool_devp, 0, 2 * sizeof(struct mem_pool_dev));
  /*6. 注册初始化设备*/
  mem_pool_setup_cdev(&mem_pool_devp[0] , 0);
  mem_pool_setup_cdev(&mem_pool_devp[1] , 1);
  /*7. 初始化信号量*/
  init_MUTEX(&mem_pool_devp[0].sem);
  /*8. 初始化信号量*/
 // init_MUTEX(&mem_pool_devp[1].sem);

  return 0;

  fail_malloc: unregister_chrdev_region(devno, 2);
  return result;
}

/*模块卸载函数*/
void mem_pool_exit(void)
{
  printk( "mem_pool_exit !\n");
	/*1. 注销cdev*/
  cdev_del(&(mem_pool_devp[0].cdev));
  cdev_del(&(mem_pool_devp[1].cdev));
	/*2. 释放设备结构体内存*/   
  kfree(mem_pool_devp);     
  /*3. 释放设备号*/
  unregister_chrdev_region(MKDEV(mem_pool_major, 0), 2); 
}

MODULE_AUTHOR("xb Deng");
MODULE_LICENSE("Dual BSD/GPL");

module_param(mem_pool_major, int, S_IRUGO);

module_init(mem_pool_init);
module_exit(mem_pool_exit);




1.inode是如何来的,这个应该和mknod这个命令有关mknod /dev/mem_pool c 250 0 这个命令生成了一个mem_pool的设备节点,

然后当我们在应用程序中打开这个设备文件的时候,系统自动赋予了inode结构体的值并且在inode结构体中,保存着一些重要的信息,

如:设备号,cdev的指针地址。为什么要创建设备节点,就是为了能够创建这些相关的信息,这里不讨论,file和inode的数据结构,

在网上随便一搜,能够搜到一大把。

2.在使用printk打印调试的时候,碰到很多愚昧的调试语句,比如

			printk("down_interruptible(&dev->sem) = %d \n",down_interruptible(&dev->sem));
试想一下。执行了这句之后不就相当于获取了一次信号量么,难怪运行程序的时候总是会停在那里。

3.在多个设备的时候,内存块也要申请多个,同时别忘记了,信号量的申请也需要多个,具体如果你不这么做,会产生什么结果,可以

自己试试

4. 创建两个设备节点就可以进行测试两个设备了

mknod /dev/mem_pool0 c 249 0

mknod /dev/mem_pool1 c 249 1

5.用户空间的测试程序如下所示:

#include <stdio.h>

int main()
{
	FILE *fp0 = NULL;
	char Buf[4096];
	
	/*初始化Buf*/
	strcpy(Buf,"mem_pool is char dev!");
	printf("BUF: %s\n",Buf);
	
	/*打开设备文件*/
	fp0 = fopen("/dev/mem_pool1","r+");
	if (fp0 == NULL)
	{
		printf("Open mem_pool Error!\n");
		return -1;
	}
	printf("Open mem_pool Success!\n");
	/*写入设备*/
	fwrite(Buf, sizeof(Buf), 1, fp0);
	printf("Write mem_pool Success!\n");	
	/*重新定位文件位置(思考没有该指令,会有何后果)*/
	fseek(fp0,0,SEEK_SET);
	printf("seek mem_pool Success!\n");	
	/*清除Buf*/
	strcpy(Buf,"Buf is NULL!");
	printf("BUF: %s\n",Buf);
	
	
	/*读出设备*/
	fread(Buf, sizeof(Buf), 1, fp0);
	printf("Read mem_pool Success!\n");	
	/*检测结果*/
	printf("BUF: %s\n",Buf);

	fclose(fp0);
	
	return 0;	

}

最后可以看看这个头文件里面的内容

mem_pool.h

#define mem_pool_SIZE	0x1000	/*全局内存最大8K字节*/
#define MEM_CLEAR 	0x1  	/*清0全局内存*/
#define mem_pool_MAJOR 	249    	/*预设的mem_pool的主设备号*/

struct mem_pool_dev                                     
{                                                        
  struct cdev cdev; 		    /*cdev结构体*/                       
  unsigned char mem[mem_pool_SIZE]; /*全局内存*/   
  struct semaphore sem;     
};/*mem_pool设备结构体*/

int mem_pool_open(struct inode *inode, struct file *filp);
int mem_pool_release(struct inode *inode, struct file *filp);
这种封装的方法还不是很好,有机会的话继续改进一下。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics