📄 linux设备驱动程序学习(3)-并发和竞态 - linux设备驱动程序 - tekkaman ninja.htm
字号:
style="FONT-WEIGHT: normal; COLOR: rgb(0,1,2)">第五章并发和竞态的学习。</SPAN></FONT>
</P>
<P align=left><FONT color=#000000><SPAN
style="FONT-WEIGHT: normal; COLOR: rgb(0,1,2)">对并发的管理是操作系统编程中核心的问题之一。</SPAN></FONT><FONT
color=#000000><SPAN
style="FONT-WEIGHT: normal; COLOR: rgb(0,1,2)"> 并发产生竞态,竞态导致<FONT
color=#0000ff>共享数据</FONT>的非法访问。因为竞态是一种极端低可能性的事件,因此程序员往往会忽视竞态。但是<FONT
color=#0000ff>在计算机世界中,百万分之一的事件可能没几秒就会发生</FONT>,而其结果是灾难性的。</SPAN></FONT></P><FONT
color=#000000><FONT size=2>
<HR id=null>
</FONT></FONT><FONT color=#0000ff
size=4><STRONG>一、并发及其管理</STRONG></FONT>
<DIV>竞态通常是作为<FONT
color=#ff0000>对资源的共享访问</FONT>结果而产生的。</DIV>
<DIV>在设计自己的驱动程序时,第一个要记住的规则是:<FONT
color=#ff0000>只要可能,就应该避免资源的共享。</FONT>若没有并发访问,就不会有竞态。<FONT
color=#0000ff>这种思想的最明显的应用是避免使用全局变量。</FONT></DIV>
<DIV>但是,资源的共享是不可避免的 ,如硬件资源本质上就是共享、指针传递等等。</DIV>
<DIV>资源共享的硬性规则:</DIV>
<DIV>(1)在单个执行线程之外共享硬件或软件资源的任何时候,因为另外一个线程可能产生对该资源的不一致观察,因此<FONT
color=#ff0000>必须显示地管理对该资源的访问</FONT>。--访问管理的常见技术成为“锁定”或者“互斥”:确保一次只有一个执行线程可操作共享资源。</DIV>
<DIV>(2)当内核代码创建了一个可能和其他内核部分共享的对象时,该对象必须在还有其他组件引用自己时保持存在(并正确工作)。对象尚不能正确工作时,不能将其对内核可用。</DIV>
<DIV>
<HR id=null>
</DIV>
<DIV><FONT color=#0000ff
size=4><STRONG>二、信号量和互斥体</STRONG></FONT></DIV>
<DIV>一个信号量(semaphore:
旗语,信号灯)本质上是一个整数值,它和一对函数联合使用,这一对函数通常称为P和V。希望进入临届区的进程将在相关信号量上调用P;如果信号量的值大于零,则该值会减小一,而进程可以继续。相反,如果信号量的值为零(或更小),进程必须等待知道其他人释放该信号。对信号量的解锁通过调用V完成;该函数增加信号量的值,并在必要时唤醒等待的进程。</DIV>
<DIV>当信号量用于互斥时(即避免多个进程同是在一个临界区运行),信号量的值应初始化为1。这种信号量在任何给定时刻只能由单个进程或线程拥有。在这种使用模式下,一个信号量有事也称为一个“互斥体(mutex)”,它是互斥(mutual
exclusion)的简称。<FONT
color=#0000ff>Linux内核中几乎所有的信号量均用于互斥</FONT>。</DIV>
<DIV>使用信号量,内核代码必须包含<FONT
color=#0000ff><asm/semaphore.h></FONT>
。</DIV>
<DIV> </DIV>
<DIV>以下是信号量初始化的方法:</DIV>
<DIV>
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: rgb(0,0,0)"><FONT face=新宋体><SPAN
style="COLOR: rgb(255,153,0)">/*初始化函数*/</SPAN><BR><SPAN
style="COLOR: rgb(0,0,255)">void</SPAN>
sema_init<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN><SPAN
style="COLOR: rgb(0,0,255)">struct</SPAN>
semaphore <SPAN
style="COLOR: rgb(0,0,204)">*</SPAN>sem<SPAN
style="COLOR: rgb(0,0,204)">,</SPAN> <SPAN
style="COLOR: rgb(0,0,255)">int</SPAN> val<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><SPAN
style="COLOR: rgb(0,0,204)">;</SPAN><BR></FONT></SPAN></CODE></P></TD></TR></TBODY></TABLE></DIV>
<DIV> </DIV>
<DIV>由于信号量通常被用于互斥模式。所以以下是内核提供的一组辅助函数和宏:
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: rgb(0,0,0)"><FONT face=新宋体><SPAN
style="COLOR: rgb(255,153,0)">/*方法一、声明+初始化宏*/</SPAN><BR>DECLARE_MUTEX<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN>name<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><SPAN
style="COLOR: rgb(0,0,204)">;</SPAN><BR>DECLARE_MUTEX_LOCKED<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN>name<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><SPAN
style="COLOR: rgb(0,0,204)">;</SPAN><BR><BR><SPAN
style="COLOR: rgb(255,153,0)">/*方法二、初始化函数*/</SPAN><BR><SPAN
style="COLOR: rgb(0,0,255)">void</SPAN>
init_MUTEX<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN><SPAN
style="COLOR: rgb(0,0,255)">struct</SPAN>
semaphore <SPAN
style="COLOR: rgb(0,0,204)">*</SPAN>sem<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><SPAN
style="COLOR: rgb(0,0,204)">;</SPAN><BR><SPAN
style="COLOR: rgb(0,0,255)">void</SPAN>
init_MUTEX_LOCKED<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN><SPAN
style="COLOR: rgb(0,0,255)">struct</SPAN>
semaphore <SPAN
style="COLOR: rgb(0,0,204)">*</SPAN>sem<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><SPAN
style="COLOR: rgb(0,0,204)">;</SPAN><BR><BR><SPAN
style="COLOR: rgb(255,153,0)">/*带有“_LOCKED”的是将信号量初始化为0,即锁定,允许任何线程访问时必须先解锁。没带的为1。*/</SPAN></FONT><BR></SPAN></CODE></P></TD></TR></TBODY></TABLE></DIV>
<DIV> </DIV>
<DIV>P函数为:
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: rgb(0,0,0)"><FONT face=新宋体><SPAN
style="COLOR: rgb(0,0,255)">void</SPAN>
down<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN><SPAN
style="COLOR: rgb(0,0,255)">struct</SPAN>
semaphore <SPAN
style="COLOR: rgb(0,0,204)">*</SPAN>sem<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><SPAN
style="COLOR: rgb(0,0,204)">; <FONT
color=#ff9900>/*不推荐使用,会建立不可杀进程*/</FONT></SPAN><BR><SPAN
style="COLOR: rgb(0,0,255)">int</SPAN>
down_interruptible<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN><SPAN
style="COLOR: rgb(0,0,255)">struct</SPAN>
semaphore <SPAN
style="COLOR: rgb(0,0,204)">*</SPAN>sem<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><SPAN
style="COLOR: rgb(0,0,204)">;<FONT
color=#ff9900>/*推荐使用,使用<FONT
color=#ff9900>down_interruptible需要格外小心,若操作被中断,该函数会返回非零值,而调用这不会拥有该信号量。对down_interruptible的正确使用需要始终检查返回值,并做出相应的响应。</FONT>*/</FONT></SPAN><BR><SPAN
style="COLOR: rgb(0,0,255)">int</SPAN>
down_trylock<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN><SPAN
style="COLOR: rgb(0,0,255)">struct</SPAN>
semaphore <SPAN
style="COLOR: rgb(0,0,204)">*</SPAN>sem<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><SPAN
style="COLOR: rgb(0,0,204)">;<FONT
color=#ff9900>/*带有<FONT
color=#ff9900>“_trylock”的</FONT>永不休眠,若信号量在调用是不可获得,会返回非零值。*/</FONT></SPAN></FONT></SPAN></CODE></P></TD></TR></TBODY></TABLE></DIV>
<DIV> </DIV>
<DIV>V函数为:
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: rgb(0,0,0)"><FONT face=新宋体><SPAN
style="COLOR: rgb(0,0,255)">void</SPAN> up<SPAN
style="COLOR: rgb(0,0,204)">(</SPAN><SPAN
style="COLOR: rgb(0,0,255)">struct</SPAN>
semaphore <SPAN
style="COLOR: rgb(0,0,204)">*</SPAN>sem<SPAN
style="COLOR: rgb(0,0,204)">)</SPAN><SPAN
style="COLOR: rgb(0,0,204)">;<FONT
color=#ff9900>/*任何拿到信号量的线程都必须通过一次(只有一次)对up的调用而释放该信号量。在出错时,要特别小心;若在拥有一个信号量时发生错误,必须在将错误状态返回前释放信号量。*/</FONT><BR></SPAN></FONT></SPAN></CODE></P></TD></TR></TBODY></TABLE></DIV>
<DIV> </DIV>
<DIV><FONT color=#0000ff
size=3><STRONG>在scull中使用信号量</STRONG></FONT></DIV>
<DIV> </DIV>
<DIV>其实在之前的实验中已经用到了信号量的代码,在这里提一下应该注意的地方:</DIV>
<DIV>在初始化scull_dev的地方:
<TABLE style="BORDER-COLLAPSE: collapse"
borderColor=#999999 cellSpacing=0 cellPadding=0
width="95%" bgColor=#f1f1f1 border=1>
<TBODY>
<TR>
<TD>
<P
style="MARGIN: 5px; LINE-HEIGHT: 150%"><CODE><SPAN
style="COLOR: rgb(0,0,0)"><FONT face=新宋体><SPAN
style="COLOR: rgb(255,153,0)">/* Initialize each
device.
*/</SPAN><BR> <SPAN
style="COLOR: rgb(0,0,255)">for</SPAN> <SPAN
style="COLOR: rgb(0,0,204)">(</SPAN>i <SPAN
style="COLOR: rgb(0,0,204)">=</SPAN> 0<SPAN
style="COLOR: rgb(0,0,204)">;</SPAN> i <SPAN
style="COLOR: rgb(0,0,204)"><</SPAN>
scull_nr_devs<SPAN
style="COLOR: rgb(0,0,204)">;</SPAN> i<SPAN
style="COLOR: rgb(0,0,204)">+</SPAN><SPAN
style="COLOR: rgb(0,0,204)">+</SPAN><SPAN
style="COLOR: rgb(0,0,204)">)</SPAN> <SPAN
style="COLOR: rgb(0,0,204)">{</SPAN><BR> scull_devices<SPAN
style="COLOR: rgb(0,0,204)">[</SPAN>i<SPAN
style="COLOR: rgb(0,0,204)">]</SPAN><SPAN
style="COLOR: rgb(0,0,204)">.</SPAN>quantum
<SPAN style="COLOR: rgb(0,0,204)">=</SPAN>
scull_quantum<SPAN
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -