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");