持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第33天,点击查看活动详情
信号量
信号量允许多个进程同时进入临界区,大多数情况下只允许一个进程进入临界区,把信号量的计数值设置为1,即二值信号量,这种信号量称为互斥信号量。 和自旋锁相比,信号量适合保护比较长的临界区,因为竞争信号量时进程可能睡眠和再次唤醒,代价很高。 内核使用的信号量定义如下。
include/linux/semaphore.h
struct semaphore {
raw_spinlock_t lock;
unsigned int count;
struct list_head wait_list;
};
成员lock是自旋锁,用来保护信号量的其他成员。 成员count是计数值,表示还可以允许多少个进程进入临界区。 成员wait_list是等待进入临界区的进程链表。 初始化静态信号量的方法如下。 (1)__SEMAPHORE_INITIALIZER(name, n):指定名称和计数值,允许n个进程同时进入临界区。
(2)DEFINE_SEMAPHORE(name):初始化一个互斥信号量。 在运行时动态初始化信号量的方法如下:static inline void sema_init(struct semaphore *sem, int val); 参数val指定允许同时进入临界区的进程数量。 获取信号量的函数如下。
(1)void down(struct semaphore *sem); 获取信号量,如果计数值是0,进程深度睡眠。
(2)int down_interruptible(struct semaphore *sem); 获取信号量,如果计数值是0,进程轻度睡眠。
(3)int down_killable(struct semaphore *sem); 获取信号量,如果计数值是0,进程中度睡眠。
(4)int down_trylock(struct semaphore *sem); 获取信号量,如果计数值是0,进程不等待。
(5)int down_timeout(struct semaphore *sem, long jiffies); 获取信号量,指定等待的时间。
释放信号量的函数如下:void up(struct semaphore *sem);
读写信号量
读写信号量是对互斥信号量的改进,允许多个读者同时进入临界区,读者和写者互斥,写者和写者互斥,适合在以读为主的情况使用。 读写信号量的定义如下:
include/linux/rwsem.h
struct rw_semaphore {
atomic_long_t count;
struct list_head wait_list;
raw_spinlock_t wait_lock;
struct task_struct *owner;
…
};
初始化静态读写信号量的方法如下:DECLARE_RWSEM(name); 在运行时动态初始化读写信号量的方法如下:init_rwsem(sem); 申请读锁的函数如下。
(1)void down_read(struct rw_semaphore *sem); 申请读锁,如果写者占有写锁或者正在等待写锁,那么进程深度睡眠。
(2)int down_read_trylock(struct rw_semaphore * sem); 尝试申请读锁,不会等待。如果申请成功,返回1;否则返回0。 释放读锁的函数如下:void up_read(struct rw_semaphore *sem); 申请写锁的函数如下。
(1)void down_write(struct rw_semaphore *sem); 申请写锁,如果写者占有写锁或者读者占有读锁,那么进程深度睡眠。
(2)int down_write_killable(struct rw_semaphore *sem); 申请写锁,如果写者占有写锁或者读者占有读锁,那么进程中度睡眠。
(3)int down_write_trylock(struct rw_semaphore *sem); 尝试申请写锁,不会等待。如果申请成功,返回1;否则返回0。 占有写锁以后,可以把写锁降级为读锁,函数如下:void downgrade_write(struct rw_semaphore *sem);
释放写锁的函数如下:void up_write(struct rw_semaphore *sem);