竞争的概念

什么是竞争

Linux 系统是个多任务操作系统,会存在多个任务同时访问同一片内存区域,这些任务可能会相互覆盖这段内存中的数据,造成内存数据混乱。针对这个问题必须要做处理,严重的话可能会导致系统崩溃。 Linux 系统并发产生的原因有下面几个主要原因:

多线程并发访问,Linux 是多任务(线程)的系统,所以多线程访问是最基本的原因。

抢占式并发访问,从 2.6 版本内核开始,Linux 内核支持抢占,也就是说调度程序可以在任意时刻抢占正在运行的线程,从而运行其他的线程。

中断程序并发访问

SMP(多核)核间并发访问,现在 ARM 架构的多核 SOC 很常见,多核 CPU 存在核间并发访问。

==所谓的临界区就是共享数据段,对于临界区必须保证一次只有一个线程访问==,也就是要保证临界区是原子访问的

什么是共享资源

共享资源是指在同一时间内可以被多个执行单元(如进程、线程、中断处理程序、内核任务等)访问(读取或修改) 的任何数据、数据结构、硬件设备或状态信息。

  • 关键特性:

    • 并发访问可能性: 多个执行单元(几乎)同时或在时间上重叠地尝试访问该资源。
    • 状态可变性: 资源的状态(值、内容、配置)通常是可以被改变的(写操作)。即使只是读操作,如果资源的状态在读取过程中可能被其他执行单元修改,也可能导致问题(读到不一致的数据)。
    • 稀缺性: 通常资源不是无限的(例如,硬件设备只有一个,内存空间有限),需要协调访问。
  • 内核中的共享资源

    • 进程(用户态) 内核线程 中断处理程序 软中断 Tasklet 工作队列 对称多处理
    • 内核中的共享资源是指可以被上述任何一种或多种执行单元并发访问的内核数据结构、硬件寄存器、外设状态等。
  • 内核中共享资源的经典例子

    • **全局变量和静态变量:**它们在内核地址空间中只有一份实例。任何访问它的代码路径(无论是进程上下文还是中断上下文)都在操作同一个内存位置。
    • 数据结构(链表、哈希表、树、队列等):这些是内核组织和管理信息(进程、文件、网络连接、设备等)的核心方式
      • 进程列表(task_struct 组成的链表)。
      • 文件描述符表(files_struct)。
      • 虚拟内存区域链表(vm_area_struct)。
      • 网络协议栈中的套接字队列、数据包缓冲区(sk_buff 链表)。
      • 块设备驱动程序的请求队列。
    • **硬件资源及其寄存器:**物理设备(网卡、磁盘控制器、GPU、串口等)只有一套寄存器。对这些寄存器的读写操作控制着设备的行为。
      • 网卡的发送/接收队列描述符寄存器。
      • 磁盘控制器的命令/状态寄存器。
      • 定时器配置寄存器。
      • 共享内存映射的 I/O 区域。
    • 文件系统元数据: 文件系统的超级块、inode 表、位图(标记数据块使用情况)等,描述了磁盘上数据的组织方式,被所有访问该文件系统的进程共享。
      • inode 的 i_size(文件大小)、i_blocks(占用块数)、i_atime/i_mtime/i_ctime(访问/修改/改变时间)。
      • 超级块中的空闲块计数、空闲 inode 计数。
      • 数据块位图(标记哪些块空闲)。
    • **中断状态:**中断处理程序(尤其是共享中断线)需要访问和修改与设备状态相关的数据,这些数据也可能被进程上下文的驱动代码访问。
      • 网络驱动中,中断处理程序接收数据包并将其放入队列,用户态进程(通过系统调用)或内核线程从队列中取出数据包处理。这个队列就是共享资源。

什么是临界区

临界区(Critical Section)是并发编程中的核心概念,指访问共享资源的代码段,其执行必须满足互斥性(同一时刻仅允许一个执行单元进入)。

临界区的本质特征

1. 共享资源依赖性

  • 操作目标必须是多执行单元可访问的资源:
    • 内存变量(全局/静态变量)
    • 硬件寄存器(设备状态寄存器)
    • 数据结构(链表、树、队列)
    • 文件/数据库记录

2. 非原子性操作

  • 操作由多条指令组成,可能被中断打断:

    1
    2
    3
    // 典型非原子操作
    counter++; // 实际步骤:LOAD -> ADD -> STORE
    list_del(node); // 涉及多指针修改

3. 状态可变性

  • 对资源进行写操作(或读-改-写组合)