原子操作

自旋锁

自旋锁(spin lock)和互斥锁类似,但是自旋锁在失败时不会放弃 CPU,而是不断尝试获取锁。直至成功。Linux 内核中的 自旋锁是不可递归的

读写自旋锁

读写自旋锁和普通自旋锁类似,但是区分了读写操作。下面是相关的 API:

Table 1. linux/rwlock_types.h
API作用

DEFINE_RWLOCK(x)

定义一个名为 x 的读写自旋锁

rwlock_init

初始化读写锁

read_lock

读加锁

read_unlock

读解锁

write_unlock

写加锁

write_unlock

写解锁

内核中的读写自旋锁是一种读优先自旋锁,这意味着即使是在尝试获取写锁的过程中依然可以加读锁。

信号量

信号量分为两种:计数信号量和二值信号量。二值信号量可简单等价于可跨线程的互此锁,计数信号量的资源可以为任意值。

信号量的 PV 操作在内核中被称为 down 和 Up 操作。

信号量相关的 API 如下:

API作用

DECLARE_MUTEX(x)

声明并初始化一个名为 x 的信号量。

sema_init

初始化一个信号量。

init_MUTEX(sem)

初始化一个互斥信号量。

down_interruptible

尝试获取信号量,允许中断。

down

尝试获取信号量,不允许中断。

down_trylock

无阻塞地尝试获取信号量。

up

释放一个信号量,如果休眠队列不为空,唤醒其中一个任务。

读写信号量

顺序锁

seqlock 和读写锁类似,都是针对读多写少的场景,但是顺序锁的读锁不会阻塞写锁。

由于读锁不会阻塞写锁,因此在进入临界区的时候并不会加锁,为了解决读脏数据的问题,seqlock 引入了 seqcount:

typedef struct {
     struct seqcount seqcount;
     spinlock_t lock;
} seqlock_t;

写锁在进入临界区后首先使用自旋锁进行加锁(因为写锁是互斥的),然后将 seqcount 加一,在退出临界区后再将 seqcount 加一。这样若 seqcount 为奇数,则表明有写锁在临界区。

读锁在进入临界区的时候会检查 seqcount,当 seqcount 为奇数的时候会陷入自旋状态,当 seqcount 为偶数时才会进入临界区。

当读锁退出临界区时会检查序号是否一致,若不一致,则需要重新读取数据。

顺序锁相关的 API 如下:

API作用

DEFINE_SEQLOCK(x)

定义一个名为 x 的 seq 锁。

write_seqlock

获取写锁。

write_sequnlock

解锁写锁。

read_seqbegin

读临界区开始。

read_seqretry

尝试读取数据。

互斥锁

互斥锁和二值信号量功能类似。互斥锁的 API 如下:

API作用

DECLARE_MUTEX(x)

定义一个名为 x 的互斥锁。

mutex_init

初始化一个已有的互斥锁。

mutex_lock

尝试加锁,如果失败则睡眠。

mutex_unlock

为指定 mutex 解锁。

mutex_trylock

尝试加锁,失败返回 0,成功返回 1。

mutex_is_locked

检查是否一个锁已经加锁。

  • 内核中的互斥锁不是递归的。

  • 加锁者和解锁者必须是同一个人。否则结果未定义。

  • 持有 mutex 时进程不能退出。

内存顺序

Last moify: 2022-12-04 15:11:33
Build time:2025-07-18 09:41:42
Powered By asphinx