Notes
main
main
  • Introduction
  • linuxKernel
    • tips
    • make_help
    • old linux
      • compile_linux0.11
      • TestEnvironment
      • load_setup
      • get_hard_data
    • list
    • plist
    • fifo
    • idr
    • xarray
    • rbtree
    • maple_tree
    • syscall
    • bitmap
    • page
    • page_flags
    • page_size
    • page mapcount
    • page refcount
    • folio
    • slub
      • proc_slabinfo
      • slub_theory
      • kmalloc_kfree
      • kmem_cache
      • slab_alloc
      • slab_free
      • proc_meminfo_SReclaimable_SReclaimable
    • vmalloc
    • brk
    • mmap
    • mremap
    • mprotect
    • madvise
    • read
    • write
    • shmem
    • huge_page
    • page_fault
    • rmap
    • lru
    • multi-gen-LRU
    • page_reclaim
    • page_cache
    • page_table
    • rcu
    • kvm
    • aarch64_boot
    • tracing_system
    • cache_coherence_and_memory_consistency
    • cpu_speculates
    • mmap_lock
    • per-vma_lock
    • cgroup
    • symbol
    • campact
    • page_ext
    • mempool
    • kernelstack
    • filesystem
    • io_stack
    • workingset
    • ioremap
    • sched_period
  • linuxDebug
    • openocd_openjtag
    • i2c_tools
    • objdump
    • addr2line
    • gdb_useage
    • debug_linux_kernel_via_gdb
    • debug_linux_module_via_gdb
    • early_boot
    • sequentially_execute
    • dynamic_debug
    • research_linuxKernel_by_patch
    • tracefs
    • ebpf
    • bpftrace
    • perf
    • flame_graph
    • crash
    • ASAN_HWASAN_MTE_check_mem_bug
    • page_owner
    • vmtouch
    • fio
    • benchmark
  • linuxSystem
    • common
      • system_version
      • procfs
      • proc_sys_vm
      • cmd_ps
      • makefile
      • file_descriptor
      • psi
      • ulimit
      • top
      • delay_accounting
    • ubuntu
      • custom_kernel
      • get_cmd_src
      • record_ssh_info
      • log
      • run_custom_script
      • repo
      • cockpit
      • nfs
      • tftp
      • misc
    • fedora
      • system_upgrade
      • custom_kernel
      • lvextend
      • yt-dlp
      • jellyfin
  • linuxDriver
    • i2c_peripherals_driver
    • spi_peripherals_driver
    • gpio_subsystem
    • IRQ_driver
    • blockIO_unblockIO_async
    • linux_own_driver
    • misc_device
    • input_device
    • timer
    • atomic_spinlock_semaphore_mutex
    • lcd
    • touch_screen
    • debugfs
    • v4l2
    • mmap
  • hardware
    • paging_mmu_pt
    • iommu
  • process_thread_scheduler
    • scheduler01
    • scheduler02
    • scheduler03
    • scheduler04
    • scheduler05
    • scheduler06
  • memory_management
    • mm1
    • mm2
    • mm3
    • mm4
    • mm5
  • input_output_filesystem
    • io_fs_01
    • io_fs_02
    • io_fs_03
    • io_fs_04
  • lock_and_lockup_detector
    • general_lock
    • hung_task
    • softLockup_hardLockup
    • crash_experiment
  • MIT_6.S081
    • 6.S081_Operating_System_Engineering
    • Schedule.md
    • Class
      • Overview
      • Administrivia
    • Labs
      • Tools
      • Guidance
      • startup
      • syscall
      • page_table
      • Calling_Convention
      • traps
    • xv6
      • xv6
    • References.md
  • qemu
    • qemu_buildroot
    • qemu_busybox.md
    • Serial.md
    • demo_mini2440
      • 0_compilation_error_summary
      • 1_compilation_steps
      • 2_operation_mode
      • 3_transplant_tools_libraries
      • 4_tools_use
      • reference_website
  • tools
    • getKernelSourceCodeList
    • nat
    • shell
    • translating
    • YouCompleteMe
    • cscope
    • global
    • vscode
    • vim
    • binary
    • markdown
    • draw
    • git
    • tig
    • tmux
    • mail_client
    • download_patchset_from_LKML
    • minicom
    • clash
  • other
    • interview
    • interview_c_base
    • know_dontknow
    • Stop-Ask-Questions-The-Stupid-Ways
    • How-To-Ask-Questions-The-Smart-Way
    • docker
    • buildroot
    • rv32_to_rv64
Powered by GitBook
On this page
  • 自旋锁
  • 普通自旋锁 spinlock
  • 读写自旋锁 rwlock_t
  • 信号量
  • 普通信号量
  • 读写信号量
  • 互斥量
  • 死锁原理

Was this helpful?

  1. lock_and_lockup_detector

general_lock

自旋锁

普通自旋锁 spinlock

相关结构体:

typedef struct spinlock(
    union {
        struct raw_spinlock rlock:
    }
} spinlock t:

struct raw_spinlock {
    arch_spinlock_t raw_lock;
}
  • 获取锁的过程(即上锁的过程)是自旋(忙等)的,不会引起睡眠和调度

  • 持有自旋锁的临界区中不允许调度和睡眠,自旋锁的加锁操作会禁止抢占,解锁操作时再恢复抢占

    自旋锁既可以用于进程上下文,又可以用于中断上下文

  • 自旋锁的主要用途是多处理器之间的并发控制,适用于锁竞争不太激烈的场景

    如果锁竞争非常激烈,那么大量的时间会浪费在加锁自旋中,导致整体性能下降

相关API:

DEFINE_SPINLOCK(lock) // 静态定义一个名为lock的自旋锁
spin_lock_init(lock)  // 自旋锁初始化(设置为未锁状态)

spin_lock(lock)                // 加锁操作,加锁成功后返回,否则一直自旋(忙等)
spin_lock_irqsave(lock, flags) // 加锁操作,并关闭硬中断
spin_lock_bh(lock)             // 加锁操作,并关闭软中断

spin_unlock(lock)                   // 解锁操作,解锁过程无竟争,因此必然会成功
spin_unlock_irqrestore(lock, flags) // 解锁操作,并打开硬中断
spin_unlock_bh(lock)                // 解锁操作,并打开软中断

读写自旋锁 rwlock_t

普通自旋锁的缺点:

  • 对所有的竞争者不做区分

  • 很多情况下,有些竞争者并不会修改共享资源(只读不写)

  • 普通自旋锁总是会限制只有一个内核路径持有锁,而实际上这种限制是没有必要的

读写自旋锁的改进:

  • 允许多个读者同时持有读锁(允许多个读者同时进入读临界区)

  • 只允许一个写者同时持有写锁(只允许一个写者同时进入写临界区)

  • 不允许读者和写者同时持有锁

  • 与普通自旋锁相比,读写自旋锁更适合读者多,写者少的应用场景

相关API:

DEFINE_RWLOCK(lock) // 静态定义一个名为lock的自旋锁
rwlock_init(lock)   // 自旋锁初始化(设置为未锁状态)

read_lock(lock)     // 加读锁操作,加锁成功后返回,否则一直自旋
write_lock(lock)    // 加写锁操作,加锁成功后返回,否则一直自旋
read_unlock(lock)   // 解读锁操作,解锁过程无竟争,因此必然会成功
wite_unlock(lock)   // 解写锁操作,解锁过程无竟争,因此必然会成功

信号量

  • Linux内核中应用最广泛的同步方式: 自旋锁、信号量(semaphore)

  • 自旋锁和信号量是一种互补的关系,它们有各自适用的场景

  • 信号量可以是多值的,当其用作二值信号量时,类似于锁 : 一个值代表未锁,另一个值代表已锁

  • 工作原理与自旋锁相反

    • 获取锁的过程中,若不能立即得到锁,就会发生调度,转入睡眠

    • 另外的内核执行路径释放锁时,唤醒等待该锁的执行路径

  • 自旋锁的使用限制及信号量的解决方法

    • 持有自旋锁的临界区不允许调度和睡眠 : 竟争激烈时整体性能不好

    • 信号量解决了以上两个问题:

      • 锁的竟争者不是忙等,信号量的临界区允许调度和睡眠而不会导致死锁

      • 锁的竞争者会转入睡眠,从而让出CPU资源给别的内核执行路径,因此对锁的竞争不会影响整体性能

    • 信号量的缺点:

      • 中断上下文要求整体运行时间可预测(不能太长),而信号量临界区可能发生调度,因此不能用于中断上下文(只能用于进程上下文)

      • 如果抢锁的过程很短,那么用信号量并不合算,因为进程睡眠加上唤醒的代价太大,消耗的CPU资源可能远远大于短时间的忙等

