通过《linux内核并发基本概念》,我们看到了,对于并发访问共享资源,造成的运行结果与预期的不一致问题,这样的结果是软件设计者不能允许的。我们知道,我们编写一个软件一定是需要软件实现特定的功能,如果我们在设计的时候,期望得到结果A,但实际软件运行中却得到的结果B,那么这个软件是相当糟糕的,因此,对于并发引起的竞态,是我们在设计内核和驱动软件时,必须要留意的。
在《linux内核并发基本概念》中,通过一个简单的并发的场景,引出了竞态、共享资源和临界区等概念。接下来,本文就linux内核中,容易造成并发的场景,进行一下分析。我们学习知识,往往偏重于对问题的解决方法,而对需要解决什么问题,一知半解,结果学完了方法,也不知道该怎么去在实际中使用,所以andrew觉得,首先我们对需要解决的问题来进行分析,通过分析问题为什么会出现,出现后有什么后果,来让我们认识并发引起的竞态到底是什么,有心的读者,可以在了解了问题之后,不要去翻阅资料查看linux并发控制,而是自己思考下如何解决这些问题,发散下自己的思维,也许会创造出比linux更好的并发控制方法呢。
本文只针对linux内核中常见的并发场景进行描述,为了让并发引起的竞态问题展现的淋漓尽致,本文在场景中不使用任何并发控制。
图1中,数组array为全局的,函数ArrayWrite_A和函数ArrayWrite_B都可以访问,执行函数ArrayWrite_A的线程为线程A,执行函数ArrayWrite_B的线程为线程B。
对称多处理器:
Linux内核从2.0版本开始支持对称多处理器,对称多处理器可以理解为一个计算机中有多个CPU,多个CPU共享内存、总线等资源。对称多处理器可以同时运行,每个CPU可以同时运行不同的代码段,也可能同时运行同一代码段。
如图2所示,例如CPU1和CPU2上执行的两个程序流,程序流1恰好运行好函数ArrayWrite_A开始处,同时,程序流2恰好运行到函数ArrayWrite_B的开始处,如果两个CPU继续运行下去,两条数据流并发运行引起的竞态,会对共享数组array造成什么样的影响,我们是无法预知的,但肯定不是我们想要的结果。
再来看一个简单的例子,如图3,data为一个全局的整形变量,初值为0,函数DataWrite将入参的值赋给全局变量data。在对称多处理器中,多个CPU也可能同时运行同一个代码段,如果CPU1和CPU2同时运行同一个代码段,该代码段包含图3中的函数,那么当两个CPU执行完函数DataWrite的代码后,全局变量的data的值到底是哪个CPU运行的结果呢?这就无法预知了。
中断:
中断也是linux内核中主要的并发源,因为中断可能随时会打断正在运行的代码,这就造成了竞态的危险。
线程A会调用图1中函数ArrayWrite_A,而中断处理程序会调用函数ArrayWrite_B。如图4所示,当线程A正在执行时,中断产生打断了线程A的执行,而中断处理程序也需要对全局数据array进行操作,这样就会引起全局数组array的数据不一致问题。如果线程A已经对全局数组array赋值完毕,中断处理程序势必会覆盖线程A的赋值结果。如果线程A在中断处理程序之后对全局数据array进行赋值,又势必会覆盖掉中断处理程序的结果,如果线程A正在对全局数组array进行赋值,还没有赋值完毕,而被中断处理程序打断,那么全局数组array的赋值情况就是未知的。
除了中断外,中断下半部也是并发的主要来源,这部分会在中断下半部中介绍。在linux内核2.6之后,开始支持内核抢占,正在执行的内核代码,随时可能被抢占,这又会导致并发,从而引起竞态的可能。
Ok,通过对对称多处理器和中断引起的并发场景进行描述,希望读者能更加深入地了解什么是并发,以及并发引起的竞态造成了什么样的后果。如果还没有了解过linux内核并发控制的读者,先不要去查看linux并发控制相关的资料,可以先想一下,上述的情景中,如何能够解决竞态引起的数据不一致问题呢?
原文:http://blog.csdn.net/andrew_yau/article/details/41628259