自旋锁和信号量的使用要点
(1)自旋锁不能递归
(2)自旋锁可以用在中断上下文(信号量不可以,因为可能睡眠),但是在中断上下文中获取自旋锁之前要先禁用本地中断
(3)自旋锁的核心要求是:拥有自旋锁的代码必须不能睡眠,要一直持有CPU直到释放自旋锁
(4)信号量和读写信号量适合于保持时间较长的情况,它们会导致调用者睡眠,因此只能在进程上下文使用,而自旋锁适合于保持时间非常短的情况,它可以在任何上下文使用。如果被保护的共享资源只在进程上下文访问,使用信号量保护该共享资源非常合适,如果对共享资源的访问时间非常短,自旋锁也可以。但是如果被保护的共享资源需要在中断上下文访问(包括底半部即中断处理句柄和顶半部即软中断),就必须使用自旋锁。自旋锁保持期间是抢占失效的,而信号量和读写信号量保持期间是可以被抢占的。自旋锁只有在内核可抢占或SMP(多处理器)的情况下才真正需要,在单CPU且不可抢占的内核下,自旋锁的所有操作都是空操作。
自旋锁使用函数:
spinlock_t *lock
spin_lock_init();//初始化
spin_lock() ; //上锁
spin_unlock() ;//解锁
一个信号量(semaphore: 旗语,信号灯)本质上是一个整数值,它和一对函数联合使用,这一对函数通常称为P和V。
希望进入临届区的进程将在相关信号量上调用P;如果信号量的值大于零,则该值会减小一,而进程可以继续。相反,如果信号量的值为零(或更小),进程必须等待知道其他人释放该信号。
对信号量的解锁通过调用V完成;该函数增加信号量的值,并在必要时唤醒等待的进程。
当信号量用于互斥时(即避免多个进程同是在一个临界区运行),信号量的值应初始化为1。这种信号量在任何给定时刻只能由单个进程或线程拥有。在这种使用模式下,一个信号量有事也称为一个“互斥体(mutex)”,它是互斥(mutual exclusion)的简称。Linux内核中几乎所有的信号量均用于互斥。
使用信号量,内核代码必须包含<asm/semaphore.h> 。
信号量的创建与初始化
1.void sema_init(struct semaphore *sem,int val);
其中val是赋予一个信号量的初始值
P函数为:
void down(struct semaphore*sem);/*不推荐使用,会建立不可杀进程*/
int down_interruptible(struct semaphore*sem);/*推荐使用,使用down_interruptible需要格外小心,若操作被中断,该函数会返回非零值,而调用这不会拥有该信号量。对down_interruptible的正确使用需要始终检查返回值,并做出相应的响应。*/
int down_trylock(struct semaphore*sem);/*带有“_trylock”的永不休眠,若信号量在调用是不可获得,会返回非零值。*/
V函数为:
void up(struct semaphore*sem);/*任何拿到信号量的线程都必须通过一次(只有一次)对up的调用而释放该信号量。在出错时,要特别小心;若在拥有一个信号量时发生错误,必须在将错误状态返回前释放信号量。*/
#include<linux/module.h>
#include<linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/device.h> /** udev **/
struct cdev cdev;
dev_t devno;
static struct class* test_class; /** udev **/
static struct device* test_device; /** udev **/
struct semaphore sem; /** sem **/
int test_open(struct inode *inode, struct file *filp)
{
if( !down_trylock(&sem) )
{
printk("test_open ok \n");
return 0;
}
else
{
printk("test_open error\n");
return -1;
}
return 0;
}
int test_release(struct inode *inode, struct file *filp)
{
up(&sem);
return 0;
}
static ssize_t test_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
return 0;
}
static ssize_t test_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
return 0;
}
static const struct file_operations test_fops =
{
.read = test_read,
.write = test_write,
.open = test_open,
.release = test_release,
};
static int test_init(void)
{
cdev_init(&cdev,&test_fops);
alloc_chrdev_region(&devno, 0, 1, "test");
cdev_add(&cdev, devno, 1);
test_class = class_create(THIS_MODULE, "testdrv"); /** udev **/
if(IS_ERR(test_class)){
printk(KERN_ERR "create class error!\n");
}
test_device = device_create(test_class, NULL, devno, NULL, "test"); /** udev **/
sema_init(&sem,1);/** sem **/
printk("test_init\n");
return 0;
}
static void test_exit(void)
{
device_destroy(test_class, devno);/** udev **/
class_destroy(test_class); /** udev **/
cdev_del(&cdev);
unregister_chrdev_region(devno, 1);
printk("test_exit\n");
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");