一 原子操作

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

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. TypeScript 0.9.1 发布,新增 typeof 关键字

    TypeScript 0.9.1 发布了,该版本提升了编译器和语言的性能,增加新的语言特性 typeof ,更好的 this 处理等.详细介绍请看发行说明. TypeScript 是微软新推出的一种语 ...

  2. 教务管理系统数据库E/R图

  3. LoadRunner测试50人同时登陆下单

    LoadRunner测试50人同时登陆下单 一.LoadRunner简介 LoadRunner,是一种预测系统行为和性能的负载测试工具.通过以模拟上千万用户实施并发负载及实时性能监测的方式来确认和查找 ...

  4. 享受LINQ:判断一组文字是否在字符串中同时出现的最简单方法

    需求是这样的:不允许在一个字符串中同时出现"博", "客", "园", "团", "队"这5个文字. ...

  5. missing locales

    原文地址:http://codewut.de/content/missing-locales-under-debian This drives me crazy! Every time I deboo ...

  6. 创建Fragment

    你可以认为fragment是Activity中模块化的部分.Fragment有它自己的生命周期,接收它自己的输入事件,并且你可以在Activity运行的时候添加或移除它(有点像可以被重用在不同Acti ...

  7. [JS12] 统计访问次数

    <html> <head> <meta http-equiv="Content-Type" content="text/html; char ...

  8. [stm32] 利用uc-gui封装画图和画线函数移植51上的模拟动画

    >_<:这里的动画是黄色矩形区域中一个模仿俯视图的起重机运作动画,一个是模仿主视图的吊钩的运动.通过改变初始Init函数中的数据b_x,b_y实现矩形区域的移动.当实时采集时要首先根据起重 ...

  9. 布局神器display:table-cell

    元素两端对齐 第一个案例是让两个元素分别向左和向右对齐,如果是过去,我一定会用float来实现,但其实用table可以这么做: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ...

  10. 在Html中使用Requirejs进行模块化开发

    在前端模块化的时候,不仅仅是js需要进行模块化管理,html有时候也需要模块化管理.这里就介绍下如何通过requirejs,实现html代码的模块化开发. 如何使用requirejs加载html Re ...