普通信号量

相关结构体:

 struct semaphore {
     raw_spinlock_t lock;
     unsigned int count;
     struct list_head wait_list;
 };
  • count标识信号量的状态 : 值为0表示忙(已锁),值为正代表自由(未锁,允许竞争者进入临界区)

  • count的初值就是最大允许进入临界区的进程数目,初值为1的信号量就是二值信号量

  • 二值信号量类似于一个普通的锁,而多值信号量类似于一个允许一定并发性的锁

  • wait_list字段是当信号量为忙时,所有等待信号量的进程列表,而lock则是保护wait_list的自旋锁

相关API:

DEFINE_SEMAPHORE(sem)                          // 静态定义一个名为sem信号量
void sema_init(struct semaphore *sem, int val) // 初始化一个信号量sem,计数器初值为val

// 减少信号量sem的计数器(类似于获取锁)
// 如果失败(计数器己经是0),那么转入睡眠(状态为IASK_ UNINTERRUPTIBLE,不会被任何信号唤醒)并把当其进程挂到wait_list; 被唤醒后继续尝试获取锁
void down(struct semaphore *sem)
// 增加信号量sem的计数器(类似于释放锁),然后唤醒wait_list里面的第一个进程(如果有的话)
void up(struct semaphore *sem)

读写信号量

读写信号量的引入原因类似于读写自旋锁,是为了区分不同的竟争者(读者和写者),以便允许读者共享而写者互斥

相关结构体:

struct rw_semaphore {
    long count;
    struct list_head wait_list;
    raw_spinlock_t wait_lock;

#ifdef CONFIG_RWSEM_SPIN_ON_OWNER
    struct optimistic_spin_queue osq;
    struct task_struct *owner;
#endif
}

主要字段 count、 wait_list 和 wait_lock的含义与普通信号量基本相同,owner指向写者进程

相关API:

DECLARE_RWSEN(sem) // 静态声明一个名为sem信号量
init_rwsen(sem)    // 初始化一个信号量sem

down_read(struct rw_semaphore *sem) // 读者减少信号量sem的计数器(类似于获取锁)
down_write(struct rw_semaphore *sem)// 写者减少信号量sem的计数器(类似于获取锁)
up_read(struct rw_semaphore *sem)   // 读者增加信号量sem的计数器(类似于释放锁)
up_write(struct rw_semaphore *sem)  // 写者增加信号量sem的计数器(类似于释放锁)

互斥量

互斥量是二值信号量

相关结构体:

struct mutex {
    atomic_t count;
    spinlock_t wait_lock;
    struct list_head wait_list;

#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
    struct task_struct *owner;
    struct optimistic_spin_queue osq;
#endif
};

在数据结构上与信号量几乎相同

相关API:

互斥量提供了一套新的API,这套API专为二值的互斥量优化

DEFINE_MUTEX(mutex) // 静态定义一个名为 mutex的互斥量
mutex_init(mutex)   // 初始化一个互斥量 mutex,初始状态为未锁

// 对互斥量加锁,如果失败,那么转入睡眠(状态为 TASK_UNINTERRUPTIBLE,不会被任何信号唤醒)并将进程挂到wait_list
void mutex_lock(struct mutex *lock)
// 对互斥量解锁,然后唤醒wait_list里面的第一个进程(如果有的话)
void mutex_unlock(struct mutex *lock)

死锁原理

典型例子:

  • spinlock, rwlock, mutex 在 linux内核中是不能递归,如果产生递归会发生死锁

    例如: 调用spinlock(A),在本cpu上发生中断,中断上下文也调用到 spinlock(A)

  • 多个CPU相互等待lock A/B

    --------CPU1------------------------------CPU2---------

    获取 lock A -> ok ----------------- 获取 lock B -> ok

    获取 lock B -> spin --------------- 获取 lock A -> spin

Previouslock_and_lockup_detectorNexthung_task

Last updated 4 years ago

Was this helpful?