一 原子操作

指令以原子的方式执行——执行过程不被打断。

1 原子整数操作

原子操作函数接收的操作数类型——atomic_t

//定义
atomic_t v;
//初始化
atomic_t u = ATOMIC_INIT(0); //操作
atomic_set(&v,4); // v = 4
atomic_add(2,&v); // v = v + 2 = 6
atomic_inc(&v); // v = v + 1 = 7
//实现原子操作函数实现
static inline void atomic_add(int i, atomic_t *v)
{
unsigned long tmp;
int result;
__asm__ __volatile__("@ atomic_add\n"
    "1: ldrex %0, [%3]\n"
    " add %0, %0, %4\n"
    " strex %1, %0, [%3]\n"
    " teq %1, #0\n"
    " bne 1b"
  : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
  : "r" (&v->counter), "Ir" (i)
  : "cc");
}

 

2 原子位操作

//定义
unsigned long word = 0; //操作
set_bit(0,&word); //第0位被设置1
set_bit(0,&word); //第1位被设置1
clear_bit(1,&word); //第1位被清空0 //原子位操作函数实现
static inline void ____atomic_set_bit(unsigned int bit, volatile unsigned long *p)
{
unsigned long flags;
unsigned long mask = 1UL << (bit & 31); p += bit >> 5;
raw_local_irq_save(flags);
*p |= mask;
raw_local_irq_restore(flags);
}

二 自旋锁

  原子位和原子整数仅能对简单的整形变量进行原子操作,对于复杂的数据复杂的操作并不适用。

需要更复杂的同步方法实现保护机制——锁。

  自旋锁:同一时刻只能被一个可执行线程持有,获得自旋锁时,如果已被别的线程持有则该线程进行循环等待锁重新可用,

然后继续向下执行。

过程如下:

      

    

 

使用锁得基本形式如下:

spinlock_t lock;
//获取锁
spin_lock(&lock);
//临界区
…… //释放锁
spin_unlock(&lock);

使用自旋锁防止死锁:

自旋锁不可递归,自旋处于等待中造成死锁;

中断处理程序中,获取自旋锁前要先禁止本地中断,中断会打断正持有自旋锁的任务,中断处理程序有可能争用已经被持有的自旋锁,造成死锁。

读写自旋锁:将锁的用途直接分为读取和写入。

三 信号量

  信号量:睡眠锁。如果有一个任务试图获取信号量时,

信号量未被占用:该任务获得成功信号量;

信号量已被占用:信号量将任任务推进等待队列,让其睡眠,处理器继续工作;当信号量被释放后,

        唤醒信号量队列中的任务,并获取该信号量。

可有读写信号量。

      

声明信号量:

信号量数据结构:
/* Please don't access any members of this structure directly */
struct semaphore { raw_spinlock_t lock; unsigned int count; struct list_head wait_list; };

静态声明信号量:

//声明可用数量为1信号量
#define DEFINE_SEMAPHORE(name) \
struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1) //声明可用数量为n的信号量
#define __SEMAPHORE_INITIALIZER(name, n) \
{ \
.lock = __RAW_SPIN_LOCK_UNLOCKED((name).lock), \ .count = n, \ .wait_list = LIST_HEAD_INIT((name).wait_list), \
}

动态声明信号量:

static inline void sema_init(struct semaphore *sem, int val)
{
static struct lock_class_key __key;
*sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0); }

使用信号量:

  //初始化定义信号量
struct semaphore driver_lock;
sema_init(&driver_lock, 1); //获取信号量
if (down_interruptible(&driver_lock))
return -EINTR;
//执行临界区
…… //释放信号量
up(&driver_lock);

自旋锁与信号量对比:

需求                             使用锁

低开销加锁         :               优先使用自旋锁

短期锁定            :               优先使用自旋锁

长期锁定            :               优先使用信号量

中断上下文加锁   :               使用自旋锁

持有锁需要睡眠   :               使用信号量

四 完成变量

  完成变量:如果在内核中一个任务需要发出信号通知另一个任务发生了某个特定事件,

       使用完成变量去唤醒在等待的任务,使两个任务得以同步。信号量的简易方法。

数据结构:

struct completion {
unsigned int done;
wait_queue_head_t wait;
};

完成变量声明:

动态:#define COMPLETION_INITIALIZER(work) \
{ 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait) } 静态: static inline void init_completion(struct completion *x)
{
x->done = 0;
init_waitqueue_head(&x->wait);
}

完成变量使用:

//等待制定的完成变量
void __sched wait_for_completion(struct completion *x) //发信号唤醒等待的任务
void complete(struct completion *x)

还有实现同步机制诸如:禁止抢占,Seq锁(读写共享数据),顺序和屏障……

