Linux锁API

flock、lockf和fcntl

 

Linux的文件锁主要有两种:flocklockflockf只是fcntl系统调用的一个封装。

 

lockffcntl实现了更细粒度文件锁,即记录锁,可以对文件的部分字节上锁,而flock只能对整个文件加锁。

 

应该避免在多线程场景下使用flock对文件加锁,而lockf/fcntl则没有这个问题。

 

 

 

建议锁和强制锁

建议锁:如果某一个进程对一个文件持有一把锁之后,其他进程仍然可以直接对文件进行操作的。只是一种编程上的约定。

 

强制锁:试图实现一套内核级的锁操作。当有进程对某个文件上锁之后,其他进程即使不在操作文件之前检查锁,也会在openreadwrite等文件操作时发生错误。

 

flocklockf都是建议锁。

 

fcntl系统调用可以支持强制锁。

 

 

 

线程锁

多线程共享地址空间在方便我们编程的同时,也带来了一些麻烦,其中就包括共享资源的争抢问题.

 

互斥锁(pthread_mutex_t)。当一个线程拿到锁的时候,其他线程运行到这里的时候会被挂起阻塞,等待锁释放后再唤醒运行。也可用作进程间同步,不过需要利用共享内存。

 

#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

 

 

自旋锁(pthread_spinlock_t)。没抢到锁的线程不会挂起,而是不断的检查锁是否被释放,其原理是内部保存了一个原子变量,加锁和解锁就是对原子变量的增减,被阻塞的线程不断检查这个变量的值,决定是否继续忙等。由于用户态程序无法控制线程调度,所以可能出现一个线程刚刚拿到了锁就被切换了出去,造成另一个线程在整个时间片内自旋,白白浪费cpu资源。

 

读写锁(pthread_rwlock_t)读锁可以被多个线程持有,而同时只能有一个写线程,且不能同时存在读写操作读多写少的场合用读写锁可以提高并发性,但读写锁的实现本身就要维护一个原子的读者数量,如果锁的粒度比较小,锁本身的消耗就不可忽略了。

 

读写锁加解锁的代价比普通锁大,但是依然有其使用场景:
少量操作需要写锁,多个线程需要读锁,并且持有读锁的时间比较长(显著高于加解锁的耗时)那么用读写锁能够明显提高并发度。

 

条件变量(pthread_cond_t)。与pthread_mutex_t配合,让线程睡眠,不占用CPU,直到事件发生(pthread_cond_broadcast、pthread_cond_signal)为止。也可用作进程间同步,不过需要利用共享内存。

在使用pthread_cond_wait函数之前,我们要先用一个互斥锁锁住,然后当我们调用pthread_cond_wait函数进入睡眠。该函数原子的执行两个动作:
1,给互斥锁解锁。(这就要求在调用这个函数之前要上锁)
2,把调用线程投入睡眠。
一旦这个函数被唤醒后,那么在此函数返回前重新给互斥锁上锁。这也就决定了在接下来的程序里必须有解锁的步骤。

 

#include <pthread.h>  
#include <stdio.h>  
#include <stdlib.h>  
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;/*初始化互斥锁*/  
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;/*初始化条件变量*/  
void *thread1(void *);  
void *thread2(void *);  
int i=1;  
int main(void)  {  
    pthread_t t_a;  
    pthread_t t_b;  
    pthread_create(&t_a,NULL,thread1,(void *)NULL);/*创建进程t_a*/  
    pthread_create(&t_b,NULL,thread2,(void *)NULL); /*创建进程t_b*/  
    pthread_join(t_a, NULL);/*等待进程t_a结束*/  
    pthread_join(t_b, NULL);/*等待进程t_b结束*/  
    pthread_mutex_destroy(&mutex);  
    pthread_cond_destroy(&cond);  
    exit(0);  
}  
void *thread1(void *junk)  {  
    for(i=1;i<=6;i++){  
        pthread_mutex_lock(&mutex);/*锁住互斥量*/  
        if(i%3==0){  
            printf("thread1:signal 1  %d/n", __LINE__);  
            pthread_cond_signal(&cond);/*条件改变,发送信号,通知t_b进程*/  
            printf("thread1:signal 2  %d/n", __LINE__);  
            sleep(1);  
        }  
        pthread_mutex_unlock(&mutex);/*解锁互斥量*/  
        sleep(1);  
    }  
}  
void *thread2(void *junk)  {  
    while(i<6){  
        pthread_mutex_lock(&mutex);   
        if(i%3!=0){  
            printf("thread2: wait 1  %d/n", __LINE__);  
            pthread_cond_wait(&cond,&mutex);/*解锁mutex,并等待cond改变*/  
            printf("thread2: wait 2  %d/n", __LINE__);  
        }  
        pthread_mutex_unlock(&mutex);  
        sleep(1);  
    }  
}  

 

 

如果信号处理函数打断了pthread_cond_wait(),该函数要么自动重新自行(linux是这样实现的),或者返回0(这时应用要检查返回值,判断是否为假唤醒)

 

 

 

信号量

 

信号量集:信号量机制解决了单个资源的互斥访问,信号量集解决同时需要多个资源时的互斥访问。

 

 

脚本

-n参数可以让flock命令以非阻塞方式探测一个文件是否已经被加锁。

脚本退出的时候锁会被释放,所以这里可以不用显式的使用flock解锁。

*/1 * * * * /usr/bin/flock -xn/tmp/script.lock -c '/home/bash/script.sh'

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值