内核同步机制
在使用共享内存的程序代码中,为了保护共享资源,放置共享资源的并发访问导致的错误逻辑,需要用到内核同步机制。
1.临界区
是访问和操作共享数据的代码段。多个执行线程并发访问同一个资源(读/写),通常是不安全的,为了避免这种隐患,编程人员必须保证这种访问是原子的(不可打断)。
2.造成并发的原因
造成并发有多种原因:
中断--------------------------------中断几乎能够在任何时刻异步的发生,随时可以打断当前的代码
软中断(含 tasklet)----------内核能够在任何时候唤醒或者调度软中断和 tasklet,打断当前的代码执行
内核抢占---------------------------内核具有抢占性,所以当前的任务可能被另外一个任务抢占
睡眠和用户空间同步------------内核执行的进程可能睡眠,会唤醒调度程序,从而导致调度一个新的程序执行
对称多处理器 (SMP)---------多个处理器同时执行代码
作为内核的开发者,必须熟练的了解上述可能导致并发的因素,并在编码的时候,时刻做好解决并发的工作。真正用所来保护资源并不困难,关键是需要正确的辨识出共享数据和临界区。在代码设计的初期就需要充分的考虑加锁,而不是时候补救。
3.解决并发的办法
加锁是解决并发的办法,即,在临界区加上一把锁,让先来的进程获取锁并开始执行,后来的进程获取不到锁,就无法执行,便起到了保护临界区的办法。而锁,是原子性的,不存在竞争。单一指令实现。
4.锁保护的是什么
锁住的是数据,而不是代码!锁是针对共享的数据访问的,针对的对象是数据。
任何可能被并发访问的代码几乎都需要保护。
执行线程的局部数据仅仅被他本身使用,不需要保护。比如局部变量不需要锁。
如果数据只会被特定的线程访问,也不需要锁(一个线程一次只能在一个 处理器上运行)
5.到底什么数据需要加锁呢?
大多数内核数据都需要加锁,如果有其他执行线程可以访问这些数据,那么需要给这些数据加上锁。
6.在写内核代码的时候,需要时刻清醒一下问题:
这个数据是不是全局的?除了当前线程以外,其他线程能否访问它?
这个数据会不会在进程上下文和中断上下文共享?它是不是要在两个不同的中断处理程序中共享?
进程在访问数据的时候,内核是否处于可抢占?被调度的新程序会不会访问同一个数据?
当前进程是不是会睡眠(阻塞)在某些资源上?如果是,会让共享数据处于什么状态?
如果这个函数又在另外一个处理器上被调度执行,会怎么样?
7.死锁
当一个或者多个线程执行的时候,每个线程都在等待其他线程已经锁住的资源,会导致相互等待,进入死锁。
自死锁:如果一个线程试图去获取一个自己已经获取了的锁,它将死锁。因为这个锁已经被之前获取过,再次获取,只能进入无限的等待:
获得锁A
再次获取锁 A
等待.........................
同样道理,n个线程n个锁,如果处于相互等待,也会死锁:
线程 1 线程 2
获得锁 A 获得锁 B
试图获得锁 B 试图获取锁 A
等待锁 B.......... 等待锁 A ...............
8.预防死锁
预防死锁,几种法则:
-
按顺序加锁。若使用多个嵌套的锁的时候,需要保证加锁顺序和获取锁的顺序一致
-
防止发生饥饿。
-
切勿重复请求同一个锁。
-
锁的设计力求简单。
原文链接:https://blog.csdn.net/zhoutaopower/article/details/86586527
标签:同步,访问,并发,死锁,加锁,线程,内核,linux From: https://www.cnblogs.com/axjlxy/p/18052319