Linux设备驱动编程之信号量用作互斥

1.信号量用来做共享资源保护

在这里插入图片描述
当获取信号量down时候会进行两步操作:
(1)s=s -1//信号量值-1
(2)if(s>=0)为真则进程继续执行,否则进入休眠阻塞。

当释放信号量up的时候会执行s=s+1,信号值+1。
那么如上图,当信号量用作互斥保护共享资源的时候,一般来说信号量初始化sem_init(&sem,1),也就是信号量初始化值为1。
进程1首先获取信号量down(&sem),这时候信号值s=s-1,s = 0,满足s>=0,则进程1可以继续执行访问,但是假如当进程1在还在访问共享资源时,也就进程1还没释放信号量up(&sem)的时候;这时候进程2想要访问共享资源,那么进程2调用down(&sem)函数,信号值会由原来的s=0的值-1后为-1,-1< 0,那么进程2会阻塞到进程1访问完共享资源并释放信号量up(&sem)成功后,这时候信号量的值为0+1=1,进程2才能访问共享资源。

代码实例:(globalmem字符设备驱动参考宋宝华老师linux设备驱动编程而写
为了观察现象,利用当读进程访问共享资源的时候需要10秒后才释放信号量。加入一开始读进程访问/dev/globalmem,然后在十秒之内接着写进程访问/dev/globalmem,那么这时候写进程会阻塞,阻塞到读进程10秒后释放信号量才能访问/dev/globalmem。

#include "globalmem.h"
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/completion.h>
#include <linux/wait.h>
#include <linux/signal.h>
#include <linux/sched/signal.h>
#include <linux/delay.h>

#define GLOBALMEM_BUFFER_SIZE 50
#define GLOBALMEM_MAJOR 255

struct globalmem_dev
{
	dev_t devno;
	struct cdev dev;
	unsigned char globalmem_buf[GLOBALMEM_BUFFER_SIZE];
	struct semaphore sem;
	//struct mutex globalmem_mutex;
};

struct globalmem_dev *globalmem_devp = NULL;

static int globalmem_open(struct inode *inode, struct file *filp)
{
	if(!globalmem_devp)
		return -ENODEV;

	filp->private_data = globalmem_devp;

	return 0;
}

static int globalmem_release(struct inode *inode, struct file *filp)
{
	if(filp->private_data)
		filp->private_data = NULL;

	if(!globalmem_devp)
		return -ENODEV;

}

static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
	int ret = 0;
	unsigned long count = size;
	struct globalmem_dev *dev = filp->private_data;
	long long p = *ppos;

	//判断opps是否大于最大值
	if(p >= GLOBALMEM_BUFFER_SIZE)
		return 0;

	if(count > GLOBALMEM_BUFFER_SIZE - p)
		count = GLOBALMEM_BUFFER_SIZE -p;

	//互斥量
	//获取信号量,信号量的值-1,当信号量值为大于或者等于0,代表信号量获取成功,并且返回。否则将会休眠阻塞等待
	down_interruptible(&dev->sem);
	//mutex_lock(&dev->globalmem_mutex);
	if(copy_to_user(buf,dev->globalmem_buf,count)){
		ret = -EFAULT;
	}else{
		*ppos = +count;
		ret = count;
	}
	msleep(10000);//等待10秒观察效果。当读进程执行的时候,写进程需要10秒后才能写进去。
	//mutex_unlock(&dev->globalmem_mutex);
	//释放信号量,让信号量的值+1
	up(&dev->sem);
	return ret;    
}

static ssize_t globalmem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
	int ret = 0;
	unsigned long count = size;

	struct globalmem_dev *dev = filp->private_data;
	long long p = *ppos;

	if(p >= GLOBALMEM_BUFFER_SIZE)
		return 0;

	if(count > GLOBALMEM_BUFFER_SIZE - p)
		count = GLOBALMEM_BUFFER_SIZE - p;

	//获取信号量,信号量的值-1,当信号量值为大于或者等于0,代表信号量获取成功,并且返回。否则将会休眠阻塞等待
	down_interruptible(&dev->sem);
	//mutex_lock(&dev->globalmem_mutex);
	if(copy_from_user(dev->globalmem_buf,buf,count)){
		ret = -EFAULT;
	}else{
		*ppos += count;
		ret = count; 
	}
	//mutex_unlock(&dev->globalmem_mutex);
	//释放信号量,让信号量的值+1
	up(&dev->sem);
	return ret;
}

struct file_operations globalmem_fops = {
	.open = globalmem_open,
	.read = globalmem_read,
	.write = globalmem_write,
	.release = globalmem_release
};



static int __init globalmem_init(void)
{
	int ret ;

	globalmem_devp = (struct globalmem_dev *)kzalloc(sizeof(struct globalmem_dev),GFP_KERNEL);
	if(!globalmem_devp)
		return -ENOMEM;

	globalmem_devp->devno = MKDEV(GLOBALMEM_MAJOR,0);

	if(GLOBALMEM_MAJOR)
		ret = register_chrdev_region(globalmem_devp->devno,1,"globalmem");
	else
		ret = alloc_chrdev_region(&globalmem_devp->devno,0,1,"globalmem");
	if(ret < 0)
		return ret;

	cdev_init(&globalmem_devp->dev,&globalmem_fops);
	globalmem_devp->dev.owner = THIS_MODULE;
	ret = cdev_add(&globalmem_devp->dev,globalmem_devp->devno,1);
	if(ret < 0 ){
		unregister_chrdev_region(globalmem_devp->devno,1);
		kfree(globalmem_devp);
		return ret;
	}

	//初始化信号量为1用来用作互斥作用,1代表可以进入临界区,0代表不可以进入
	sema_init(&globalmem_devp->sem,1);


	return 0;
}

static void __exit globalmem_exit(void)
{
	cdev_del(&globalmem_devp->dev);
	unregister_chrdev_region(globalmem_devp->devno,1);
	kfree(globalmem_devp);
}

module_init(globalmem_init);
module_exit(globalmem_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("gentle");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值