Linux驱动学习笔记(6)信号量(semaphore)与互斥量(mutex)【转】

转自: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,在必须获得多个锁时,应该始终以相同的顺序获得。

时间: 2024-08-01 23:39:40

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

Linux 内核编程基本功之内核同步与互斥锁mutex

Linux 内核编程基本功之内核同步与互斥锁mutex 作者 digoal 日期 2016-11-07 标签 PostgreSQL , 同步流复制 , mutex , Linux 背景 在使用PostgreSQL实现同步流复制时,在主节点发现有大量的mutex,导致了写并发被限制. 本文为转载文章 http://blog.csdn.net/cug_fish_2009/article/details/6126414 Pro-II.内核同步与互斥锁 1.理解互斥锁? 互斥锁的使用也是保持内核临界区的

linux驱动学习(八) i2c驱动架构(史上最全) davinc dm368 i2c驱动分析【转】

转自:http://blog.csdn.net/ghostyu/article/details/8094049 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 预备知识 linux设备驱动到底复杂在什么地方 linux驱动中 i2c驱动架构 架构层次分类 具体分析 i2c_driver i2c_client i2c_adapter i2c_algorithm 梳理图 ov2715设备i2c驱动源码分析 预备知识 在阅读本文最好先熟悉一种i2c设备的驱动程序,并且浏览一下i

linux Shell学习笔记第一天_其它

以下是Shell学习1-2天学习笔记 ---------我是分隔符--------- 硬件去执行, 内核与硬件之间进行操作. 命令解析器. shell脚本的组成元素 系统命令 文本处理工具(sort.grep.sed.awk-) 变量 条件判断 环循结构 函数 Shell Scripts Center(SSC) ---------------------------- 非负Grep / awk 几天. 统计Wc Sort 排序 sort|head / sed字段处理 awk 数据区域判断 awk

linux多线程学习笔记六--一次性初始化和线程私有数据【转】

转自:http://blog.csdn.net/kkxgx/article/details/7513278 版权声明:本文为博主原创文章,未经博主允许不得转载. 一,一次性初始化 以保证线程在调用资源时,确保资源已经被初始化,并且只初始化一次. 在传统的顺序编程中,一次性初始化经常通过使用布尔变量来管理.控制变量被静态初始化为0,而任何依赖于初始化的代码都能测试该变量.如果变量值仍然为0,则它能实行初始化,然后将变量置为1.以后检查的代码将跳过初始化. 但是在多线程程序设计中,事情就变的复杂的多

驱动学习笔记

一,知识结构 二.驱动分类  可分为 :字符驱动.块设备驱动.网络设备驱动      字符设备驱动以字符为访问单位(一个字符可能对应多个字节)进行顺序访问,不能随机读取.      块设备驱动:以块为访问单位(块可以在内核中进行配置),通常512字节,或者更大的2的N次方.                           块设备可以随机读取.在linux中块设备也可以以字节为单位进行访问,块设备跟字符设备                          主要区别是访问接口的不同,并且块设备

LINUX中断学习笔记【转】

转自:http://blog.chinaunix.net/uid-14825809-id-2381330.html 1.中断的注册与释放: 在 , 实现中断注册接口: int request_irq(unsigned int irq,irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *dev_name,void *dev_id); void free_irq(unsigne

c/c++unix/linux基础学习笔记-常用命令和vi的使用

linux 基本命令的使用-命令在ubuntu下面执行,有些命令通用其他linux,有些不通用. 多条命令间用;号隔开,回车后可以一起执行. clear-前屏,pwd显示当前目录,cd跳转目录. sudo [命令]  -ubuntu 下以管理员身份运行命令. 一般情况下,运行当前目录下的程序,要用 ./文件名 执行. 查看当前shell名称:ps 进入另外一个shell,直接输入shell名称:ksh/tcsh/sh/bash,退出一个shell用:exit. 切换shell命令,如:exec

非常实用的Linux入门级学习笔记

1. 磁盘管理 1.1 查看磁盘分区信息(修改分区) 方法1: 查看 /proc/partitions 文件 [root@localhost TestLabs]# cat /proc/partitions major minor  #blocks  name    8        0   67108864 sda    8        1     512000 sda1    8        2   66595840 sda2  253        0   31985664 dm-0  

linux Shell学习笔记第五天_其它

第五天:函数与任务调度 函数的优势 分而治之f 协同合作 方便管理 维护简单 函数的结构     function 函数名()     {         命令1         命令2         命令3     } 函数的参数传递     向函数传递参数就像在一般脚本中使用特殊变量$1,$2,$3-$9一样,函数取得所传参数后将原始参数传回shell脚本,因此最好先在函数内重新设置变量保存所传的参数.这样如果函数有一点错误,就可以通过已经本地化的变量名迅速加以跟踪. 函数文件     当