在操纵体系中,并发是指一个时候段中有几个法度都处于已启动运行到运行完毕之间,且这几个法度都是在同一个处理惩罚机上运行,但任一个时刻点上只有一个法度在处理惩罚机上运行。并发轻易导致竞争的题目。竞争就是两个或两个以上的过程同时接见一个资料,同时引起资料的错误。并发把握机制有以下几种:
1.原子变量操纵:
原子变量操纵(分为原子整型操纵和原子位操纵)就是毫不会在履行完毕前被任何其他任务和时候打断,不会履行一半,又去履行其他代码。原子操纵须要硬件的支撑,是以是架构相干的,其API和原子类型的定义都在include/asm/atomic.h中,应用汇编说话实现。
在linux中,原子变量的定义如下:
typedef struct { volatile int counter; } atomic_t;
关键字volatile用来暗示GCC不要对该类型做数据优化,所以对这个变量counte的接见都是基于内存的,不要将其缓冲到存放器中。存储到存放器中,可能导致内存中的数据已经改变,而存放此中的数据没有改变。
(1)原子整型操纵:
1)定义atomic_t变量:
#define ATOMIC_INIT(i) ( (atomic_t) { (i) } )
atomic_t v = ATOMIC_INIT(0); //定义原子变量v并初始化为0
2)设置原子变量的值:
#define atomic_set(v,i) ((v)->counter = (i)) void atomic_set(atomic_t *v, int i);//设置原子变量的值为i
3)获取原子变量的值:
#define atomic_read(v) ((v)->counter + 0) atomic_read(atomic_t *v);//返回原子变量的值
4)原子变量加/减:
static __inline__ void atomic_add(int i, atomic_t * v); //原子变量增长i static __inline__ void atomic_sub(int i, atomic_t * v); //原子变量削减i
5)原子变量自增/自减:
#define atomic_inc(v) atomic_add(1, v); //原子变量加1 #define atomic_dec(v) atomic_sub(1, v); //原子变量减1
6)操纵并测试:
//这些操纵对原子变量履行自增,自减,减操纵后测试是否为0,是返回true,不然返回false #define atomic_inc_and_test(v) (atomic_add_return(1, (v)) == 0) static inline int atomic_add_return(int i, atomic_t *v)
(2)原子位操纵(按照数据的每一位零丁进行操纵):
static inline void set_bit(nr, void *addr); //设置addr地址的第nr位,所谓设置位即将位写为1 static inline void clear_bit(nr,void *addr); //清除addr地址的第nr位,所谓清除位即将位写为0 static inline void change_bit(nr,void *addr); static inline void test_bit(nr, void *addr); static inline int test_and_set_bit(nr, void *addr); static inline int test_and_clear_bit(nr, void *addr); static inline int test_and_change_bit(nr, void *addr);
原子操纵的长处编写简单;毛病是功能太简单,只能做计数操纵,保护的器材太少。
2.自旋锁
自旋锁是专为防止多处理惩罚器并发而引入的一种锁,它应用于中断处理惩罚等项目组。对于单处理惩罚器来说,防止中断处理惩罚中的并发可简单采取封闭中断的体式格式,不须要自旋锁。
自旋锁最多只能被一个内核任务持有,若是一个内核任务试图恳求一个已被争用(已经被持有)的自旋锁,那么这个任务就会一向进行忙轮回——扭转——守候锁从头可用。如果锁未被争用,恳求它的内核任务便能立即获得它并且持续进行。自旋锁可以在任何时刻防止多于一个的内核任务同时进入临界区,是以这种锁可有效地避免多处理惩罚器上并发运行的内核任务竞争共享资料。
(1)自旋锁的应用:
spinlock_t spin; //定义自旋锁 spin_lock_init(lock); //初始化自旋锁 spin_lock(lock); //成功获得自旋锁立即返回,不然自旋在那边直到该自旋锁的对峙者开释 spin_trylock(lock); //成功获得自旋锁立即返回真,不然返回假,而不是像上一个那样"在原地打转" spin_unlock(lock);//开释自旋锁
应用代码:
spinlock_t lock; spin_lock_init(&lock); spin_lock (&lock); ....//临界资料区 spin_unlock(&lock);
(2)重视事项:
1)自旋锁是一种忙守候。它是一种合适短时候锁定的轻量级的加锁机制。
2)自旋锁不克不及递归应用。自旋锁被设计成在不合线程或者函数之间同步。这是因为,若是一个线程在已经持有自旋锁时,其处于忙守候状况,则已经没有机会开释本身持有的锁了。若是这时再调用自身,则自旋锁永远没有履行的机会了。
3.旌旗灯号量
linux中,供给了两种旌旗灯号量:一种用于内核法度中,一种用于应用法度中。这里只讲属前者。
旌旗灯号量和自旋锁的应用办法根蒂根基一样。与自旋锁比拟,旌旗灯号量只有当获得旌旗灯号量的过程或者线程时才干够进入临界区,履行临界代码。旌旗灯号量和自旋锁的最大差别在于:当一个过程试图去获得一个已经锁定的旌旗灯号量时,过程不会像自旋锁一样在远处忙守候。
旌旗灯号量是一种睡眠锁。若是有一个任务试图获得一个已被持有的旌旗灯号量时,旌旗灯号量会将其推入守候队列,然后让其睡眠。这时处理惩罚器获得自由去履行其它代码。当持有旌旗灯号量的过程将旌旗灯号量开释后,在守候队列中的一个任务将被唤醒,从而便可以获得这个旌旗灯号量。
(1)旌旗灯号量的实现:
在linux中,旌旗灯号量的定义如下:
struct semaphore { spinlock_t lock; //用来对count变量起保护感化。 unsigned int count; // 大于0,资料余暇;便是0,资料忙,但没有过程守候这个保护的资料;小于0,资料不成用,并至少有一个过程守候资料。 struct list_head wait_list; //存放守候队列链表的地址,当前守候资料的所有睡眠过程都邑放在这个链表中。 };
(2)旌旗灯号量的应用:
1)定义旌旗灯号量:
struct semaphore sem;
2)初始化旌旗灯号量 :
static inline void sema_init(struct semaphore *sem, int val); //设置sem为val #define init_MUTEX(sem) sema_init(sem, 1) //初始化一个用户互斥的旌旗灯号量sem设置为1 #define init_MUTEX_LOCKED(sem) sema_init(sem, 0) //初始化一个用户互斥的旌旗灯号量sem设置为0
定义和初始化可以一步完成:
DECLARE_MUTEX(name); //该宏定义旌旗灯号量name并初始化1 DECLARE_MUTEX_LOCKED(name); //该宏定义旌旗灯号量name并初始化0
3)获取(锁定)旌旗灯号量:
void down(struct semaphore *sem);
//该函数会导致睡眠,所以不克不及在中断高低文中应用; * * down - acquire the semaphore * @sem: the semaphore to be acquired * * Acquires the semaphore. If no more tasks are allowed to acquire the * semaphore, calling this function will put the task to sleep until the * semaphore is released. * * Use of this function is deprecated, please use down_interruptible() or * down_killable() instead. */
int down_interruptible(struct semaphore *sem);
/** *该函数进入睡眠后,可以被旌旗灯号唤醒; * down_interruptible - acquire the semaphore unless interrupted * @sem: the semaphore to be acquired * * Attempts to acquire the semaphore. If no more tasks are allowed to * acquire the semaphore, calling this function will put the task to sleep. * If the sleep is interrupted by a signal, this function will return -EINTR. * If the semaphore is successfully acquired, this function returns 0. */
int down_killable(struct semaphore *sem);
/** * down_killable - acquire the semaphore unless killed * @sem: the semaphore to be acquired * * Attempts to acquire the semaphore. If no more tasks are allowed to * acquire the semaphore, calling this function will put the task to sleep. * If the sleep is interrupted by a fatal signal, this function will return * -EINTR. If the semaphore is successfully acquired, this function returns * 0. */
4)开释旌旗灯号量
void up(struct semaphore *sem); //开释旌旗灯号量sem,唤醒守候者
4.完成量
它用于一个履行单位守候另一个履行单位履行完某事;
struct completion { unsigned int done; //大于0,默示完成量的函数可以立即履行,不要要守候;便是0,将拥有完成量的线程置于守候状况。 wait_queue_head_t wait; };
应用办法:
1)定义完成量:
struct completion com;
2)初始化:
init_completion(&com); //如果感觉这两步麻烦,就再给你来个宏即定义又初始化DECLARE_COMPLETION(com);
3)守候完成量:
void __sched wait_for_completion(struct completion *x); //守候一个completion被唤醒
/** * wait_for_completion: - waits for completion of a task * @x: holds the state of this particular completion * * This waits to be signaled for completion of a specific task. It is NOT * interruptible and there is no timeout. * * See also similar routines (i.e. wait_for_completion_timeout()) with timeout * and interrupt capability. Also see complete(). */
int __sched wait_for_completion_interruptible(struct completion *x);//可中断的wait_for_completion
/** * wait_for_completion_interruptible: - waits for completion of a task (w/intr) * @x: holds the state of this particular completion * * This waits for completion of a specific task to be signaled. It is * interruptible. */
unsigned long __sched wait_for_completion_timeout(struct completion *x, unsigned long timeout);//带超时处理惩罚的wait_for_completion
/** * wait_for_completion_timeout: - waits for completion of a task (w/timeout) * @x: holds the state of this particular completion * @timeout: timeout value in jiffies * * This waits for either a completion of a specific task to be signaled or for a * specified timeout to expire. The timeout is in jiffies. It is not * interruptible. */
4)唤醒完成量
void complete(struct completion *x); //只唤醒一个守候的过程或线程。 void complete_all(struct completion *x); //唤醒所有守候这个完成量的过程或者线程。
跋文:除了上述几种广泛应用的的并发把握机制外,还有中断樊篱、次序锁(seqlock)、RCU(Read-Copy-Update)等等,做个简单总结如下图: