LDD3学习笔记(8):并发和竞争

在计算机世界,百万分之一的事情会在几秒钟发生,并且问题的结果是严重的!

1、并发的管理

SMP(Symmetrical Multi-Processing)对称多处理机。

竞争情况来自对共享资源的存取。

存取管理的常用技术是加锁或者互斥。

2、旗标的互斥体

当一个进程到了无法做进一步处理的时候,它就去睡眠(阻塞),让出处理器给别人知道以后某个时间它能够再做其他事情。

旗标是一个单个整型值,结合一对函数,成为P和V,一个想要进入临界区的进程将在旗标上调用P,如果旗标的值大于0,这个值递减1并且进程继续,相反,如果旗标的值是0,进程必须等待直到别的进程释放旗标,解锁一个旗标通过调用V完成,这个函数递增旗标的值,如果需要,唤醒等待的进程。

旗标的使用需要包含<asm/semaphore.h>,相关类型是struct semaphore,直接创建一个旗标,使用sema_init来设定它:

Void sema_init(struct semaphore *sem,int val);

这里val是安排给旗标的初始值。

互斥锁模式的旗标:

DECLARE_MUTEX(name);

DECLARE_MUTEX_LOCKED(name);

这里name是一个旗标变量,前面是初始化为1,后面是初始化为0。

互斥锁在运行时初始化使用下面中的一个:

Void init_MUTEX(struct semaphore *sem);

Void init_MUTEX_LOCKED(struct semaphore *sem);

3、completions(实现)机制

completion 是任务使用的一个轻量级机

制: 允许一个线程告诉另一个线程工作已经完成。

completion 机制的典型使用是在模块退出时与内核线程的终止一起,当模块准备好被清理时, exit 函数告

知线程退出并且等待结束. 为此目的, 内核包含一个特殊的函数给线程使用:

void complete_and_exit(struct completion *c, long retval); 

4、自旋锁

一个自旋锁是一个互斥设备, 只能有 2 个值:"上锁"和"解锁". 它常常实现为一个整数值中的一个单个位. 想获取一个特殊锁的代码则测试相关的位. 如果锁是可用的, 这个"上锁"位被置位并且代码继续进入临界区. 相反, 如果这个锁已经被别人获得, 代码进入一个紧凑的循环中反复检查这个锁, 直到它变为可用. 这个循环就是自旋锁的"自旋"部分。

5、快速参考

本章已介绍了很多符号给并发的管理. 最重要的这些在此总结:

#include <asm/semaphore.h>

定义旗标和其上操作的包含文件.

DECLARE_MUTEX(name);

DECLARE_MUTEX_LOCKED(name);

2 个宏定义, 用来声明和初始化一个在互斥模式下使用的旗标.

void init_MUTEX(struct semaphore *sem);

void init_MUTEX_LOCKED(struct semaphore *sem);

这 2 函数用来在运行时初始化一个旗标.

void down(struct semaphore *sem);

int down_interruptible(struct semaphore *sem);

int down_trylock(struct semaphore *sem);

void up(struct semaphore *sem);

加锁和解锁旗标. down 使调用进程进入不可打断睡眠, 如果需要; down_interruptible, 相反, 可以被信号打断. down_trylock 不睡眠; 相反, 它立刻返回如果旗标不可用. 加锁旗标的代码必须最终使用 up 解锁它.

struct rw_semaphore;

init_rwsem(struct rw_semaphore *sem);

旗标的读者/写者版本和初始化它的函数.

void down_read(struct rw_semaphore *sem);

int down_read_trylock(struct rw_semaphore *sem);

void up_read(struct rw_semaphore *sem);

获得和释放对读者/写者旗标的读存取的函数.

void down_write(struct rw_semaphore *sem);

int down_write_trylock(struct rw_semaphore *sem);

void up_write(struct rw_semaphore *sem);

void downgrade_write(struct rw_semaphore *sem);

管理对读者/写者旗标写存取的函数.

#include <linux/completion.h>

DECLARE_COMPLETION(name);

init_completion(struct completion *c);

INIT_COMPLETION(struct completion c);

描述 Linux completion 机制的包含文件, 已经初始化 completion 的正常方法. 

INIT_COMPLETION 应当只用来重新初始化一个之前已经使用过的 completion. 

void wait_for_completion(struct completion *c);

等待一个 completion 事件发出.

void complete(struct completion *c);

void complete_all(struct completion *c);

发出一个 completion 事件. completion 唤醒, 最多, 一个等待着的线程, 而 complete_all 唤醒全

部等待者.

void complete_and_exit(struct completion *c, long retval);

通过调用 complete 来发出一个 completion 事件, 并且为当前线程调用 exit.

#include <linux/spinlock.h>

spinlock_t lock = SPIN_LOCK_UNLOCKED;

spin_lock_init(spinlock_t *lock);

定义自旋锁接口的包含文件, 以及初始化锁的 2 个方法.

void spin_lock(spinlock_t *lock);

void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);

void spin_lock_irq(spinlock_t *lock);

void spin_lock_bh(spinlock_t *lock);

加锁一个自旋锁的各种方法, 并且, 可能地, 禁止中断.

int spin_trylock(spinlock_t *lock);

int spin_trylock_bh(spinlock_t *lock);

上面函数的非自旋版本; 在获取锁失败时返回 0, 否则非零.

void spin_unlock(spinlock_t *lock);

void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags);

void spin_unlock_irq(spinlock_t *lock);

void spin_unlock_bh(spinlock_t *lock);

释放一个自旋锁的相应方法.

rwlock_t lock = RW_LOCK_UNLOCKED

rwlock_init(rwlock_t *lock);

初始化读者/写者锁的 2 个方法.

void read_lock(rwlock_t *lock);

void read_lock_irqsave(rwlock_t *lock, unsigned long flags);

void read_lock_irq(rwlock_t *lock);

void read_lock_bh(rwlock_t *lock);

获得一个读者/写者锁的读存取的函数.

void read_unlock(rwlock_t *lock);

void read_unlock_irqrestore(rwlock_t *lock, unsigned long flags);

void read_unlock_irq(rwlock_t *lock);

void read_unlock_bh(rwlock_t *lock);

释放一个读者/写者自旋锁的读存取.

void write_lock(rwlock_t *lock);

void write_lock_irqsave(rwlock_t *lock, unsigned long flags);

void write_lock_irq(rwlock_t *lock);

void write_lock_bh(rwlock_t *lock);

获得一个读者/写者锁的写存取的函数.

void write_unlock(rwlock_t *lock);

void write_unlock_irqrestore(rwlock_t *lock, unsigned long flags);

void write_unlock_irq(rwlock_t *lock);

void write_unlock_bh(rwlock_t *lock);

释放一个读者/写者自旋锁的写存取的函数.

#include <asm/atomic.h>

atomic_t v = ATOMIC_INIT(value);

void atomic_set(atomic_t *v, int i);

int atomic_read(atomic_t *v);

void atomic_add(int i, atomic_t *v);

void atomic_sub(int i, atomic_t *v);

void atomic_inc(atomic_t *v);

void atomic_dec(atomic_t *v);

int atomic_inc_and_test(atomic_t *v);

int atomic_dec_and_test(atomic_t *v);

int atomic_sub_and_test(int i, atomic_t *v);

int atomic_add_negative(int i, atomic_t *v);

int atomic_add_return(int i, atomic_t *v);

int atomic_sub_return(int i, atomic_t *v);

int atomic_inc_return(atomic_t *v);

int atomic_dec_return(atomic_t *v);

原子地存取整数变量. atomic_t 变量必须只通过这些函数存取.

#include <asm/bitops.h>

void set_bit(nr, void *addr);

void clear_bit(nr, void *addr);

void change_bit(nr, void *addr);

test_bit(nr, void *addr);

int test_and_set_bit(nr, void *addr);

int test_and_clear_bit(nr, void *addr);

int test_and_change_bit(nr, void *addr);

原子地存取位值; 它们可用做标志或者锁变量. 使用这些函数阻止任何与并发存取这个位相关的竞争情况.

#include <linux/seqlock.h>

seqlock_t lock = SEQLOCK_UNLOCKED;

seqlock_init(seqlock_t *lock);

定义 seqlock 的包含文件, 已经初始化它们的 2 个方法.

unsigned int read_seqbegin(seqlock_t *lock);

unsigned int read_seqbegin_irqsave(seqlock_t *lock, unsigned long flags);

int read_seqretry(seqlock_t *lock, unsigned int seq);

int read_seqretry_irqrestore(seqlock_t *lock, unsigned int seq, unsigned long flags);

获得一个 seqlock-保护 的资源的读权限的函数.

void write_seqlock(seqlock_t *lock);

void write_seqlock_irqsave(seqlock_t *lock, unsigned long flags);

void write_seqlock_irq(seqlock_t *lock);

void write_seqlock_bh(seqlock_t *lock);

获取一个 seqlock-保护的资源的写权限的函数.

void write_sequnlock(seqlock_t *lock);

void write_sequnlock_irqrestore(seqlock_t *lock, unsigned long flags);

void write_sequnlock_irq(seqlock_t *lock);

void write_sequnlock_bh(seqlock_t *lock);

释放一个 seqlock-保护的资源的写权限的函数.

#include <linux/rcupdate.h>

需要使用读取-拷贝-更新(RCU)机制的包含文件.

void rcu_read_lock;

void rcu_read_unlock;

获取对由 RCU 保护的资源的原子读权限的宏定义.

void call_rcu(struct rcu_head *head, void (*func)(void *arg), void *arg);

安排一个回调在所有处理器已经被调度以及一个 RCU-保护的资源可用被安全的释放之后运行.

 

时间: 2024-12-27 13:27:53

LDD3学习笔记(8):并发和竞争的相关文章

LDD3学习笔记(22):感想

 经过几天奋战终于将LDD3看完,应该是属于略看,看过之后的感想是:1.需要学习的东西还很多! 2.中文版的翻译真的不行. 3.内核真的不简单.

LDD3学习笔记(2):建立和运行模块

1.建立开发环境 在开始编写代码以及研究代码之前,你需要有一个开发环境,也就是一个linux系统.通常我们的做法是在windows下安装一个虚拟机软件,然后在虚拟机软件中安装一个linux系统的发行版本,在众多的发行版本中我推荐ubuntu,不过具体还要看个人爱好. 2.hello world模块 许多编程书籍都从hello world开始,下面的代码是一个完整的hello world模块.将下列代码输入hello.c文件中. #include <linux/init.h>#include &

LDD3学习笔记(10):延时和延后

 1.延后执行 设备驱动常常需要延后一段时间来执行一个特定片段的代码,常常允许硬件完成某个任务. 2.时间管理 #include <linux/param.h> HZ  HZ 符号指定了每秒产生的时钟嘀哒的数目. #include <linux/jiffies.h> volatile unsigned long jiffies; u64 jiffies_64; jiffies_64 变量每个时钟嘀哒时被递增; 因此, 它是每秒递增 HZ 次. 内核代码几乎常常引用  jiffies

LDD3学习笔记(3):字符驱动1

1.scull( Simple Character Utility for Loading  Localities)的设计 Scull是一个字符驱动,它操作一块内存区域,就好像它是一个设备,因此在以下的介绍中我们可以互换的使用设备和scull操作的内存区. 编写驱动的第一步是定义驱动将要提供给用户的功能. 模块实现的每种设备都被引用为一种类型. Scull源码实现: scull0到scull3四个设备,每个都由一个全局永久的内存区组成,全局表示如果设备被多次打开设备中含有的数据将由所有打开它的文

LDD3学习笔记(15):PCI驱动

 #include <linux/pci.h> 包含 PCI 寄存器的符号名和几个供应商和设备 ID 值的头文件. struct pci_dev; 表示内核中一个 PCI 设备的结构. struct pci_driver; 代表一个 PCI 驱动的结构. 所有的 PCI 驱动必须定义这个. struct pci_device_id; 描述这个驱动支持的 PCI 设备类型的结构. int pci_register_driver(struct pci_driver *drv); int pci_m

LDD3学习笔记(1):设备驱动简介

 1.几乎每个系统操作都被映射到具体的物理设备上,而任何设备的控制操作都由特定于要寻址的相关代码来进行,这些代码成为设备驱动. 2.linux可以在运行时扩展由内核提供的特性,既可以在系统运行时增加内核的功能(也可以删除). 3.每块可以在运行时添加到内核的代码称为一个模块,每个模块由目标代码组成(既功能的实现). 4.linux将设备分为三种基本类型:字符设备.块设备.网络设备.典型的字符设备如:/dev/tty1(串口),块设备如:/dev/sda(磁盘),网络设备如:eth0(网卡),在这

LDD3学习笔记(18):内存映射和DMA

  1.介绍性材料 #include <linux/mm.h> #include <asm/page.h> 和内存管理相关的大部分函数和结构, 原型和定义在这些头文件. void *__va(unsigned long physaddr); unsigned long __pa(void *kaddr); 在内核逻辑地址和物理地址之间转换的宏定义. PAGE_SIZE PAGE_SHIFT  常量, 给出底层硬件的页的大小(字节)和一个页面号必须被移位来转变为一个物理地址的位 数.

LDD3学习笔记(7):调试技术

  1.内核中的调试 开发中需要激活的配置选项 CONFIG_DEBUG_KERNEL CONFIG_DEBUG_SLAB CONFIG_DEBUG_PAGEALLOC CONFIG_DEBUG_SPINLOCK CONFIG_DEBUG_SPINLOCK_SLEEP CONFIG_INIT_DEBUG CONFIG_DEBUG_INFO CONFIG_MAGIC_SYSRQ CONFIG_DEBUG_STACKOVERFLOW CONFIG_DEBUG_STACK_USAGE CONFIG_K

LDD3学习笔记(16):USB驱动

  #include <linux/usb.h> 所有和 USB 相关的头文件. 它必须被所有的 USB 设备驱动包含. struct usb_driver; 描述 USB 驱动的结构. struct usb_device_id; 描述这个驱动支持的 USB 设备的结构. int usb_register(struct usb_driver *d); 用来从USB核心注册和注销一个 USB 驱动的函数. struct usb_device *interface_to_usbdev(struct