Chinaunix首页 | 论坛 | 博客
  • 博客访问: 80541
  • 博文数量: 19
  • 博客积分: 575
  • 博客等级: 中士
  • 技术积分: 203
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-03 20:06
个人简介

好好学习,天天向上

文章分类

全部博文(19)

分类: LINUX

2011-04-08 12:12:11

    内核将加锁的情况分为两种,一种是可以睡眠的情况,一种是不能睡眠的情况。对于可以睡眠的情况,内核提供了旗标(semaphore,又称互斥锁),而对于另一种情况,内核提供了自旋锁。

asm/semaphore

1.静态初始化

DECLARE_MUTEX(name); //定义并初始化为1

DECLARE_MUTEX_LOCKED(name);//定义并初始化为0

2.运行时初始化:

void init_MUTEX(struct semaphore *sem);

void init_MUTEX_LOCKED(struct semaphore *sem);

3.直接定义一个semaphore结构体,然后初始化:

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

 

void down(struct semaphore *sem);//不可中断,不推荐

int down_interruptible(struct semaphore *sem);//如果它返回非零, 操作被打断了. 在这个情况下通常要做的是返回 -ERESTARTSYS.

int down_trylock(struct semaphore *sem);//立即返回

 

void up(struct semaphore *sem); //解锁

 

记得读者和写者的经典问题吗,为了提高并发度,区分读者和写者,内核也提供了读者写者semaphore

linux/rwsem.h

初始化方式只有一种:

void init_rwsem(struct rw_semaphore *sem);

 

void down_read(struct rw_semaphore *sem);

int down_read_trylock(struct rw_semaphore *sem);//注意,成功返回非零

void up_read(struct rw_semaphore *sem);

 

void down_write(struct rw_semaphore *sem);

int down_write_trylock(struct rw_semaphore *sem);

void up_write(struct rw_semaphore *sem);

void downgrade_write(struct rw_semaphore *sem);

写者有优先权, 当一个写者试图进入临界区, 就不会允许读者进入直到所有的写者完成了它们的工作. 这个实现可能导致读者饥饿 -- 读者被长时间拒绝存取 -- 如果你有大量的写者来竞争旗标. 由于这个原因, rwsem 最好用在很少请求写的时候, 并且写者只占用短时间。

 

等待semaphore是幸福的,可以睡觉,而等待spinlock--自旋锁无疑是痛苦的,因为不能睡觉还要一直自旋。在中断上下文这种不能睡觉的地方,只能使用spinlock进行互斥访问临界资源。显然只有在多核系统上,中断才需要加锁访问资源,而对于一个单核的系统,自旋锁是杯具的,永远的自旋,因此, 自旋锁在没有打开抢占的单处理器系统上的操作被优化为什么不作。

使用自旋锁的原则:

1.持有锁的时候不能睡眠,如果睡眠,别的线程可能要等很长时间,也可能导致系统死锁

2.持有锁的时候,要禁止本地CPU中断,试想不禁止中断的情况,线程刚拿到锁,就被中断了,而中断服务程序中也请求这个锁,死锁就这样发生了。

3.持有锁的时候,请尽可能短,不要让别人等很久。

linux/spinlock.h

静态初始化:

spinlock_t my_lock = SPIN_LOCK_UNLOCKED;

运行时初始化:

void spin_lock_init(spinlock_t *lock);

 

void spin_lock(spinlock_t *lock);

void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);

void spin_lock_irq(spinlock_t *lock); //禁止中断(只在本地处理器)在获得自旋锁之前,之前的中断状态保存在 flags 里。

void spin_lock_bh(spinlock_t *lock);//只禁止软中断,开放硬中断

 

void spin_unlock(spinlock_t *lock);

void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags);

void spin_unlock_irq(spinlock_t *lock);

void spin_unlock_bh(spinlock_t *lock);

 

int spin_trylock(spinlock_t *lock);

int spin_trylock_bh(spinlock_t *lock);

这些函数成功时返回非零

 

同样也有读者写者自旋锁:

rwlock_t my_rwlock = RW_LOCK_UNLOCKED; /* Static way */

rwlock_t my_rwlock;

rwlock_init(&my_rwlock); /* Dynamic way */

 

void read_lock(rwlock_t *lock);

void read_lock_irqsave(rwlock_t *lock, unsigned long flags);

void read_lock_irq(rwlock_t *lock);

void read_lock_bh(rwlock_t *lock);

 

void read_unlock(rwlock_t *lock);

void read_unlock_irqrestore(rwlock_t *lock, unsigned long flags);

void read_unlock_irq(rwlock_t *lock);

void read_unlock_bh(rwlock_t *lock);

 

void write_lock(rwlock_t *lock);

void write_lock_irqsave(rwlock_t *lock, unsigned long flags);

void write_lock_irq(rwlock_t *lock);