(笔记)Linux内核学习(七)之内核同步机制和实现方式的更多相关文章

  1. (笔记)Linux内核学习(一)之内核介绍

    内核与操作系统: 内核是操作系统的核心部分,包含了系统运行的核心过程,决定系统的性能,操作系统启动内核被装入到RAM中: 操作系统与底层硬件设备交互和为运行应用程序提供执行环境. Linux内核与微内 ...

  2. (笔记)Linux内核学习(九)之内核内存管理方式

    一 页 内核把物理页作为内存管理的基本单位:内存管理单元(MMU)把虚拟地址转换为物理 地址,通常以页为单位进行处理.MMU以页大小为单位来管理系统中的也表. 32位系统:页大小4KB 64位系统:页 ...

  3. ucore操作系统学习(七) ucore lab7同步互斥

    1. ucore lab7介绍 ucore在前面的实验中实现了进程/线程机制,并在lab6中实现了抢占式的线程调度机制.基于中断的抢占式线程调度机制使得线程在执行的过程中随时可能被操作系统打断,被阻塞 ...

  4. Linux驱动技术(七) _内核定时器与延迟工作

    内核定时器 软件上的定时器最终要依靠硬件时钟来实现,简单的说,内核会在时钟中断发生后检测各个注册到内核的定时器是否到期,如果到期,就回调相应的注册函数,将其作为中断底半部来执行.实际上,时钟中断处理程 ...

  5. Linux系统学习 七、网络基础—常用网络命令

    1.ifconfig                 #查看网络(设置IP临时生效) 2.hostname [主机名]            #查看或设置主机名       默认的是localhost ...

  6. [笔记]linux命令学习

    scp /root/Downloads/cymothoa-1-beta.tar.gz root@192.168.1.66:/root/ rc.local exit 0前加入: sh /root/abc ...

  7. 浅谈linux读写同步机制RCU

    RCU是linux系统的一种读写同步机制,说到底他也是一种内核同步的手段,本问就RCU概率和实现机制,给出笔者的理解. [RCU概率] 我们先看下内核文档中对RCU的定义: RCU is a sync ...

  8. windows核心编程 - 线程同步机制

    线程同步机制 常用的线程同步机制有很多种,主要分为用户模式和内核对象两类:其中 用户模式包括:原子操作.关键代码段 内核对象包括:时间内核对象(Event).等待定时器内核对象(WaitableTim ...

  9. Linux内核分析第七周学习笔记——Linux内核如何装载和启动一个可执行程序

    Linux内核分析第七周学习笔记--Linux内核如何装载和启动一个可执行程序 zl + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study. ...

随机推荐

  1. SQL关于日期的查询

    SQL查询某天的记录: datediff(day,[Datetime],'2012-08-03')=0 把Datetime换为你的相应字段: SQL查询今天的记录: datediff(day,[Dat ...

  2. 谷歌(Chrome)浏览器调试JavaScript小技巧

    谷歌浏览器不仅仅可以用来上网,对于开发人员来说,它更像是一款强大的开发辅助工具. 工欲善其事必先利其器,接下来笔者给大家分享一些Chrome的使用方法. 假如读者了解如何在Chrome中添加JavaS ...

  3. js 函数提升和变量提升

    总结: 函数提升比变量提升优先级高! 词法分析 词法分析方法: js运行前有一个类似编译的过程即词法分析,词法分析主要有三个步骤: 分析参数 再分析变量的声明 分析函数说明 具体步骤如下: 函数在运行 ...

  4. Asset Catalog Help (二)---Creating an Asset Catalog

    Creating an Asset Catalog Create an asset catalog to simplify management of your app’s images. 创建一个a ...

  5. Leetcode 328 Odd Even Linked List 链表

    Given 1->2->3->4->5->NULL, return 1->3->5->2->4->NULL. 就是将序号为单数的放在前面,而 ...

  6. untiy数据包的输出、加载和卸载

    1:untiy数据包的输出: BuildPipeline.BuildAssetBundle将任意类型的资源打包成AssetsBundle文件. BuildPipeline.BuildAssetBund ...

  7. DataWindow.Net 2.5 配置

    在用过的所有的开发工具中,感觉最简单好用的就数PB了,他的DataWindow最方便,拖拖拽拽就把报表做好了,可惜现在很少人有用了.现在C/S系统一般用C#来做,但是做报表的时候总感觉没有DataWi ...

  8. curl常用操作

    1.cURL介绍 cURL 是一个利用URL语法规定来传输文件和数据的工具,支持很多协议,如HTTP.FTP.TELNET等.最爽的是,PHP也支持 cURL 库.本文将介绍 cURL 的一些高级特性 ...

  9. iOS开发网络篇—发送GET和POST请求(使用NSURLSession)

    iOS开发网络篇—发送GET和POST请求(使用NSURLSession) 说明: 1)该文主要介绍如何使用NSURLSession来发送GET请求和POST请求 2)本文将不再讲解NSURLConn ...

  10. 【转】Swift开源项目精选

    https://github.com/ipader/SwiftGuide/blob/master/Featured.md 目录 “轮子” 工具类 存储类 网络类 图片类 界面类 框架类 “车子” 示例 ...