linux设备驱动--并发与竞态之信号量

        信号量(semaphore)是用于保护临界区的一种常用手段。与自旋锁相同,只有得到信号量的进程才能执行临界区代码。但不同的是,当获取不到信号量时,进程不会“自旋”而是进入休眠等待状态。

       To use semaphores,kernel code must include <asm/semaphore.h>.

        信号量的初始化:

        直接创建信号量:

        void sema_init(struct semaphore *sem, int val);

        声明和初始化互斥体的宏:

        DECLARE_MUTEX(name);

        DECLARE_MUTEX_LOCKED(name);

        动态分配互斥体:

        void init_MUTEX(struct semaphore *sem);
        void init_MUTEX_LOCKED(struct semaphore *sem);

        获得信号量:

        void down(struct semaphore *sem);
        int down_interruptible(struct semaphore *sem);
       int down_trylock(struct semaphore *sem);

        释放信号量:

        void up(struct semaphore *sem);


        正如读写锁,信号量也有读写信号量。

        信号量对所有的调用者执行互斥。但很多任务可以划分两种不同的工作类型:一些任务只需要读取受保护的数据结构,而其他的来做写入的动作。允许多个并发的读取是可能的,这样做会大大提高性能。

       一个rwsem可允许一个写入者或无限多个读取者拥有该信号量。写入者具有更高的优先级;所以在读远远大于写的时候用rwsem会大大提高新能。在APUE中,在讲到线程同步时提到了读写锁,那里的读写锁同样是非常适合于对数据结构读的次数远大于写的情况。

      下面继续用scull的例子来看下使用信号量实现设备文件只能被一个进程打开的例子:

      代码添加如下:

static DECLARE_MUTEX(scull_lock);//define mutex

int scull_open(struct inode *inode, struct file *filp)
{
	struct scull_dev *dev; /* device information */

	dev = container_of(inode->i_cdev, struct scull_dev, cdev);
	filp->private_data = dev; /* for other methods */
	if(down_trylock(&scull_lock)){
		return -EBUSY;
	}
	return 0;          /* success */
}

int scull_release(struct inode *inode, struct file *filp)
{
	up(&scull_lock);
	return 0;
}

        用./app 测试表明该设备文件只能被一个进程打开。


       接下来继续完成增加并发控制的scull驱动。要注意的是信号量必须在scull设备对系统其他部分可用前被初始化。

       代码改动如下:

      

/*
 * Representation of scull quantum sets.
 */

struct scull_dev {
	unsigned char mem[SCULL_SIZE];
	struct cdev cdev;	  /* Char device structure		*/
	struct semaphore sem;      /*semaphore*/
};

ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
                loff_t *f_pos)
{
	unsigned long p = *f_pos;
	int ret = 0;
	struct scull_dev *dev = filp->private_data;
	if(p >= SCULL_SIZE)
		return count ? -ENXIO : 0 ;
	if(count > SCULL_SIZE - p)
		count = SCULL_SIZE - p;

	if(down_interruptible(&dev->sem))
		return -ERESTARTSYS;
        if(copy_to_user(buf, (void *)(dev->mem + p), count))
	{
		ret = - EFAULT;
	}
	else
	{
		*f_pos += count;
		ret = count;
		printk(KERN_WARNING "read %d bytes from %d\n", count, p);
	}
	printk(KERN_WARNING "ret: %d\n",ret);
	up(&dev->sem);
	return ret;
}

ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,
                loff_t *f_pos)
{
	unsigned long p = *f_pos;
	int ret = 0;
	struct scull_dev *dev = filp->private_data;
	
	if(p >= SCULL_SIZE )
		return count ? -ENXIO : 0 ;
	if(count > SCULL_SIZE - p)
		count = SCULL_SIZE - p;
	if(down_interruptible(&dev->sem))
		return -ERESTARTSYS;
	if(copy_from_user(dev->mem + p, buf, count))
	{
		ret = -EFAULT;
	}
	else
	{
		*f_pos += count;
		ret = count;
		printk(KERN_WARNING "write %d bytes to %d\n", count , p);
	}
	up(&dev->sem);
	return ret;

}

这样,用./app测试,可以多个进程同时打开访问scull设备文件,但对于文件的读写,是有互斥控制的。






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值