Linux 内核同步简介

版权声明:原创文章 && 转载请著名出处 https://blog.csdn.net/zhoutaopower/article/details/86586527

在使用共享内存的程序代码中,为了保护共享资源,放置共享资源的并发访问导致的错误逻辑,需要用到内核同步机制。

临界区

是访问和操作共享数据的代码段。多个执行线程并发访问同一个资源(读/写),通常是不安全的,为了避免这种隐患,编程人员必须保证这种访问是原子的(不可打断)。

造成并发的原因

造成并发有多种原因:

中断--------------------------------中断几乎能够在任何时刻异步的发生,随时可以打断当前的代码

软中断(含 tasklet)----------内核能够在任何时候唤醒或者调度软中断和 tasklet,打断当前的代码执行

内核抢占---------------------------内核具有抢占性,所以当前的任务可能被另外一个任务抢占

睡眠和用户空间同步------------内核执行的进程可能睡眠,会唤醒调度程序,从而导致调度一个新的程序执行

对称多处理器 (SMP)---------多个处理器同时执行代码

作为内核的开发者,必须熟练的了解上述可能导致并发的因素,并在编码的时候,时刻做好解决并发的工作。真正用所来保护资源并不困难,关键是需要正确的辨识出共享数据和临界区。在代码设计的初期就需要充分的考虑加锁,而不是时候补救。

解决并发的办法

加锁是解决并发的办法,即,在临界区加上一把锁,让先来的进程获取锁并开始执行,后来的进程获取不到锁,就无法执行,便起到了保护临界区的办法。而锁,是原子性的,不存在竞争。单一指令实现。

锁保护的是什么

锁住的是数据,而不是代码!锁是针对共享的数据访问的,针对的对象是数据。

任何可能被并发访问的代码几乎都需要保护。

执行线程的局部数据仅仅被他本身使用,不需要保护。比如局部变量不需要锁。

如果数据只会被特定的线程访问,也不需要锁(一个线程一次只能在一个 处理器上运行)

到底什么数据需要加锁呢?

大多数内核数据都需要加锁,如果有其他执行线程可以访问这些数据,那么需要给这些数据加上锁。

在写内核代码的时候,需要时刻清醒一下问题:

  • 这个数据是不是全局的?除了当前线程以外,其他线程能否访问它?
  • 这个数据会不会在进程上下文和中断上下文共享?它是不是要在两个不同的中断处理程序中共享?
  • 进程在访问数据的时候,内核是否处于可抢占?被调度的新程序会不会访问同一个数据?
  • 当前进程是不是会睡眠(阻塞)在某些资源上?如果是,会让共享数据处于什么状态?
  • 如果这个函数又在另外一个处理器上被调度执行,会怎么样?

死锁

当一个或者多个线程执行的时候,每个线程都在等待其他线程已经锁住的资源,会导致相互等待,进入死锁。

自死锁:如果一个线程试图去获取一个自己已经获取了的锁,它将死锁。因为这个锁已经被之前获取过,再次获取,只能进入无限的等待:

获得锁A

再次获取锁 A

等待.........................

同样道理,n个线程n个锁,如果处于相互等待,也会死锁:

线程 1                                            线程 2

获得锁 A                                        获得锁 B

试图获得锁 B                                 试图获取锁 A

等待锁 B..........                               等待锁 A ...............

预防死锁

预防死锁,几种法则:

1. 按顺序加锁。若使用多个嵌套的锁的时候,需要保证加锁顺序和获取锁的顺序一致

2. 防止发生饥饿。

3. 切勿重复请求同一个锁。

4. 锁的设计力求简单。

猜你喜欢

转载自blog.csdn.net/zhoutaopower/article/details/86586527