锁就是你的函数在某种条件下才可以继续执行
条件变量,条件成立唤醒执行
信号量,
自旋锁,
互斥锁,
读写锁
对于锁的理解:
设置一个标志表示可访问的状态,相当于有多少把钥匙,同时只能一个人拿到这个钥匙,
或者多个人多把钥匙,拿不到就等着
有以下问题:
等着是什么呢?
睡眠或返回或死等
保证得到锁,空出锁的原子性操作?
寄存器,唯一性操作malloc
有一把锁空出,多个等待,给谁呢?
队列或随机
更高级别的抢占?
抢占失效或可以抢占
1,信号量
struct semaphore {
spinlock_t
lock;//原子操作,判断
unsigned int
count;//钥匙的个数
struct list_head wait_list;//等待队列
};
进程获取,判断是否有钥匙,有则减1后执行
无则进入last睡眠队列,从cpu运行上摘除
或者直接返回,不会进入睡眠
进程释放,如果有等待则唤醒队列中进程,删除first
无则加1后退出
2,互斥锁
上面信号量的count为1,则变为互斥锁
互斥锁只能用于线程间通信
性能提升,fast path和slow path,
主要是基于这样一个事实:在绝大多数情况下,试图获得互斥体的代码总是可以成功获得
3,自旋锁
等待既没有睡眠也没有返回,而是死等
持有期间是抢占失效的
4,读写锁
读可以共享,写即是独占,其实是有两种锁的,并且互相联系
信号量+互斥
5,API使用
/* PTHREAD */
/* 读模式下加锁 */
int pthread_rwlock_rdlock (pthread_rwlock_t *rwlock);
/* 非阻塞的读模式下加锁 */
int pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock);
/* 限时等待的读模式加锁 */
int pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock,const struct timespec *abstime);
/* 写模式下加锁 */
int pthread_rwlock_wrlock (pthread_rwlock_t *rwlock);
/* 非阻塞的写模式下加锁 */
int pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock);
/* 限时等待的写模式加锁 */
int pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock,const struct timespec *abstime);
/* 解锁 */
int pthread_rwlock_unlock (pthread_rwlock_t *rwlock);
返回值:成功返回0,否则返回错误代码
/* 初始化读写锁属性对象 */
int pthread_rwlockattr_init (pthread_rwlockattr_t *attr);
/* 销毁读写锁属性对象 */
int pthread_rwlockattr_destroy (pthread_rwlockattr_t *attr);
/* 获取读写锁属性对象在进程间共享与否的标识 */
int pthread_rwlockattr_getpshared (__const pthread_rwlockattr_t *attr,int *pshared);
/* 设置读写锁属性对象,标识在进程间共享与否 */
int pthread_rwlockattr_setpshared (pthread_rwlockattr_t *attr, int pshared); PTHREAD_PROCESS_SHARED
/* 读者优先或者写者优先 */
int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref);
int pthread_rwlockattr_getkind_np(const pthread_rwlockattr_t *attr, int *pref);
/*测试代码:读进程一直在循环读取读锁,写进程后启动改数据,空指针判断省略*/
/* read.c */
#include
#include
#include
#include
#include
#include
struct shared {
pthread_rwlock_t rwlock;
char data[10];
};
int main()
{
int shmid;
shmid = shmget((key_t)1234,sizeof(struct shared),0666| IPC_CREAT);
struct shared *p_map;
p_map = shmat(shmid,NULL,0);
strncpy(p_map->data,"read",10);
pthread_rwlock_t *rw = &(p_map->rwlock);
pthread_rwlockattr_t attr;
pthread_rwlockattr_init(&attr);
pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
pthread_rwlockattr_setpshared(&attr,PTHREAD_PROCESS_SHARED);
pthread_rwlock_init(rw, &attr);
int i;
while(1)
{
pthread_rwlock_rdlock(rw);
if(strcmp(p_map->data,"wirte") == 0)
printf("read data is :%s\n",p_map->data);
pthread_rwlock_unlock(rw);
}
return 0;
}
/* write.c */
#include
#include
#include
#include
#include
#include
#include
struct shared {
pthread_rwlock_t rwlock;
char data[10];
};
int main()
{
int shmid;
shmid = shmget((key_t)1234,sizeof(struct shared),0666| IPC_CREAT);
struct shared *p_map;
pthread_rwlock_t *wr;
p_map = shmat(shmid,NULL,0);
wr = &(p_map->rwlock);
int i;
for(i=0;i<1000;i++)
{
sleep(2);
pthread_rwlock_wrlock(wr);
strncpy(p_map->data,"wirte",10);
printf("%d wirte data is :%s\n",i,p_map->data);
pthread_rwlock_unlock(wr);
}
return 0;
}
/* FCNTL */
/*seqlock(顺序锁)*/
/* 优化了锁的等待,提高并行,如果有修改则直接重复读 */
用于能够区分读与写的场合,并且是读操作很多、写操作很少,写操作的优先权大于读操作。
seqlock的实现思路是,用一个递增的整型数表示sequence。
写操作进入临界区时,sequence++;退出临界区时,sequence再++。
写操作还需要获得一个锁(比如mutex),这个锁仅用于写写互斥,以保证同一时间最多只有一个正在进行的写操作。
当sequence为奇数时,表示有写操作正在进行,这时读操作要进入临界区需要等待,直到sequence变为偶数。
读操作进入临界区时,需要记录下当前sequence的值,等它退出临界区的时候用记录的sequence与当前sequence做比较,
不相等则表示在读操作进入临界区期间发生了写操作,这时候读操作读到的东西是无效的,需要返回重试。
/* RCU(Read-Copy Update)*/
RCU就是指读拷贝修改,它是基于其原理命名的。
对于被RCU保护的共享数据结构,读操作不需要获得任何锁就可以访问,但写操作在访问它时首先拷贝一个副本,
然后对副本进行修改,最后在适当的时机把指向原来数据的指针重新指向新的被修改的数据。
这个时机就是所有引用该数据的CPU都退出对共享数据的操作。