在内核中,可能出现多个进程访问同一个对象、进程和硬中断访问同一个对象、多个处理器访问同一个对象等现象,我们需要使用互斥技术,确保每个时刻只有一个主体可以进入临界区访问对象
如果临界区的执行时间比较长或者可能睡眠,可使用:
如果申请临界区的执行时间短,并且不会睡眠,那么使用上面的锁不太合适,因为进程切换代价很高,可以使用:
进程还可以使用以下技术
避免使用锁的互斥技术
信号量允许多个进程同时进入临界区,大多情况下只允许一个进程进入临界区,把信号量的计数值设置为1,即二值信号量,也称为互斥信号量
与自旋锁相比,信号量适合保护比较长的临界区,因为竞争信号量时进程可能睡眠被再次唤醒,代价高
struct semaphore {
raw_spinlock_t lock; /* 自旋锁,保护信号量其他成员 */
unsigned int count; /* 计数器,表示还可以允许多少进程进入临界区 */
struct list_head wait_list; /* 等待进入临界区的进程链表 */
};
struct rw_semaphore {
atomic_long_t count;
atomic_long_t owner;
#ifdef CONFIG_RWSEM_SPIN_ON_OWNER
struct optimistic_spin_queue osq; /* spinner MCS lock */
#endif
raw_spinlock_t wait_lock; /* 自旋锁,保护信号量其他成员 */
struct list_head wait_list; /* 等待进入临界区的进程链表 */
#ifdef CONFIG_DEBUG_RWSEMS
void *magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
};
互斥锁只允许一个进程进入临界区,适合保护比较长的临界区,因为竞争互斥锁时进程可能睡眠和再次唤醒,代价很高,尽管可以把二值信号量当作互斥锁使用
struct mutex {
atomic_long_t owner;
spinlock_t wait_lock;
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
struct optimistic_spin_queue osq; /* Spinner MCS lock */
#endif
struct list_head wait_list;
#ifdef CONFIG_DEBUG_MUTEXES
void *magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
};
如果使用实时互斥锁,编译内核时需要开启配置宏CONFIG_RT_MUTEXES
struct rt_mutex {
raw_spinlock_t wait_lock;
struct rb_root_cached waiters;
struct task_struct *owner;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
};