Linux学习第18天:Linux并发与竞争: 没有规矩不成方圆

2023-11-02 22:10

本文主要是介绍Linux学习第18天:Linux并发与竞争: 没有规矩不成方圆,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Linux版本号4.1.15   芯片I.MX6ULL                                    大叔学Linux    品人间百味  思文短情长


        提到锁”,可能想到的更多的是限制。现实中,生活中锁也 存在于身边的方方面面。正所谓没有规矩不成方圆, 没有身边的这些锁,这些限制,社会将会变得无序、混乱。为了规范这些无序和混乱,就得根据实际情况制定规则制度甚至法律来进行束缚和限制。正如本篇笔记要讲解的内容一样,Linux内核采用一定的方式方法(函数)制定了这些所谓的规则,才能使程序变得更流畅。

        本篇笔记主要学习Linux处理并发与竞争的机制。主要内容包括原子操作、自旋锁、信号量和互斥体。

一、并发与竞争

1.简介

        Linux是多任务操作系统,存在多个任务同时访问同一内存区域,造成这些任务会相互覆盖这段内存中的数据,从而造成内存数据的混乱。多个线程同时操作临界区就会发生竞争(竞争)并发导致。

2.保护什么

        保护共享资源(数据)。

二、原子操作

1.简介

        原子操作就是指不能再进一步分割的操作,用于变量和位操作。

2.原子整形操作API函数

原子整形操作API函数
函数描述
ATOMIC_INIT(init i)定义原子变量的时候对其进行初始化。
int atomic_read(atomic_t *v)读取v的值,并返回。
void atomic_set(atomic_t *v,int i)向v写入i值。
void atomic_add(atomic_t *v)给v加上i值。
void atomic_sub(atomic_t *v)给v减去i值。
void atomic_inc(atomic_t *v)自增
void atomic_dec(atomic_t *v)自减
void atomic_inc_return(atomic_t *v)自增并返回v值
void atomic_dec_return(atomic_t *v)自减并返回v值
int atomic_sub_and_test(int i,atomic_t *v)从v减i,如果结果为0就返回真,否则返回为假
int atomic_dec_and_test(int i,atomic_t *v)从v减1,如果结果为0就返回真,否则返回为假
int atomic_add_and_test(int i,atomic_t *v)从v加i,如果结果为0就返回真,否则返回为假
int atomic_inc_and_test(int i,atomic_t *v)从v加1,如果结果为0就返回真,否则返回为假

3.原子位操作API函数

原子位操作API函数
函数描述
void set_bit(int nr,void *p)将p地址的第nr位置1
void clear_bit(int nr,void *p)将p地址的第nr位清零
void change_bit(int nr,void *p)将p地址的第nr位翻转
int test_bit(int nr,void *p)获取p地址的第nr位的值
int test_and_set(int nr,void *p)将p地址的nr位置1,并返回nr位原来的值
int test_and_clear(int nr,void *p)将p地址的nr位清零,并返回nr位原来的值
int  test_and_change(int nr,void *p)将p地址的第nr位翻转,并返回nr位原来的值

三、自旋锁

1.简介

        当一个线程要访问某个共享资源的时候首先要先获取相应的锁,锁只能被一个线程持有,只要此线程不释放拥有的锁,那么其他的线程就不能获取此锁。

        自旋---原地打转

        缺点:时间短。

        使用结构体spinlock_t表示自旋锁。

2.自旋锁API函数

自旋锁API函数
函数描述
DEFINE_SPINLOCK(spinlock_t lock)定义并初始化一个自选变量
int spin_lock_init(spinlock_t *lock)初始化自旋锁

void spin_lock(spinlock_t *lock)

获取指定的自旋锁,加锁
void spin_unlock(spinlock_t *lock)释放指定的自旋锁
int spin_trylock(spinlock_t *lock)尝试获取指定的自旋锁,如果没有获取就返回0
int spin_is_locked(spinlock_t *lock)

检查指定的自旋锁是否被获取,如果没有被获取就返回非0,否则返回0

        自旋锁适用于线程与线程之间,被自旋锁保护的临界区一定不能调用任何能引起睡眠和阻塞的API函数,否则会导致死锁的发生。

        自旋锁会自动禁止抢占。

        中断里面可以使用自旋锁,但在中断里面使用自旋锁的时候,在获取之前一定要先禁止本地中断。相应的API函数如下:

函数描述
void spin_lock_irqsave(spinlock_t *lock,unsigned long flags)保存中断状态,禁止本地中断,并获取自旋锁。
void spin_unlock_irqrestore(spinlock_t *lock,unsigned long flags)将中断状态恢复打以前状态,并且激活本地中断,释放自旋锁。

        下半部也会竞争共享资源,下半部使用自旋锁的API函数有:

函数       描述
void spin_lock_bh(spinlock_t *lock)关闭下半部,并获取自旋锁
void spin_unlock_bh(spinlock_t lock)打开下半部,并释放自旋锁

3.其他类型的锁

        实际应用中用的不多,多的是在Linux内核中使用。

1)、读写自旋锁

2)、顺序锁

4.使用注意事项

1)、持有时间不能太长

2)、自旋锁保护的临界区内部能调用任何可能导致线程休眠的API函数,否则可能会导致死锁。

3)、不能递归申请自旋锁

4)、多核SOC编写程序

四、信号量

1.简介

        信号量常用于对共享资源的访问。

        信号量可以使线程进入休眠状态。

        信号量的开销比自旋锁要大。

        信号量特点:

1)、适用于占用资源比较长的场所。

2)、不能用于中断中。

        通过信号量控制访问资源的线程数。

        不能用于互斥访问。

2.信号量API函数

        使用semaphore结构体表示信号量。相关的API函数如下:

函数描述
DEFINE_SEMAPHORE(name)定义一个信号量,并设置信号量的值为1
void sema_init(struct semaphore *sem,int val)初始化信号量sem,设置信号量的值为val.
void down(struct semaphore *sem)获取信号量,不能用在中断中使用
int down_trylock(struct semaphore *sem)尝试获取信号量,能获取就返回0.如果不能返回非0,并且不会进入休眠。
int down_interruptible(struct semaphore *sem)获取信号量,进入休眠以后是可以被信号打断的。
void up(struct semaphore *sem)释放信号量

五、互斥体

1.简介

        一次只有一个线程访问共享资源,不能递归申请。需要互斥访问的时候建议使用mutex.

        注意以下几点:

1)、不能在中断中使用。

2)、保护的临界区可以调用引起阻塞的API函数。

3)、必须由mutex的持有者释放mutex。mutex不能递归上锁和解锁。

2.互斥体API函数

        相关的API函数有:

函数描述
DEFINE_MUXTEX(name)定义并初始化一个mutex变量
void mutex_init(mutex *lock)初始化mutex
void mutex_lock(struct mutex *lock)获取mutex  上锁  获取不到就休眠
void mutex_unlock(struct mutex *lock)释放mutex  解锁
iint mutex_trylock(struct mutex *lock)尝试获取mutex 成功返回0 失败返回0
int mutex_is_locked(struct mutex *lock)判断mutex是否被获取 获取返回1 否则返回0
int mutex_lock_interruptible(struct mutex *lock)使用此函数获取信号量失败进入休眠以后可以被信号打断

六、总结

        本篇笔记主要学习了相关的概念及API函数,并没有相关的案例进行说明。案例将在下一篇笔记中给出。本篇笔记主要内容包括原子操作、自旋锁、信号量和互斥体。

这篇关于Linux学习第18天:Linux并发与竞争: 没有规矩不成方圆的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/333629

相关文章

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法

《ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法》本文介绍了Elasticsearch的基本概念,包括文档和字段、索引和映射,还详细描述了如何通过Docker... 目录1、ElasticSearch概念2、ElasticSearch、Kibana和IK分词器部署

Linux流媒体服务器部署流程

《Linux流媒体服务器部署流程》文章详细介绍了流媒体服务器的部署步骤,包括更新系统、安装依赖组件、编译安装Nginx和RTMP模块、配置Nginx和FFmpeg,以及测试流媒体服务器的搭建... 目录流媒体服务器部署部署安装1.更新系统2.安装依赖组件3.解压4.编译安装(添加RTMP和openssl模块

linux下多个硬盘划分到同一挂载点问题

《linux下多个硬盘划分到同一挂载点问题》在Linux系统中,将多个硬盘划分到同一挂载点需要通过逻辑卷管理(LVM)来实现,首先,需要将物理存储设备(如硬盘分区)创建为物理卷,然后,将这些物理卷组成... 目录linux下多个硬盘划分到同一挂载点需要明确的几个概念硬盘插上默认的是非lvm总结Linux下多

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

linux进程D状态的解决思路分享

《linux进程D状态的解决思路分享》在Linux系统中,进程在内核模式下等待I/O完成时会进入不间断睡眠状态(D状态),这种状态下,进程无法通过普通方式被杀死,本文通过实验模拟了这种状态,并分析了如... 目录1. 问题描述2. 问题分析3. 实验模拟3.1 使用losetup创建一个卷作为pv的磁盘3.

Linux环境变量&&进程地址空间详解

《Linux环境变量&&进程地址空间详解》本文介绍了Linux环境变量、命令行参数、进程地址空间以及Linux内核进程调度队列的相关知识,环境变量是系统运行环境的参数,命令行参数用于传递给程序的参数,... 目录一、初步认识环境变量1.1常见的环境变量1.2环境变量的基本概念二、命令行参数2.1通过命令编程

Linux之进程状态&&进程优先级详解

《Linux之进程状态&&进程优先级详解》文章介绍了操作系统中进程的状态,包括运行状态、阻塞状态和挂起状态,并详细解释了Linux下进程的具体状态及其管理,此外,文章还讨论了进程的优先级、查看和修改进... 目录一、操作系统的进程状态1.1运行状态1.2阻塞状态1.3挂起二、linux下具体的状态三、进程的

Linux编译器--gcc/g++使用方式

《Linux编译器--gcc/g++使用方式》文章主要介绍了C/C++程序的编译过程,包括预编译、编译、汇编和链接四个阶段,并详细解释了每个阶段的作用和具体操作,同时,还介绍了调试和发布版本的概念... 目录一、预编译指令1.1预处理功能1.2指令1.3问题扩展二、编译(生成汇编)三、汇编(生成二进制机器语

Rsnapshot怎么用? 基于Rsync的强大Linux备份工具使用指南

《Rsnapshot怎么用?基于Rsync的强大Linux备份工具使用指南》Rsnapshot不仅可以备份本地文件,还能通过SSH备份远程文件,接下来详细介绍如何安装、配置和使用Rsnaps... Rsnapshot 是一款开源的文件系统快照工具。它结合了 Rsync 和 SSH 的能力,可以帮助你在 li