转自:http://blog.chinaunix.net/uid-24943863-id-3193530.html

并发导致竟态,从而导致对共享数据的非控制访问,产生非预期结果,我们要避免竟态的发生。遵循以下原则:1,尽量避免资源共享;2,显示地管理对共享资源的访问。管理技术通常为“锁定”或者“互斥”,保证任何时刻只有一个执行线程可操作共享资源。

        几个重要的概念:1,原子操作,顾名思义,就是说该操作是原子性的(原子是保持物质物理新性质的最小单位),不可分割的,亦即操作要么处于不间断地执行的状态,要么处于不执行的状态。2,临界区,临界区是这么一段代码,这段代码在任意给定时刻只能被一个线程执行。3,休眠,休眠是程序处于阻塞的状态,进程到达某个时间点,此时它不能进行任何处理(可能是等待某资源),它让出处理器给别的进程用,直到它能够完成自己的处理为止。相当于进程进入睡觉的状态,但它永远不知道要睡多长时间,也许它几天后才醒来,但是它会认为只是打了个盹。
         信号量的本质是一个整数值,可以取0或者正整数,信号量为0表示该信号量标志的资源对当前进程不可用(可能其他线程正在使用),为正整数n表示允许唤醒n个线程对资源进行操作。信号量配合一对函数P和V使用,P是Proberen(荷兰语),测试的意思,V(Verhogen)是增加。P操作对应down()函数,测试信号量的值,若大于0,则信号量值减1,进程继续;若信号量等于0,那么调用进程休眠(调用sleep()),此时down()并没有结束(而是处于休眠状态)。P操作是原子操作,也就是说检查信号量和修改信号量(或者休眠,如果信号量为0的话)是一个单一的不可分割的操作。V操作对应于up()函数,完成对信号量的加1操作,如果有线程在等待该信号量而处于休眠状态(调用down()检查到信号量为0),则由系统唤醒其中一个线程完成down()函数。虽然此刻信号量又回到了0,但是处于等待的进程数少了一个,等该进程完成了对共享资源的访问,需要调用up()函数释放信号量,使得其他等待该信号量的线程能够继续。
    要使用信号量,首先需要初始化,使用下面的函数进行初始化一个信号量,val用来指定信号量的初始值,即有最多有val 个进程可以并发访问down()和up()之间保护的资源。    
  void sema_init(struct semaphore *sem,int val)
内核中P操作有三个版本
void down(struct semaphore *sem)
int down_interruptible(struct semaphore *sem)
int down-trylock(struct semaphore *sem) //检查信号量后立即返回,不进入休眠
down_interruptible()是dwon()的可中断版本,也就是说用户可以中断正在等待该信号量的进程。这个函数是我们需要始终使用的版本。
down()和up()是成对调用的,up()函数用于释放信号量,其函数原型是
void up(struct semaphore *sem)
        当调用sema_init()函数初始化信号量,如果val取1,那么该信号量就是一个互斥量!互斥量是只有两个状态的变量,解锁(0)和加锁(非零)。也就是说信号量可以实现互斥,但并不是所有互斥量都是信号量。信号量和互斥量就像有交集的两个集合。对于一些不能休眠的代码,如中断处理handle,不能使用信号量的down()函数,因为down会导致休眠,这时候可以使用自旋锁来实现互斥!自旋锁的工作原理与信号量颇为相似,spin_lock()不断检查锁是否可用,当锁可用时(处于解锁状态,互斥量为0),那么加锁(互斥量设置为非零),进程可以继续,进入临界区执行代码,当锁不可用时,那么spin_lock()进入忙循环并重复检查锁,直到锁可用,代码此刻在这里循环,像不像自旋(down()函数此时则进入休眠)。与信号量一样,使用自旋锁时必须对互斥量进行初始化,有两种方法可以完成初始化,一是声明锁时赋值,二是调用初始化函数传递实参
spinlock_t mylock=SPIN_LOCK_UNLOCKED
void spin_lock_init(spinlock_t *lock)
获得锁的函数(相当于信号量中的P操作)是
void spin_lock(spinlock_t *lock)
同样,用完之后需要释放锁,调用下面的代码
void spin_unlock(spinlock_t *lock)
        由于自旋锁会造成死锁,因此需要小心使用。需要遵循以下规则:
1,任何拥有自旋锁的代码必须是原子的,它不能休眠!注意,copy_from_user,kmalloc等会导致休眠的函数在拥有spinlock的临界区是不能使用的。
2,在拥有自旋锁时禁止中断,使用函数spin_lock_irqsave()或spin_lock_irq()代替spin_lock()
3,自旋锁必须在可能的最短时间内拥有。也就是锁自旋锁保护的临界区代码执行得越快越好。
4,必须避免获得锁的函数调用同样试图获得该锁的函数,否则会造成死锁。
5,在必须获得多个锁时,应该始终以相同的顺序获得。

