Chinaunix首页 | 论坛 | 博客
  • 博客访问: 852074
  • 博文数量: 321
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 936
  • 用 户 组: 普通用户
  • 注册时间: 2013-02-23 11:25
文章分类

全部博文(321)

文章存档

2017年(1)

2016年(10)

2015年(61)

2014年(187)

2013年(62)

分类: 嵌入式

2013-03-05 11:33:36

IOCTLFD,CMD,ARG);其中CMD为数字2时传不过去,这是bug

一、并发指的是多个执行单元同时被执行。即使最简单的设备驱动程序也要考虑并发与竞态并作出相应处理======>导致竞态。

二、导致并发的可能原因:

对称多处理器的多个CPU

         多个CPU可访问共同的外设和存储器

CPU内进程与抢占它的进程

         内核支持抢占调度,进程被更高优先级的进程打断。2进程都访问共同资源。

中断与进程之间

         中断处理程序访问进制正在访问的资源。

进程抢占也是通过中断来实现的。

三、解决并发:(加锁和互斥)自旋锁和信号量,中断屏蔽(因为Linux内核的进程调度等操作都是依赖中断来实现的,所以中断屏蔽也就避免了内核抢占进程之间的并发)、原子操作。

中断对于内核的运行非常重要,中断屏蔽过程中,所以中断无法得到处理,因此长时间中断是非常危险的。---附:中断屏蔽只能紧屏蔽CPU内的中断,所以无法解决SMP(对称多处理器的多个CPU)。单独使用中断屏蔽不值得推荐,适宜与自旋锁联合使用

解决竞态:保证对共享资源的互斥访问。就是指一个执行单元在访问共享资源的时候,其他执行单元被禁止访问。

四、中断屏蔽:

中断屏蔽将使中断与进程之间的并发不再发生,因为LINUX内核的进程调度等操作都是依赖于中断来实现的,因此中断屏蔽使内核抢占进程之间的并发就避免了。

缺点:1、长时间中断屏蔽可能导致系统崩溃;

     2、只能屏蔽本CPU的,对于多SMPCPU引发的竞态,无法解决。

保证正在执行的内核执行路径不被中断处理程序锁抢占,防止某些竞态条件的发生

Local_irq_disable()

Local_irq_enable()//不能保证enable的时候把之前disable的中断也给使能了

//把之前的中断状态保留。

Local_irq_save(flags) //保存了目前CPU的中断位信息

Local_irq_restore(flags)

五、原子操作:#include  P138

在执行过程中不会被中断的操作(整形原子,位原子)

atomic_t v = ATOMIC_INIT(value);//定义原子变量V的值为

Int a=0;//有可能从寄存器加载出来的时候被打断

六、自旋锁=====忙锁

1、自旋锁最多只能被一个可执行线程持有。被多个线程占有会成死锁。。

2、如果进程获得自旋锁之后阻塞,可能导致死锁,调用copy_from_user()copy_to_user()和kmalloc()都有可能一起阻塞。

信号量最多可以允许多个持有者,而自旋锁在任何时候只能允许一个持有者

  没有获得自旋锁的时候会一直占用CPU,一直守着你。效率降低。所以用在一下子就完成的工作上。

---是忙锁,当一个线程试图去获得一个自旋锁的时候,但是此时该锁已经被别人拥有,则会一直等待下去。一直守着你。所以自旋锁用在时间很短的地方,就几行代码。==如:“我当前在运行,您稍等一会儿”,“我当前不在运行,您使用”

Spinlock_t lock;

spin_lock_init(&lock) //该宏用于初始化自旋锁lock ,自旋锁在使用前必须先初始化。

spin_lock(&lock)//获取自旋锁lock,如果成功,立即获得锁,并马上返回,否则它将一 直自旋在那里,直到该自旋锁的保持者释放。

spin_trylock(&lock) //尝试获得自旋锁lock ,如果能获得就立即获得并返回真,不能获得则马上返回。

spin_unlock(&lock)

自旋锁注意:

自旋锁是忙等锁,只有在占用锁的时间极短的情况下使用才合理。

递归使用自旋锁则会死锁。就是已经拥有了锁,又想拥有。又在等待,想获得,肯定是获不到的,就一直占用CPU,死机  

能保证不被别的CPU或者本CPU的其他进程打搅,但可能受中断或者底半部的 影响

自旋锁:ssleep(),会造成死锁,因为获得锁的一方ssleep,就会让出时间片,另一个自旋锁会霸占所有的时间片,如果有第三个进程,这剩下的两个进程分掉时间片。而获得锁的一方就再也获不到时间片,以至于sleep醒不过来。造成死锁。

?????而如果read用自旋锁,write用尝试上自旋锁,都有ssleep。则如果先读在写,没问题,如果先写再读,则会造成死机。

1代码:

  spinlock_t lock;//1 并发控制用的信号量

    spin_lock_init(&(globalmem_devp->lock));//2初始化

 Write

spin_lock(&(globalmem_devp->lock));//3上锁

spin_unlock(&(globalmem_devp->lock));//4解锁

 Read

spin_lock(&(globalmem_devp->lock));//上锁

spin_unlock(&(globalmem_devp->lock));//4解锁

七、读写锁:读读共享,读写,写写互斥P142-====是自旋锁的派生,所以不要休眠。不然 也会死机

自旋锁的衍生:

读读共享,读写和写写互斥。

中断上半部分:非常紧急的事

中断下半部分:普通事件

rwlock_t lock = RW_LOCK_UNLOCKED//静态初始化

rwlock_t lock

rwlock_init(rwlock_t *lock); //动态初始化

void read_lock(rwlock_t *lock);void read_unlock(rwlock_t *lock);

void read_lock_irq(rwlock_t *lock);void read_unlock_irq(rwlock_t *lock); 

void write_lock(rwlock_t *lock);void write_unlock(rwlock_t *lock); 

void write_lock_irq(rwlock_t *lock);void write_unlock_irq(rwlock_t *lock); 

注:拥有锁的时候不要休眠。

2代码:

rwlock_t lock;

rwlock_init(&(globalmem_devp->lock));

 Write

write_lock(&(globalmem_devp->lock));

write_unlock(&(globalmem_devp->lock));

Read

read_lock(&(globalmem_devp->lock));

read_unlock(&(globalmem_devp->lock));

八、顺序锁:

是对读写锁的优化,使用顺序锁,读执行单元绝不会被写执行单元阻塞。也就是说读之下单元可以再写执行单元被对顺序锁包含的共享资源进行写操作时仍可以继续读,而不必等写执行单元完成写操作,写执行单元也不必等所有的读执行单元完成度操作才去进行写操作。

但是,写写仍是互斥的。即有些执行单元在进行写,其他写执行单元必须自旋在那边。

如果读执行单元在读操作期间,写执行单元已经发现了写操作,那么,读执行单元必须重新读取数据,以便确保得到的数据是完整的。

九、信号量(重要)======睡眠锁。

信号量最多可以允许多个持有者,而自旋锁在任何时候只能允许一个持有者。但是被多个持有者则不能保证原子性。

信号量与自旋锁不同,只有得到信号量才能执行代码,获得不到就睡眠。

不能在内核之外使用,它是一种睡眠锁。就是当一个想去占用信号量的时候,但是此时信号量已经被别人占有,信号量就会进入等待队列,进行睡眠。当信号量被释放后,等待队列中的任务会被唤醒。从而得到信号量。

信号量的初始值为多少是,就代表最多可以被多少个进程同时访问。当为1的时候,就为互斥。为0的时候就代表直接上锁。

动态:

struct semaphore sem;//定义信号量

void sema_init (struct semaphore *sem, int val);//初始化信号量,设置初始值,val

void init_MUTEX (struct semaphore *sem);该函数用于初始化一个互斥锁,即它把信号量 sem的值设置为1

void init_MUTEX_LOCKED (struct semaphore *sem);该函数也用于初始化一个互斥锁, 但它把信号量sem的值设置为0,即一开始就处在已锁状态。

静态:

DECLARE_MUTEX(name)定义一个信号量name,并初始化它的值为1

DECLARE_MUTEX_LOCKED(name)定义一个信号量name,但把它的初始值设置为0 即锁在创建时就处在已锁状态。

int down_interruptible(struct semaphore * sem)down不会被信号(signal)打断,但 down_interruptible能被信号打断

void up(struct semaphore * sem);   该函数释放信号量sem,即把sem的值加1,如果sem 的值为非正数,表明有任务等待该信号量,因此唤醒这些等待者。

3代码:

struct semaphore sem;

init_MUTEX(&(globalmem_devp->sem));

 Write

if(down_interruptible(&dev->sem))

{

return -ERESTARTSYS;

}

 up(&dev->sem);

Read

if(down_interruptible(&dev->sem))

{

return -ERESTARTSYS;

  up(&dev->sem);

十、信号量同步 

信号量:用于一个执行单元等待另一执行单元执行完某事。

4代码

struct semaphore sem;

init_MUTEX_LOCKED(&(globalmem_devp->sem));

Wtite:后面

up(&dev->sem);//释放信号量

Read:前面

if(down_interruptible(&dev->sem))

{

return -ERESTARTSYS;

}

完成量同步:

struct completion completion;

init_completion(&(globalmem_devp->completion));

Write:后面

complete(&dev->completion);

Read:前面

wait_for_completion(&dev->completion);

十一、自旋锁VS信号量读写信号量 

信号量和自旋锁属于不同层次的互斥手段,前者实现依赖后者。在信号量本身实现上,为了保证信号量结构存取的原子性,在多CPU中需要自旋锁来互斥

信号量最多可以允许多个持有者,而自旋锁在任何时候只能允许一个持有者。当然也有信号量叫互斥信号量(只能一个持有者),允许有多个持有者的信号量叫计数信号量。

信号量适合于保持时间较长的情况;而自旋锁适合于保持时间非常短的情况,在实际应用中自旋锁控制的代码只有几行,而持有自旋锁的时间也一般不会超过两次上下文切换的时间,因为线程一旦要进行切换,就至少花费切出切入两次,自旋锁的占用时间如果远远长于两次上下文切换,我们就应该选择信号量。

十二、读写信号量 

5代码

 struct rw_semaphore semrw;

 init_rwsem(&(globalmem_devp->semrw));//2初始化

 Write

down_write(&(dev->semrw));

up_write(&dev->semrw);//释放信号量

Read

down_read(&(dev->semrw));

up_read(&(dev->semrw));

十三、互斥体

6代码

struct mutex mutex;

mutex_init(&(globalmem_devp->mutex));

 Write

if(mutex_lock_interruptible(&dev->mutex))

{

return -ERESTARTSYS;

}

mutex_unlock(&dev->mutex);//释放信号量

Read

if(mutex_lock_interruptible(&dev->mutex))

{

return -ERESTARTSYS;

}

 mutex_unlock(&dev->mutex);

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