关于内核状态下的锁和用户空间的锁的介绍可以参考以下连接:
https://blog.csdn.net/lilichang11106/article/details/84069357
主要内容可以描述如下:
内核锁 | 用户锁 |
---|---|
spinlock_t | pthread_ spinlock_t |
mutex_t | pthread_mutex_t、pthread_rwlock_t |
struct semaphore | sem_t |
rwlock_t | pthread_ spinlock_t |
pthread_cond_t |
表格中每一行中的锁功能类似,具体定义,数据结构,初始化方法,开闭方法如上文中连接所示。
本文将给出实际的代码来实例如何使用锁保护资源。
内核锁的使用在内核驱动等模块,进程调度处理进程列表,中断下半部分任务列表等地方随处可见,这里就不做实例了。
首先我们来看看在linux用户空间使用spin_lock:
#include<string>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#define TEST_USER_SPIN_LOCK
#ifdef TEST_USER_SPIN_LOCK
#include<pthread.h>
pthread_spinlock_t lock = 0;
int count = 0;
void* work(void* param)
{
for (int i = 0; i < 3; i++)
{
printf("pt before lock.\n");
pthread_spin_lock(&lock);
++count;
//usleep(0);
pthread_spin_unlock(&lock);
printf("pt after lock.\n");
}
return NULL;
}
#endif
//#include "dervied.h"
int version=1;
int main()
{
#ifdef TEST_USER_SPIN_LOCK
pthread_t pt = 0;
pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE);
pthread_create(&pt, NULL, work, NULL);
for (int i = 0; i < 3; i++)
{
printf("main before lock.\n");
pthread_spin_lock(&lock);
++count;
usleep(10);
pthread_spin_unlock(&lock);
printf("main after lock.\n");
}
pthread_join(pt, NULL);
pthread_spin_destroy(&lock);
#endif
return 0;
}
以上代码编译运行结果如下:
main before lock.
pt before lock.
main after lock.
main before lock.
pt after lock.
pt before lock.
main after lock.
main before lock.
pt after lock.
pt before lock.
main after lock.
pt after lock.
可见,spin_lock已经起了作用。
上面的代码中,我把pt trhread中的usleep(0);注释掉了,感兴趣朋友可以打开试一下。
thread_spinlock_t的特点是高效。但是如果一个线程在获得spinlock的时候陷入操作系统内核(比如时间片超时、缺页异常)会怎么样呢?另外一个线程在获取spinlock的时候会一直占用cpu。会有一个瞬间的cpu占用率高峰?这个高峰的持续时间取决于另外一个线程何时从内核返回。当然,如果临界区很小的话,这个冲突的几率也是很小的。但是使用pthread_mutex_t就不会有这个问题,代价是线程上下文切换的开销。
以下代码使用usleep(0)来模拟时间片超时的情况,证实了这个猜想是符合实际的。
另外,spinlock的还有一个实现上的特点是会锁总线,降低系统吞吐量。对于内核来说,线程数就是核心/cpu数量,所以影响不大?但是对于应用程序来说,如果线程很多(>>cpu数量)而且冲突严重,会非常影响性能?
有时间再给大家展示别的锁的使用。