Linux同步的实现初步学习

来源:互联网 发布:java课程设计案例源码 编辑:程序博客网 时间:2024/06/05 14:51

内核的同步方法:

Limux提供了一组相当完备的内核同步方法。这些方法使得内核开发者们能编写出高效而又自由竞争的代码。内容主要包括他们的接口,行为和用途。

       同步方法的基石是原子操作。他是其他同步方法的基础。原子操作保证指令以原子的方式执行。即执行过程不会被打断。原子操作也指的是不能够被分割的指令。

       内核提供了两组原子操作接口——一组针对整数进行操作,另一组针对单独的位进行操作。在Linux支持的所有体系结构上都实现了这两组接口。大多数体系结构会提供支持原子操作的简单算术指令。

原子整数操作:

       针对整数的原子操作只能针对atomic_t类型的数据进行处理。atomic_t类型确保编译器不对相应的值进行访问优化——这一点使得原子操作最终接收到正确的内存地址。在不同体系结构上实现原子操作的时候,使用atomic_t可以屏蔽期间的差异。

       Atomic_t类型定义在<linux/types.h>中。

       Typedefstruct

{

       Volatile int counter;

}atomic_t;

       Atomic_t在SPARC上只能将整数类型当做24位来用。

定义一个atomic_t类型变量:

       Atomic_tv;//定义一个v变量

       Atomic_tu = ATOMIC_INIT(0);

 

原子的操作

       Atomic_set(&v,4);

       Atomic_add(2,&v)

Atomic_inc(&v);

 

原子操作最常见的用途就是实现计数器。`

同样,在64位机上有64位的原子操作。

原子位操作

内核提供了一组针对位这一级数据进行操作的函数。他们是与体系结构相关的操作。

自旋锁

 

自旋锁的实现方法:

自旋锁的和体系结构密切相关,代码往往通过汇编实现,

基本的使用形式如下:

       DEFINE_SPINLOCK(mr_lock)

       Spin_lock(&mr_lock);

       Spin_unlock(&mr_lock);

自旋锁位多处理器机器提供了防止并发访问所需要的保护机制。在但处理机器上,变异的时候并不会加入自旋锁。他仅仅被当做一个设置内核抢占机制是否被开启的开关,如果禁止内核抢占,那么在编译时自旋锁就会被完全剔除。

自旋锁时不可递归的。

 

自旋锁可以使用在中断处理程序当中,此处不能使用信号量,因为信号量会导致睡眠。

 

读-写自旋锁

       有时候,锁的用途可以明确地分为读取和写入两个场景。任务列表就很符合这种场景。

信号量

       Linux中的信号量是一种睡眠锁。如果有一个任务试图获得一个不可用的信号量时,信号量将会将其推入一个等待队列,然后让其睡眠,这时候处理器能重获自由,从而去执行其他代码。当持有的信号量可用时,处于等待队列中的那个任务将被唤醒,并获得该信号量。

信号量的睡眠特性的一些结论

       由于争用信号量的进程在等待锁重新可用时会睡眠,所以信号领适用于锁会被长期争用的情况。

       锁被短期持有时,信号量就不太适用了,因为睡眠,维护等待列队以及唤醒所花费的开销可能比锁被占用的全部时间还长。

       由于执行线程在锁征用期时会睡眠,所以只能在进程上下文才能获取信号量锁。因为在中断上下文中时不能进行调度的。

       可以在持有信号量的时候去睡眠,其他试图获得信号量的进程不会因此而死锁。

       占用信号量的时候,不能占用自旋锁。等待信号量时可能会睡眠,而自旋锁不允许睡眠。

计数信号量和二值信号量

       计数信号量可以大于一,而二值信号量也叫做互斥信号量。信号量支持两个原子操作P()和V()操作。后来的系统吧两种操作分别叫做down() 和up()操作。

创建和初始化信号量

       信号量的实现是与体系结构相关的。可以通过以下方式静态的申明信号量——其中name是信号量变量名,count是信号量的使用数量。

       Struct semaphore name;

       Sema_init(&name,count);

       创建更为普通的互斥信号量可以使用以下的快捷方式。

       Static DECLEAR_MUTEX(name);

       更常见的情况是,信号量作为一个大数据结构一部分动态的创建。此时,只有指向该动态创建的信号量的间接指针。可以使用如下函数来对他进行初始化

       Sema_init(sem,count);

Sem是指针,count是信号量的使用者数量。

使用信号量

       函数down_interruptible()试图获取指定信号量,不可用则进程进入睡眠状态。

       使用down_trylock()函数,可以尝试以阻塞方式来获取指定的信号量。

要释放指定的信号量,需要调用up()函数。

互斥体

       为了找到一个更简单的睡眠锁,和你喝开发者们引入了互斥体。互斥体指的是任何可以睡眠的强制互斥锁。互斥体是一种互斥信号。

       静态定义mutex,可以这样做:

       DEFINE_MUTEX(name);

       初始化mutex,可以这样做

       Mutex_init(&mutex);

       对互斥锁锁定和解锁:

       Mutex_lock(&mutex);

       //临界区

       Mutex_unlock(&mutex);

这就是一个简化版的信号量。

Mutex(互斥体)使用的限制

       任何时刻只有一个任务可以持有mutex,也就是说,mutex的使用技术永远是1;

       给mutex上锁着也必须给其解锁。不可以在一个上下文中锁定一个mutex,而在另一个上下文当中给他解锁。这决定了mutex不适合内核同用户空间复杂的同步场景。经常在同一个人上下文当中上锁和解锁。

       递归的上锁和劫夺是不允许的。不能递归的持有用一个锁。同样也不能再去解锁一个已经解开的mutex

       当持有一个mutex时,进程不可以退出。

       Mutex不能再中断或者下半部中使用。

今天学习到这里,爱你YZ

 

       

0 0
原创粉丝点击