void write_lock_bh(rwlock_t *lock);

int write_trylock(rwlock_t *lock);

 

void write_unlock(rwlock_t *lock);

void write_unlock_irqrestore(rwlock_t *lock, unsigned long flags);

void write_unlock_irq(rwlock_t *lock);

void write_unlock_bh(rwlock_t *lock);

 

加锁是件比较容易出错的事情,所幸有前人的经验可以参考:

1. 一个正确的加锁机制需要清晰和明确的规则

2.不论旗标还是自旋锁都不允许一个持锁者第 2 次请求锁; 如果你试图这样做, 事情就简单地完了。

3.在假设持有锁的函数边注明,是否持有锁。

4.要请求多个锁时,确定加锁的顺序,所有请求锁的操作必须遵循此顺序。semaphore应当在spinlock前获取,如果需要的话。最重要的, 尽力避免需要多于一个锁的情况。

 

 

completions机制

这种机制出现的背景是,一个进程等待它的一个线程完成一个任务,然后继续执行,进程并不知道线程什么时候可以完成,内核提供了一个机制,这种机制允许线程完成任务后通知进程。

linux/completion.h

静态初始化:

DECLARE_COMPLETION(my_completion);

运行时初始化:

struct completion my_completion;

init_completion(&my_completion);

 

void wait_for_completion(struct completion *c); //等待complete,如果没有线程的complete,它将永远等待下去。

void complete(struct completion *c);//通知线程任务完成了,释放complete

void complete_all(struct completion *c);//通知所有等待的线程任务完成了

 

一个 completion 正常地是一个单发设备; 使用一次就放弃,在使用了complete_all的情况下,如果要继续使用它,必须重新初始化:

INIT_COMPLETION(struct completion c);

当然如果你使用的是complete通知函数,就不必初始化了。

 

一个经典的使用方法是,模块退出时,通知工作线程可以退出了,然后调用wait_for_completion等待工作线程的退出消息,工作线程收到退出通知后调用void complete_and_exit(struct completion *c, long retval);退出线程并通知模块退出函数,模块退出函数收到后开始结束。

 

实践:

为内存设备加上semaphore:

struct dev_info_struct

{

  char* data;

  int size;

  struct semaphore sem;

  struct cdev cdev;

}dev_info;

 

ssize_t base_char_read(struct file *fp, char __user *buf, size_t size, loff_t *ff)

{

  struct dev_info_struct *dev_info_ptr;

  if(size<0)

    return -1;

  if(down_interruptible(&dev_info.sem)!=0)

    return -ERESTARTSYS;

  dev_info_ptr=(struct dev_info_struct*)fp->private_data;

  if(size>dev_info_ptr->size)

    size=dev_info_ptr->size;

  if(copy_to_user(buf,dev_info_ptr->data,size)!=0)

    size=-1;

  printk("base char read data,size %d!\n",size);

  up(&dev_info.sem);

  return size;

}

base_char_init中初始化:

sema_init(&dev_info.sem,1);

base_char_writebase_char_read差不多,不再贴出。

 

 

有时候只需要对一个整数进行原子操作,使用加锁就有些大材小用了,内核为此提供了整数的原子变量。

asm/atomic.h

atomic_t v = ATOMIC_INIT(value);

void atomic_set(atomic_t *v, int i);

 

int atomic_read(atomic_t *v);

void atomic_add(int i, atomic_t *v);

void atomic_sub(int i, atomic_t *v);

void atomic_inc(atomic_t *v);

void atomic_dec(atomic_t *v);

 

//操作后如果为0,返回真

int atomic_inc_and_test(atomic_t *v);

int atomic_dec_and_test(atomic_t *v);

int atomic_sub_and_test(int i, atomic_t *v);

 

//操作后如果为负,返回真

int atomic_add_negative(int i, atomic_t *v);

int atomic_add_return(int i, atomic_t *v);

int atomic_sub_return(int i, atomic_t *v);

int atomic_inc_return(atomic_t *v);

int atomic_dec_return(atomic_t *v);

 

注意:两个原子变量函数之间是非原子的,如:

atomic_set(&v,1);

atomic_read(&v);

它们之间可能v已经被改变了。

 

位操作:

asm/bitops.h

void set_bit(nr, void *addr); //设置第 nr 位在 addr 指向的数据项中.

void clear_bit(nr, void *addr); //清除指定位在 addr 处的无符号长型数据. 它的语义与 set_bit 的相反.

void change_bit(nr, void *addr); //翻转这个位.

test_bit(nr, void *addr); //这个函数是唯一一个不需要是原子的位操作; 它简单地返回这个位的当前值.

int test_and_set_bit(nr, void *addr);

int test_and_clear_bit(nr, void *addr);

int test_and_change_bit(nr, void *addr);

阅读(1677) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~