1 前言
linux系统是一个多用户、多任务的现代操作系统,同时任务间(进程)又可细分为多个线程。操作系统在调度用户任务过程,必须遵循用户指定的规则进行,否则不能达到用户预期功能,甚至引起系统异常。操作系统上的多个任务,在宏观上是“并行执行”,用户必须按照互斥或者同步的一种机制,实现资源共享和进程(线程)协作,避免资源冲突,保证程序地正确执行。典型的任务模型:
- 多个任务访问同一资源
- 任务间存在依赖关系和先后顺序
2 临界资源
临界资源指的是多个进程(线程)共享的,并且可能同时访问的资源,典型直观的物理共享资源就是打印机,A和B同时请求打印,打印过程必然是有先后顺序的。临界资源一次只允许一个进程(线程)访问。我们知道进程是操作系统资源分配的基本单位,线程是任务调度和执行的基本单位,不同进程拥有独立的代码空间和内存资源,同一进程下的线程是共享当前进程的所有系统资源。 线程间的所有共享的资源都可以理解为“临界资源”。典型的临界资源包括:
- 共享内存
- 文件描述符
- 堆栈空间
- 全局变量
2.1 临界区
进程(线程)访问临界资源的一段程序称为临界区。从临界区含义看,是先有临界资源再有临界区。对临界资源的互斥访问即是确保多个进程(线程)互斥地进入自己的临界区。一个进程(线程)进入临界区意味着占用了临界资源,因此,应确保临界区代码尽可能少,且停留在临界区的时间要尽量短。
临界区调度原则:
-
临界区代码量尽可能少,减少进程(线程)串行时间
-
一次值允许一个进程(线程)进入
-
已有进程(线程)进入临界区,其他进程(线程)需等待或者被挂起
-
进程(线程)进入临界区应能够尽快退出,不长能时间停留
-
高优先级的进程(线程)在适当时候主动退出临界区,防止其他进程(线程)饥饿
2.2 临界资源访问原则
临界资源的访问必须互斥进行,否则会导致数据异常。即是当临界资源被一个进程(线程)占用时,另一个申请临界资源的进程(线程)会被阻塞,直到其所申请的临界资源被释放。比如A在使用打印机,如果打印机机没有互斥机制,此时又响应B的打印请求,那么打印出来的数据肯定是不符合预期的,只有A的请求打印完成后才执行B的打印才能得到正确的打印结果。对于临界资源访问,一般分为几个步骤:
【1】进入区过程:检查资源是否可访问
【2】临界区过程:访问资源,标记资源已被占用
【3】退出区过程:退出访问,清除资源占用标识
3 互斥与同步
3.1 互斥
互斥指的是多个进程(线程)不能同时访问临界资源,一个进程(线程)访问临界资源时,其他进程(线程)必须等等其访问完并退出后才能访问。
互斥特性:
- 唯一性
- 排他性
互斥场景:
- 文件读写模型,多个读或者写线程访问文件
- 总线访问模型,多个线程访问一个总线(spi、i2c、usb)
- 外设访问模型,多个线程访问一个外部设备(flash、sensor、adc)
3.2 同步
同步指的是多个进程(线程)间必须严格按照一种先后顺序的原则访问临界资源,存在一种依赖的关系,只有A执行了,B才能执行,是一个“生产者—消费者”模型。
同步特性:
- 唯一性
- 排他性
- 顺序性
同步场景:
- 文件读写模型,写文件线程与读文件过程
- 数据通信模型,数据接收线程与数据处理线程
3.3 互斥与同步的联系
从互斥和同步的特性可看出,互斥和同步都具备“唯一性”和“排他性”;不同的是,互斥可以是无序,同步还具有一个“顺序性”,带有依赖关系。可以认为,互斥是特殊的同步;同步是更为复杂的互斥。即是,同步一定是互斥的,但互斥不一定是同步的。
3 POSIX线程互斥与同步机制
POSIX线程标准和POSIX标准提供了几种线程互斥与同步(线程间通信)机制,关于每一种机制的含义、特性、应用场景、函数API及使用规则和使用方法,在后面文章针对性地一一描述。
POSIX线程标准:
【1】互斥锁(Mutex)
【2】读写锁(Read/Write lock)
【4】自旋锁(Spin lock)
【5】屏障(Barrier)
【6】条件变量(Condition variable)
POSIX标准:
【1】信号量(Seamphore)
【2】消息队列(Message queue)
4 参考文章
【1】同步和互斥