Chinaunix首页 | 论坛 | 认证专区 | 博客 登录 | 注册

无氧森林

和敬清寂

  • 博客访问: 149479
  • 博文数量: 65
  • 博客积分: 26
  • 博客等级: 民兵
  • 技术积分: 1164
  • 用 户 组: 普通用户
  • 注册时间: 2012-07-23 14:46
个人简介

linux --- 一切皆文件

文章分类

全部博文(65)

文章存档

2017年(7)

2016年(11)

2015年(1)

2014年(2)

2013年(34)

2012年(10)

微信关注

IT168企业级官微



微信号:IT168qiye



系统架构师大会



微信号:SACC2013

订阅
热词专题
linux同步和竞争 2013-10-13 00:20:41

分类: LINUX

有SMP,中断,调度,内核竞争产生同步和竞争问题,通过 atomic_t,spinlock_t,mutex_t,semaphore解决以上产生的问题。


atomic_t 原子操作
spinlock_t 自旋锁
mutex_t 互斥量
semaphore 信号量


*临界区(critical region)
访问要保护的变量的代码段,称为临界区。临界区中同一时间只能一个人进入。
临界区的代码可能分散在不同的函数中。如果要对临界区加锁,则必须保证在临界区的所有部分都加锁。如果有遗漏,则加锁是不可靠的。


*同步(synchronization)
其实就是加锁。同步是保证临界区只进一个人的机制。


*竞争(race condition)
如果有多个人同时进入临界区,就称为竞争。一般来说,认为出现竞争是错误的。


*加锁是自愿的
内核不强迫程序加锁,但一旦使用了锁,驱动设计人员应该保证正确地使用锁,并保证在临界区的每个部分都加上锁


*不要用锁来实现功能
锁就是用来避免对变量同时访问,是一种保护机制。
如果将加锁相关代码去掉,程序功能应该没有任何变化。


*应该在一开始设计代码时,就考虑如何加锁
一定不要代码设计完毕后再加锁,很容易出问题。最好在设计私有结构体时,就开始考虑锁的使用。锁一般会包括到私有结构体中,根据设备的复杂程度,有>可能需要多把锁。


常见死锁的情况:
1:自死锁常见情况
函数A里面加锁,然后调用B函数里面没有加锁,而函数B调用函数C,函数C里面有加锁,然后产生了自死锁。
A(lock(mutex))  --->B() ---->C(lock(mutex))
解决方法:只有借口函数里提供锁,在库里提供非加锁版本被借口函数调用
2:ABBA死锁
        A                  B
        lock(a)           lock(b)
        1:去执行B      这样a就永远加不上锁了
        lock(b)              lock(a)

解决方法:尝试加锁,失败则放弃之前加的锁


2.linux内核中的哪些机制导致必须要加锁
换句话说,如果没有这些机制,就可以不加锁了。
也就是说,如果当前内核不可能出现“同时”,则不必加锁。


可能导致同时访问变量的内核机制有:
(1)SMP
真同时,必须加锁
(2)中断
上锁关中断,在进程上下文中关中断,可以在中断上下文关中断
(3)内核抢占
2.4内核:内核态不抢占,用户态抢占
2.6内核:内核抢占机制,make menuconfig --> kernel__featch --> preemptible_kernel 开启或者关闭内核抢占


用户程序使用内核抢占则可以提高响应速度
服务器则不需要抢占,抢占反而消耗资源
(4)schedule()
在硬件中断函数里处理进程的调度


需要了解这些机制,才能够正确使用加锁函数。


atomic_t 原子操作:ARM是用汇编实现的,而x86是直接锁总线
ARM:
atomic_t i;
atomic_add(1,&i);
static inline void atomic_add(int i, atomic_t *v)
{
unsigned long tmp;
int result;


__asm__ __volatile__("@ atomic_add\n"
"1: ldrex %0, [%2]\n" 将counter放到result中
" add %0, %0, %3\n" 将result和i相加
" strex %1, %0, [%2]\n" 将result的值写入到counter中
" teq %1, #0\n" 然后检测tmp返回值
" bne 1b" 1b,就是back到1的位置,1f则向前跳转到1的位置,1为内部标签
如果是0,则说明没有产生冲突,如果不等于0,则就说明数据有丢弃,则返回1,继续计算
: "=&r" (result), "=&r" (tmp)
: "r" (&v->counter), "Ir" (i)
: "cc");
}


int i;
atomic_t j;
j = ATOMIC_INIT(1);
i = atomic_read(&j);
atomic_inc(&j); //j++;
atomic_dec(&j); //j --;
atomic_add(1,&j); //j += 1;
atomic_sub(1,&j); //j -= 1;


spinlock_t 自旋锁
spinlock_t,ns秒级,临界区内的代码不能睡眠。
1:SMP 适合使用
2:中断 加锁关中断,解锁开中断,适合使用
3:内核抢占: 加锁自动关闭内核抢占,解锁开启内核抢占,适合使用
4:schedle调度,不适用


如果临界去里要用到kzalloc(100,GFP_KERNEL),copy_to_user等函数,则不能使用spinlock_t


#include
spinlock_t mylock;


spin_lock_init(&mylock);


(1)如果临界区可能被中断处理函数打断,并影响到要保护的变量,则应该在加锁的同时关闭中断
//加锁同时关中断,将CPSR的当前值存储到flags中
//解锁时打开中断,并将flags的值恢复到CPSR中
unsigned long flags;
spin_lock_irqsave(&mylock, flags);
...//临界区
spin_unlock_irqrestore(&mylock, flags);




(2)普通的加解锁
spin_lock(&mylock);
...//临界区
spin_unlock(&mylock);




5. mutex互斥锁
mutex锁也是内核中非常重要的锁,属于重量级的锁,等待锁的时间常常为ms级
持有锁时可以睡眠(必须确保能被唤醒)


1:SMP 适合使用
2:中断 中断中不能睡眠,不适用
3:内核抢占: 不关闭内核抢占,出现程序切换,适用
4:schedle调度,适用


#include


//声明mutex锁
struct mutex mylock;


//用之前要先初始化
mutex_init(&mylock);


//加锁和解锁
mutex_lock(&mylock);
ret = mutex_lock_interruptible(&mylock); //睡眠可以接收到信号
if (ret)
   return -ERESTARTSYS;
...//临界区
mutex_try_lock(&mylock); //尝试加锁失败返回
mutex_unlock(&mylock);


mutex_t
定义在中。2.6内核早期没有实现mutex锁,用semaphore来模拟mutex锁的功能。


在现在的内核中,基本上已经不用semaphore来保护临界区。如果内核中某些资源限定了访问人数(比如只允许3个人同时访问),这时候可以用semaphore进行保
护。


sempahore的使用例子如下:


#include


//声明信号量
struct semaphore mysem;


//按照资源的限定初始化信号量
sema_init(&mysem, 3);


down(&mysem);
down_interruptible(&mysem);
...//访问受限资源的代码
up(&mysem);


******make rmpropor 将编译的内核恢复到最初状态
kernel 2.6.19 :   .config里面的宏会转成 include/linux/autoconf.h
after : 将在xxx.o.cmd 里面有信息



阅读(1159) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册