面试 : Linux 上的锁
Linux 中程序使用的锁
不要和我说什么原子操作,加一把大锁就完了。什么缓存过期内存延时,光速不够快赖我?
出题篇
最近在跑各种厂商的C/C++后端面试,其中一个特别高频的考点就是程序中的死锁问题,主要的问题表现形式如下:
-
如果程序中出现了死锁,除了程序会卡住之外,在操作系统上还会有什么表现形式?
-
承接上一个题目,出现死锁之后各个系统资源的占用如何?
-
如何定位到是哪里出现了死锁?
-
使用top命令查看进程处于 Sleep 状态下, 一定会让出CPU吗
还好曾经在写代码的时候脑子抽了写过死锁,这几个问题勉强答出来了
解答篇
出现死锁后的状况,问题的核心在于使用什么锁。在大量使用睡眠等待锁的今天,死锁造成的性能损失几乎可以忽略不记了,不过自旋锁可就没这么好心的让出资源。
什么,你说你消费者线程被阻塞住,生产者还在发消息?
祝任务队列 / 消息队列 早日 OOM
出现死锁后的情况 mutex
首先先构造一个简单的含有死锁的两个线程的程序, 两个线程持有一个锁并互相等待对方的锁。
thread0 获取 mutex0 等待 mutex1
thread1 获取 mutex1 等待 mutex0
#include<mutex>
#include<thread>
#include<iostream>
void mutex_test(){
std::mutex test_mutex0;
std::mutex test_mutex1;
std::thread thread0(
[&test_mutex0, &test_mutex1](){
while(true){
test_mutex0.lock();
std::cout << "thread 0 get mutex 0" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
test_mutex1.lock();
std::cout << "thread 0 get mutex 1" << std::endl;
test_mutex1.unlock();
test_mutex0.unlock();
break;
}
}
);
std::thread thread1(
[&test_mutex0, &test_mutex1](){
while(true){
test_mutex1.lock();
std::cout << "thread 1 get mutex 1" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
test_mutex0.lock();
std::cout << "thread 1 get mutex 0" << std::endl;
test_mutex0.unlock();
test_mutex1.unlock();
break;
}
}
);
thread0.join();
thread1.join();
}
int main(){
mutex_test();
}
注意到该程序中使用的是 mutex 锁,该锁在阻塞的情况下会睡眠等待,因此此时该程序占用的系统资源几乎没有,虽然程序卡住了但操作系统不会卡住,也不会拖累操作系统的速度;
可以使用 ps 命令查看卡住进程的 pid ,然后使用 top 命令查看该 pid 的系统资源占用情况。
什么?ps 命令不知道该看哪个程序?程序是人工手动启动的这能不知道过滤条件吗?
- 程序卡住的截图

- 利用
ps -aux | grep lock命令查看 pid,因为使用了 grep 命令所以就没显示表头

才意识到 top 命令中就能看到这个程序的各个状态。
| pid | 进程状态 |
|---|---|
| 5601 | SL+(睡眠S 且为多线程L) |
能拿到 pid 就可以请出来 gdb 这尊大佛了,我项目中使用最多的也是 gdb

进了 gdb 那可就是连底裤都能看出来,info threads 查看各个线程的标号,然后 bt 查看函数调用栈即可;在这里可以看到当前线程运行到了 mutex_test() 函数的 lambda 函数中,再之后就需要手动的去排查代码中该 lambda 表达式出现了什么问题

出现死锁的情况 spin_lock
这里插播一条消息,Linux中自旋锁的设计不是单纯的一个原子的bool就能实现的,需要考虑到很多问题,比如:
-
多核 CPU 的系统中,变量所在的内存物理位置距离不同的CPU距离不同,会导致不同的线程抢占的公平性不同
-
新来的线程可能更容易抢占到锁,牢线程自旋到饿死
这里不展开自旋锁的设计,可以参考该文章:
这个自旋锁的程序可就不这么友好了;

虽然我显示的是 SL+ ,我在 sleep ,但是忙等待,自旋锁让出CPU很短的时间后,回来抢占CPU检查状态
死锁代码和上面类似,只不过是使用的自旋锁
#include <pthread.h>
#include <iostream>
#include <thread>
void spin_test() {
pthread_spinlock_t lock0;
pthread_spinlock_t lock1;
pthread_spinlock_t* ptr_lock0 = &lock0;
pthread_spinlock_t* ptr_lock1 = &lock1;
pthread_spin_init(&lock0, 0);
pthread_spin_init(&lock1, 0);
std::thread thread0([ptr_lock0, ptr_lock1]() {
pthread_spin_lock(ptr_lock0);
std::cout << "thread 0 get mutex 0" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
pthread_spin_lock(ptr_lock1);
std::cout << "thread 0 get mutex 1" << std::endl;
pthread_spin_unlock(ptr_lock1);
pthread_spin_unlock(ptr_lock0);
});
std::thread thread1([ptr_lock0, ptr_lock1]() {
pthread_spin_lock(ptr_lock1);
std::cout << "thread 1 get mutex 1" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
pthread_spin_lock(ptr_lock0);
std::cout << "thread 1 get mutex 0" << std::endl;
pthread_spin_unlock(ptr_lock0);
pthread_spin_unlock(ptr_lock1);
});
thread0.join();
thread1.join();
}
int main(){
spin_test();
}

浙公网安备 33010602011771号