linux中各种锁机制的使用与区别详解

前言:

相信需要了解这方面的知识的小伙伴,已经基本对进程间通信和线程间通信有了一定了解。例如,进程间通信的机制之一:共享内存(在这里不做详解):多个进程可同时访问同一块内存。如果不对访问这块内存的临界区进行互斥或者同步,那么进程的运行很可能出现一些不可预知的错误和结果。

接下来我们了解三种常见的Linux下的互斥操作—>锁。

1.互斥锁(mutex)

特点:对于读者和写者来说。只要有一方获取了锁,另一方则不能继续获取,进而执行临界区代码。

创建锁:

有两种方法创建互斥锁,静态方式和动态方式。POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER 来静态初始化互斥锁,

方法如下:

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER; 

在LinuxThreads实现中,pthread_mutex_t是一个结构,而PTHREAD_MUTEX_INITIALIZER则是一个结构常量。

动态方式是采用pthread_mutex_init()函数来初始化互斥锁,API定义如下:

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t*mutexattr) 

其中mutexattr用于指定互斥锁属性(见下),如果为NULL则使用缺省属性。 pthread_mutex_destroy ()用于注销一个互斥锁,API定义如下:

int pthread_mutex_destroy(pthread_mutex_t *mutex) 

锁操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个,不论哪种类型的锁,都不可能被两个不同的线程同时得到, 而必须等待解锁。对于普通锁和适应锁类型,解锁者可以是同进程内任何线程; 而检错锁则必须由加锁者解锁才有效,否则返回EPERM;对于嵌套锁,文档和实现要求必须由 加锁者解锁,但实验结果表明并没有这种限制,这个不同目前还没有得到解释。在同一进程中 的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。

int pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_unlock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t *mutex)

pthread_mutex_trylock() 语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回 EBUSY而不是挂起等待。
例如:单例模式下,线程安全的加锁:

class SingleTon
{
public:
static SingleTon* getInstance()
{
pthread_mutex_lock(&mutex);
if(mpSingle == NULL)
{
mpSingleTon = new SingleTon();
}
pthread_mutex_unlock(&mutex);
return mpSingleTon;
}
private:
SingleTon(){};
~SingleTon(){pthread_mutex_desttroy(&mutex,NULL);}
static pthread_mutex_t mutex;
static SingleTon * mpSingleTon;
}
pthread_mutex_t SingleTon::mutex = PTHREAD_MUTEX_INITIALIZER;
SingleTon * SingleTon::mpSingleTon = NULL;

优点:

由一块能够被多个进程共享的内存空间(一个对齐后的整型变量)组成;这个整型变量的值能够通过汇编语言调用CPU提供的原子操作指令来增加或减少,并且一个进程可以等待直到那个值变成正数。 的操作几乎全部在应用程序空间完成;只有当操作结果不 一致从而需要仲裁时,才需要进入操作系统内核空间执行。这种机制允许使用的锁定原语有非常高的执行效率:由于绝大多数 的操作并不需要在多个进程之间进行仲裁,所以绝大多数操作都可以在应用程序空间执行,而不需要使用(相对高代价的)内核系统调
用。

2.读写锁

特点:读写锁适合于对数据结构的读次数比写次数多得多的情况.因为,读模式锁定时可以共享,以写 模式锁住时意味着独占,所以读写锁又叫共享-独占锁.

初始化和销毁:

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const
pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); 

成功则返回0,出错则返回错误编号. 同互斥量以上,在释放读写锁占用的内存之前,需要先通过 pthread_rwlock_destroy对读写锁进行清理工作, 释放由init分配的资源.

读和写:

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); 

成功则返回0,出错则返回错误编号.这3个函数分别实现获取读锁,获取写锁和释放锁的操作.获 取锁的两个函数是阻塞操作,同样,非阻塞的函数为:

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); 

成功则返回0,出错则返回错误编号.非阻塞的获取锁操作,如果可以获取则返回0,否则返回 错误的EBUSY.

3.自旋锁

特点:轮询忙等待。

在单核cpu下不起作用:被自旋锁保护的临界区代码执行时不能进行挂起状态。会造成死锁
自旋锁的初衷就是:在短期间内进行轻量级的锁定。一个被争用的自旋锁使得请求它的线程在等待锁重新可用的期间进行自旋(特别浪费处理器时间),所以自旋锁不应该被持有时间过长。如果需要长时间锁定的话, 最好使用信号量。

API:

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • Linux加载vmlinux调试

    使用gdb加载内核符号表 arm-eabi-gdb out/target/product/msm8625/obj/KERNEL_OBJ/vmlinux 在内核的.config里面要打开 DEBUG_INFO和DEBUG_VM 定位故障代码 (gdb) l * qrd7627a_add_io_devices+0x100 0xc07cd05c is in qrd7627a_add_io_devices (/home/yejialong/GH700C/kernel/arch/arm/mach-msm/

  • Linux shell环境下Zabbix Api的使用

    在linux shell环境下直接调用就可以,根据官网所述:在访问Zabbix中的任何数据之前,你需要登录并获取身份验证令牌.这可以使用该 user.login 方法完成. [root@localhost ~]# curl -i -X POST -H 'Content-Type: application/json' -d '{"jsonrpc": "2.0","method":"user.login","params

  • Linux内核宏container_of的深度剖析

    1.前面说的 我在好几年前读linux 驱动代码的时候看到这个宏,百度了好久,知道怎么用了,但是对实现过程和原理还是一知半解. container_of宏 在linux内核代码里面使用次数非常非常多,对于喜欢linux编程的同学来说,了解其实现方法,对以后看内核代码,写内核驱动的帮助都非常大,当然,我不是说了解这个就可以为所欲为了,内核博大精深,先宏观再微观去学习,不积跬步何以致千里,不要想着一口就能吃成一个胖子,我这篇文章主要剖析一下这个函数的实现原理,希望对大家学习过程中有所帮助. andr

  • pyqt远程批量执行Linux命令程序的方法

    写了个小程序: 功能 1.测试远程ssh连接是否成功, 2.批量执行远程ssh命令 效果如下: 代码如下: #-*- coding:utf-8 -*- import sys from PyQt4 import QtCore, QtGui, uic import locale import re import os from PyQt4.QtCore import * from PyQt4.QtGui import * import paramiko qtCreatorFile = "test.u

  • Linux下误删messages文件的找回方法

    如果有进程正在使用的文件,如果被误删了,可以找回.如果没有进程在使用,就无法找回被误删的文件了. 假如/var/log/messages文件被误删了: 1.查询正在使用该文件的进程. [root@www]# lsof |grep message rsyslogd 1717 root 1w REG 8,2 243321 654968 /var/log/messages 2.根据查询结果,是PID为1717的进程正在使用该文件.进入该进程/proc下的目录/proc/1717/fd: [root@w

  • linux shell 中数组的定义和for循环遍历的方法

    linux shell中的语法和普通编程语言 c/c++ java 的不太一样,平时用的不多,所以总是记不住,写脚本才会去查怎么用. 今天突然被问到数组怎么去遍历.平时写shell脚本也经常遍历数组,但是一下没答上来,被鄙视了. 所以平时学习还是好好总结吧,不能每次都问度娘谷爷.IT 知识体系较为庞大,细节的东西也太多,平时遇到问题应该的多总结记笔记. linux 中定义一个数据的语法为: variable=(arg1 arg2 arg3 ....) 中间用空格分开.数组的下标从0开始. 1 获

  • Linux shell数组与关联数组的用法实例

    1. 关联数组 使用 declare -A(declare 的用法请使用 help 进行查看,help declare) 进行声明关联数组变量: $ declare -A fruits_price $ fruits_price=([apple]='$100' [orange]='$150') 列出关联数组的索引(也就是 key): $ echo ${!fruits_price[*]} $ echo ${!fruits_price[@]} 2. 序列数组 seq 方法创建 基本用法: $ a_nu

  • Android之在linux终端执行shell脚本直接打印当前运行app的日志的实现方法

    1.问题 我们一般很多时候会需要在ubuntu终端上打印当前运行app的日志,我们一般常见的做法是 1).获取包名 打开当前运行的app,然后输入如下命令,然后在第一行TASK后面的就可以看到包名 adb shell dumpsys activity top 2).我们的终端安装了pidcat.py脚本,然后执行如下的命令就可以打印当前运行app的全日志, pidcat.py packageName 3).思考,为什么每次都需要这样重复的操作呢?一说到重复,我们应该立马想到是否可以用脚本解决重复

  • Linux中查看指定文件夹内各个子文件夹内的文件数量

    count脚本 #!/bin/sh numOfArgs=$# if [ $numOfArgs -ne 1 ]; then echo -e "Usage: \nbash $0 dirForCount" exit -1 fi # args ROOTDIR=$1 # core part find $ROOTDIR -maxdepth 1 -type d | sort | while read dir; do count=$(find "$dir" -type f | wc

  • Linux shell数组循环的实例详解

    shell数组循环 测试shell数组,循环的例子: arr=("a" "b" "c") echo "所有的内容如下:"${arr[@]} echo "数组的长度:"${#arr[*]} for var in ${arr[@]} do echo "打印的内容:"$var done 输出的内容如下: 以上就是Linux shell数组循环的实例详解,如有疑问请留言或者到本站社区交流讨论,感

随机推荐