尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

linux内核原理-原子变量,自旋锁,互斥锁/信号量

linux内核原理-原子变量,自旋锁,互斥锁/信号量 1.原子变量针对数值类型基础的算术运算提供的一种保证运算原子性机制(1). 要求a. 必须针对atomic_t类型b. 必须采用提供的原子方法实现基础算术运算原子性(2). 原理typedef struct{volatile int counter;}atomic_t;通过volatile使得每次访问counter必须访问内存以atomic_add为例说明linux内核如何保证原子操作原子性通过lock修饰汇编指令lock使得其后的一条内存访问汇编指令在执行时以原子方式不可分割的执行完成#ifdef CONFIG_SMP#define LOCK lock ; #else#define LOCK #endifstatic __inline__ void atomic_add(int i, atomic_t *v){__asm__ __volatile__(LOCKaddl %1,%0:m(v-counter):ir(i),m(v-counter));}2.自旋锁自旋锁用于保护临界区即同时只应该被一个访问者访问的区域其特性是当某个访问者申请锁时若申请不到此访问者以自旋方式等待访问者会100%占据cpu持续检测typedef struct{volatile unsigned int lock;#ifdef CONFIG_DEBUG_SPINLOCKunsigned magic;#endif#ifdef CONFIG_PREEMPTunsigned int break_lock;#endif}spinlock_t;(1). 定义与初始化#define SPINLOCK_MAGIC 0xdead4ead#ifdef CONFIG_DEBUG_SPINLOCK#define SPINLOCK_MAGIC_INIT , SPINLOCK_MAGIC#else#define SPINLOCK_MAGIC_INIT /* */#endif#define SPIN_LOCK_UNLOCKED (spinlock_t) { 1 SPINLOCK_MAGIC_INIT }#define DEFINE_SPINLOCK(x) spinlock_t x SPIN_LOCK_UNLOCKED通过DEFINE_SPINLOCK(test);定义了一个名字为test的自旋锁并对其执行了初始化(2). 加锁#define _spin_lock(lock) \do{\preempt_disable();\_raw_spin_lock(lock);\__acquire(lock);\}while(0)#define spin_lock(lock) _spin_lock(lock)通过spin_lock(test);可以申请对test加锁preempt_disable();的作用是在中断返回时禁止执行重新调度来避免当前进程被切换掉我们分析_raw_spin_lock(lock);#define spin_lock_string \\n1:\t\lock ; decb %0\n\t\jns 3f\n\2:\t\rep;nop\n\t\cmpb$0,%0\n\t\jle 2b\n\t\jmp 1b\n\3:\n\tstatic inline void _raw_spin_lock(spinlock_t *lock){__asm__ __volatile__(spin_lock_string:m(lock-lock)::memory);}上述获取锁的逻辑是a. 原子的递减lock-lock这使得内存中此变量减少1b. 分析上一步结果若未产生负数这意味着我们已经成功获取了锁继续执行若产生负数这意味着之前存在至少一个使用者已经获取了这个锁且未释放进入cc. 循环迭代空操作读取lock-lock实时值和0比较若小于等于0继续循环迭代若大于0这对应锁的使用者释放了锁跳到a因为多核下可能多个等待着此时同时感知此情况因此需让它们接下来都执行a首个执行成功者获得锁后续执行者将再次回到c中循环迭代的等待逻辑__acquire(lock);其实是一个空操作(3). 释放锁#define spin_unlock(lock) _spin_unlock(lock)通过spin_unlock(test);可以释放test#define _spin_unlock(lock) \do{\_raw_spin_unlock(lock);\preempt_enable();\__release(lock);\}while(0)我们分析_raw_spin_unlock(lock);#define spin_unlock_string \movb$1,%0\:m(lock-lock)::memorystatic inline void _raw_spin_unlock(spinlock_t *lock){__asm__ __volatile__(spin_unlock_string);}释放锁的过程是将数值1直接设置到lock-lock的过程在x86体系下单独的读一次内存写一次内存汇编操作无需lock修饰也是原子的(4).尝试加锁通过一个原子交换汇编指令即可若此前未被加锁则获得锁同时设置了lock-lock为0若此前已被加锁再次为其设置为0也无错3.互斥锁互斥锁用于保护临界区即同时只应该被一个访问者访问的区域其特性是当某个访问者申请锁时若申请不到此访问者以阻塞方式等待主动放弃cpu待条件满足时由另一角色执行唤醒任务互斥锁在实现上可认为特殊的信号量当某个信号量的初始资源数量为1时此信号量就是互斥锁(1). 定义与初始化#define MUTEX_DEFAULT 0x0struct semaphore{raw_spinlock_t lock;unsigned int count;struct list_head wait_list;};typedef struct semaphore mutex_t;mutex_t test;即可定义一个名为test的互斥锁#define __SEMAPHORE_INITIALIZER(name, n) \{\.lock__RAW_SPIN_LOCK_UNLOCKED((name).lock),\.countn,\.wait_listLIST_HEAD_INIT((name).wait_list),\}static inline void sema_init(struct semaphore *sem, int val){static struct lock_class_key __key;*sem(struct semaphore)__SEMAPHORE_INITIALIZER(*sem, val);lockdep_init_map(sem-lock.dep_map,semaphore-lock,__key,0);}#define mutex_init(lock, type, name) sema_init(lock, 1)通过mutex_init(test, MUTEX_DEFAULT, test);即可完成test初始化(2). 申请锁#define mutex_lock(lock, num) down(lock)通过mutex_lock(test, 1)即可执行加锁申请void __sched down(struct semaphore *sem){unsigned long flags;might_sleep();raw_spin_lock_irqsave(sem-lock, flags);if(likely(sem-count0))sem-count--;else__down(sem);raw_spin_unlock_irqrestore(sem-lock, flags);}上述过程可描述为a. 申请内部自旋互斥锁以便互斥访问临界区b. 若sem-count大于0则递减释放锁这时我们已经完成互斥锁的申请c. 若sem-count小于等于0意味着此时无法得到锁__down内部会将当前线程作为一个等待对象放在此互斥锁的等待着链表上然后设置自身状态为TASK_UNINTERRUPTIBLE并主动放弃调度后续锁的使用者执行释放时若释放后sem-count大于0会检测是否存在等待者存在则对其中一个等待着执行唤醒所以上述在立即得到锁时继续运行在暂时无法得到锁时阻塞自身放弃调度直到后续被唤醒得到锁再继续(3). 释放锁#define mutex_unlock(lock) up(lock)通过mutex_unlock(test)即可释放锁void __sched up(struct semaphore *sem){unsigned long flags;raw_spin_lock_irqsave(sem-lock, flags);if(likely(list_empty(sem-wait_list)))sem-count;else__up(sem);raw_spin_unlock_irqrestore(sem-lock, flags);}上述过程为a. 获取内部自旋锁以便互斥访问临界区b. 若无人等待递增sem-count即可完成释放c. 若存在等待者唤醒一个static noinline void __sched __up(struct semaphore *sem){struct semaphore_waiter *waiterlist_first_entry(sem-wait_list, struct semaphore_waiter, list);list_del(waiter-list);waiter-uptrue;wake_up_process(waiter-task);}(4). 尝试加锁int __sched down_trylock(struct semaphore *sem){unsigned long flags;int count;raw_spin_lock_irqsave(sem-lock, flags);countsem-count -1;if(likely(count0))sem-countcount;raw_spin_unlock_irqrestore(sem-lock, flags);return(count0);}很好解释先看能否得到锁得不到也立即返回
返回列表