• 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函数,并没有相关的案例进行说明。案例将在下一篇笔记中给出。本篇笔记主要内容包括原子操作、自旋锁、信号量和互斥体。

  • 相关阅读:
    机器学习代码问题总结
    3. Java 数据类型
    短视频简单无脑玩法,播放量10w+的藏头诗玩法,操作思路分享给你!
    精英荟聚,入海捉蛟 | 2022年全国水下机器人大赛线上赛圆满举办
    prometheus 监控oracle
    Docker Hub
    String.format()的详细用法
    基于微信小程序的校园外卖点餐系统
    Spring的@Configuration、@Bean、@Conponent 和 @ComponentScane
    ReentrantLock通过Condition实现锁对象的监视器功能
  • 原文地址:https://blog.csdn.net/jiage987450/article/details/132940376