Linux驱动学习笔记(6)信号量(semaphore)与互斥量(mutex)【转】的更多相关文章

  1. linux 驱动学习笔记01--Linux 内核的编译

    由于用的学习材料是<linux设备驱动开发详解(第二版)>,所以linux驱动学习笔记大部分文字描述来自于这本书,学习笔记系列用于自己学习理解的一种查阅和复习方式. #make confi ...

  2. Linux 驱动学习笔记05--字符驱动实例,实现一个共享内存设备的驱动

    断断续续学驱动,好不容易有空,做了段字符驱动的例子.主要还是跟书上学习在此记录下来,以后说不定能回过头来温故知新. 首先上驱动源码 gmem.c: /************************* ...

  3. linux驱动学习笔记---实现中断下半部以及驱动编写规范(七)【转】

    转自:https://blog.csdn.net/weixin_42471952/article/details/81609141 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协 ...

  4. linux 驱动学习笔记04--简单驱动

    首先贴代码helloworld.c和Makefile /************************************************************************ ...

  5. linux 驱动学习笔记03--Linux 内核的引导

    如图所示为 X86 PC 上从上电/复位到运行 Linux 用户空间初始进程的流程.在进入与 Linux相关代码之间,会经历如下阶段. ( 1 ) 当系统上电或复位时, CPU 会将 PC 指针赋值为 ...

  6. 小松之LINUX 驱动学习笔记(二)

    这两天一直在看字符驱动那块,后来从网上找啦几个例子,自己编译啦下,安装啥的都挺正常,就是用测试程序测试的时候总出问题,现在找到一个能测试的代码,自己先看看和原来的那个代码有啥不同,后面会继续更新,说下 ...

  7. 小松之LINUX 驱动学习笔记(一)

    本篇主要是讲解驱动开发的基础知识以及一些环境配置方面的问题. 下面是一个hello world的简单的模块代码,很简单./*********************** 模块的简单例子* author ...

  8. linux 驱动学习笔记02--应用实例:在内核中新增驱动代码目录和子目录

    下面来看一个综合实例,假设我们要在内核源代码 drivers 目录下为 ARM 体系结构新增如下用于 test driver 的树型目录:| --test  | -- cpu  | -- cpu.c ...

  9. 小松之LINUX驱动学习笔记之模块间函数调用通讯

    1. 符号导出函数 EXPORT_SYMBOL() EXPORT_SYMBOL标签内定义的函数对全部内核代码公开,不用修改内核代码就可以在您的内核模块中直接调用. EXPORT_SYMBOL_GPL( ...

随机推荐

  1. 微信支付开发(12) 认清微信支付v2和v3

    微信支付现在分为v2版和v3版 2014年9月10号之前申请的为v2版,之后申请的为v3版. V2版中的参数有AppIDAppSecret支付专用签名串PaySignKey商户号PartnerID初始 ...

  2. Sersync实现触发式文件同步 替代inotify和rsync

    Sersync实现触发式文件同步 替代inotify和rsync Pyinotify是一个Python模块,用来监测文件系统的变化. Pyinotify依赖于Linux内核的功能—inotify(内核 ...

  3. 网站提供的下载IE8很慢 由于Microsoft 联机服务暂时不可用,SmartScreen筛选器无法检查此网站。

    在内网环境中,网站系统提供了一个下载功能,用ie8下载特别慢,一个20kb的文件,下载要10分钟,但是在其他环境中是很快的,试了半天,原来是:由于Microsoft 联机服务暂时不可用,SmartSc ...

  4. andriod网址

    http://www.runoob.com/w3cnote/android-tutorial-end.html  入门教程 长青说安卓

  5. 第四篇 SQL Server代理配置数据库邮件

    本篇文章是SQL Server代理系列的第四篇,详细内容请参考原文. 正如这一系列的前几篇所述,SQL Server代理作业是由一系列的作业步骤组成,每个步骤由一个独立的类型去执行.SQL Serve ...

  6. hive 的mysql配置

    hive默认使用的是Derby数据库,Derby是一个嵌入式数据库,数据库一般创建在运行hive命令的目录,如果切换目录运行,则找不到数据库 hive mysql配置: 官网地址:https://cw ...

  7. hive DDL

    官网地址:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL https://cwiki.apache.org/co ...

  8. Java NIO 读数据处理过程

    这两天仿hadoop 写java RPC框架,使用PB作为序列号工具,在写读数据的时候遇到一个小坑.之前写过NIO代码,恰好是错误的代码产生正确的逻辑,误以为自己写对了.现在简单整理一下. 使用NIO ...

  9. Codeforce Round #224 Div2

    一下子没打,这比赛,就被虐成狗!

  10. php初探

    1.php中的连接符.可以连接多个字符串,相当于java中的+ 2.echo必须与后面的输出内容有至少一个空格 3.php编程中每个结尾都需要添加分号