
1) 【一句话结论】:在嵌入式Linux系统中,使用互斥锁保护共享资源时,需通过破坏死锁的四个必要条件(互斥、持有并等待、不剥夺、循环等待)来避免,核心策略包括统一资源加锁顺序、及时释放锁、设置合理超时,同时需权衡互斥锁与自旋锁的使用场景,并理解死锁检测方法。
2) 【原理/概念讲解】:互斥锁(mutex)是Linux中用于保护共享资源的同步原语,确保同一时间仅一个线程/进程访问资源,防止数据竞争。死锁是系统状态,导致进程永久阻塞。死锁的四个必要条件:
3) 【对比与适用场景】:避免死锁的策略及锁类型选择对比:
| 策略/机制 | 定义 | 特性 | 使用场景 | 注意点 |
|---|---|---|---|---|
| 按资源类型排序加锁 | 按资源类型(如设备ID、内存区域)顺序加锁 | 破坏循环等待条件 | 多线程访问不同资源 | 需统一加锁顺序,避免循环 |
| 及时释放锁 | 操作完成后立即释放锁 | 破坏持有并等待条件 | 短时间访问共享资源 | 避免锁持有时间过长 |
| 设置超时(pthread_mutex_timedlock) | 加锁时设置超时时间(如N秒) | 破坏不剥夺条件 | 需要避免死锁的实时系统 | 超时后释放锁,防止永久阻塞 |
| 互斥锁(pthread_mutex_lock) | 允许线程阻塞,等待锁释放 | 锁持有时间较长 | 需要保护共享资源,允许阻塞 | 适用于锁持有时间长的场景 |
| 自旋锁(pthread_spin_lock) | 线程不阻塞,循环检测锁是否释放 | 锁持有时间短 | 需要快速获取锁,避免阻塞 | 适用于锁持有时间短(如CPU周期内) |
4) 【示例】:伪代码(两个线程访问共享变量,加锁顺序不同导致死锁,及超时设置):
// 共享资源
int shared_data = 0;
pthread_mutex_t lock_A, lock_B;
void* thread1(void* arg) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 5; // 超时5秒
int ret = pthread_mutex_timedlock(&lock_A, &ts);
if (ret != 0) {
pthread_mutex_unlock(&lock_A);
return NULL;
}
pthread_mutex_lock(&lock_B); // 获取B锁
// 操作
pthread_mutex_unlock(&lock_B);
pthread_mutex_unlock(&lock_A);
return NULL;
}
void* thread2(void* arg) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 5;
int ret = pthread_mutex_timedlock(&lock_B, &ts);
if (ret != 0) {
pthread_mutex_unlock(&lock_B);
return NULL;
}
pthread_mutex_lock(&lock_A);
// 操作
pthread_mutex_unlock(&lock_A);
pthread_mutex_unlock(&lock_B);
return NULL;
}
若线程1先执行,获取lock_A,线程2先执行,获取lock_B,此时线程1等待lock_B,线程2等待lock_A,形成循环等待(死锁)。避免方法:统一加锁顺序(如都先加lock_A再lock_B),或设置超时(超时后释放锁,避免永久阻塞)。
5) 【面试口播版答案】:在嵌入式Linux中,使用互斥锁保护共享资源时,核心是避免死锁。死锁由互斥、持有并等待、不剥夺、循环等待四个条件导致。避免策略包括:统一资源加锁顺序(破坏循环等待,比如所有线程都先加A锁再B锁),及时释放锁(破坏持有并等待,操作完成后立即释放),设置超时(破坏不剥夺,超时后释放锁,防止永久阻塞)。举个例子,两个线程访问共享变量,若加锁顺序不一致(一个先加A后B,另一个先加B后A),会导致循环等待。正确做法是按资源类型排序加锁,确保线程按顺序获取资源,释放时也按相反顺序,这样就不会出现死锁。同时,要理解互斥锁(允许阻塞)和自旋锁的权衡,比如锁持有时间短用自旋锁,长用互斥锁。总结来说,互斥锁的使用关键在于遵循加锁顺序、及时释放,并设置超时,破坏死锁条件。
6) 【追问清单】:
7) 【常见坑/雷区】: