第5章 并发和竞态[高教书苑]_第1页
第5章 并发和竞态[高教书苑]_第2页
第5章 并发和竞态[高教书苑]_第3页
第5章 并发和竞态[高教书苑]_第4页
第5章 并发和竞态[高教书苑]_第5页
已阅读5页,还剩47页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、1 第第5 章章 并发和竞态并发和竞态 原子操作原子操作 自旋锁自旋锁 信号量信号量 scull的缺陷的缺陷 在在scull中使用信号量中使用信号量 2 原子操作原子操作 考虑将整数变量加考虑将整数变量加1的操作的操作i+怎么实现?怎么实现? 得到当前变量得到当前变量i的值并且拷贝到一个寄存器中的值并且拷贝到一个寄存器中 将寄存器中的值加将寄存器中的值加1 把把i的新值写回到内存中的新值写回到内存中 设设i为全局变量,初始值为为全局变量,初始值为7,有两个线程欲对其施行,有两个线程欲对其施行 i+操作,结果会怎样?操作,结果会怎样? 期望的结果!期望的结果! Thread 1 get i (7

2、) increment i (7-8) write back i (8) - - - Thread 2 - - - get i (8) increment i (8-9) write back i (9) 3 可能出现的结果!可能出现的结果! 若能把读、加与写回指令作为一个不可分割的整体执行,也能若能把读、加与写回指令作为一个不可分割的整体执行,也能 得到正确结果得到正确结果 错误的结果!错误的结果! 原子操作原子操作 Thread 1 get i (7) increment i (7-8) - write back i (8) - Thread 2 get i (7) - increment

3、 i (7-8) - write back i (8) Thread 1 get /* define v */ atomic_t u = ATOMIC_INIT(0); /* define u and initialize it to zero */ atomic_set( /* v = 4 (atomically) */ atomic_add(2, /* v = v + 2 = 6 (atomically) */ atomic_inc( /* v = v + 1 = 7 (atomically) */ int atomic_dec_and_test(atomic_t *v) 6 原子操作原子

4、操作 Atomic Integer OperationDescription ATOMIC_INIT(int i)At declaration, initialize to i. int atomic_read(atomic_t *v)Atomically read the integer value of v. void atomic_set(atomic_t *v, int i)Atomically set v equal to i. void atomic_add(int i, atomic_t *v)Atomically add i to v. void atomic_sub(int

5、i, atomic_t *v)Atomically subtract i from v. void atomic_inc(atomic_t *v)Atomically add one to v. void atomic_dec(atomic_t *v)Atomically subtract one from v. int atomic_sub_and_test(int i, atomic_t *v) Atomically subtract i from v and return true if the result is zero; otherwise false. 原子操作接口列表原子操作接

6、口列表 7 原子操作原子操作 int atomic_add_negative(int i, atomic_t *v) Atomically add i to v and return true if the result is negative; otherwise false. int atomic_add_return(int i, atomic_t *v) Atomically add i to v and return the result int atomic_sub_return(int i, atomic_t *v) Atomically subtract i from v an

7、dreturn the result. int atomic_inc_return(int i, atomic_t *v) Atomically increment v by one and return the result. int atomic_dec_return(int i, atomic_t *v) Atomically decrement v by one and return the result. int atomic_dec_and_test(atomic_t *v) Atomically decrement v by one and return true if zero

8、; false otherwise. int atomic_inc_and_test(atomic_t *v) Atomically increment v by one and return true if the result is zero; false otherwise. 8 原子操作原子操作 2. 原子位操作原子位操作 atomic_t 类型在进行整数算术操作时是不错的类型在进行整数算术操作时是不错的. 但是但是 当你需要以原子方式操作单个位时当你需要以原子方式操作单个位时, 它无法工作。它无法工作。 为为 此此, 内核提供了一套函数来原子地修改或测试单个位,内核提供了一套函数来原

9、子地修改或测试单个位, 整个操作发生在单步内整个操作发生在单步内, 没有中断没有中断(或者其他处理器或者其他处理器)能能 干扰干扰 原子位操作执行很快原子位操作执行很快, 因为它们使用因为它们使用单条机器指令单条机器指令来进来进 行操作行操作, 而在任何低层平台做的时候不用禁止中断而在任何低层平台做的时候不用禁止中断 位操作函数在位操作函数在 中声明中声明 位操作是对内存地址进行的操作,参数是一个指针和位操作是对内存地址进行的操作,参数是一个指针和 一个位号,指针指向内存地址,位号指明操作的位一个位号,指针指向内存地址,位号指明操作的位 9 原子操作原子操作 位操作接口:位操作接口: void

10、 set_bit(nr, void *addr); 设置设置 addr 指向的数据项的第指向的数据项的第 nr 位位. void clear_bit(nr, void *addr); 清除清除 addr 指向的数据项的第指向的数据项的第nr位位. void change_bit(nr, void *addr); 翻转指定的位翻转指定的位. 10 原子操作原子操作 test_bit(nr, void *addr); 返回指定位的当前值返回指定位的当前值. int test_and_set_bit(nr, void *addr); 设置指定的位,并返回其原先的值设置指定的位,并返回其原先的值 in

11、t test_and_clear_bit(nr, void *addr); 清除指定的位,并返回其原先的值清除指定的位,并返回其原先的值 int test_and_change_bit(nr, void *addr); 翻转指定的位,并返回其原先的值翻转指定的位,并返回其原先的值 11 原子操作原子操作 例例 可以使用位操作来管理一个锁变量以控制对某个可以使用位操作来管理一个锁变量以控制对某个 共享数据项的访问。假定这个位是共享数据项的访问。假定这个位是 0时锁空闲时锁空闲,非零非零 时锁忙。时锁忙。 while (test_and_set_bit(nr, addr) != 0) wait_f

12、or_a_while(); if (test_and_clear_bit(nr, addr) = 0) something_went_wrong(); 共享数据项共享数据项 12 第第5 章章 并发和竞态并发和竞态 原子操作原子操作 自旋锁自旋锁 信号量信号量 scull的缺陷的缺陷 在在scull中使用信号量中使用信号量 13 自旋锁自旋锁 自旋锁是一个互斥设备自旋锁是一个互斥设备, 它只能有它只能有 2 个值个值:“上锁上锁”和和 “解锁解锁”. 它通常实现为某个整数值中的单个位它通常实现为某个整数值中的单个位. 希希 望获取特定锁的代码测试相关的位,望获取特定锁的代码测试相关的位, 如果

13、锁是可用如果锁是可用 的的, 这个这个“上锁上锁”位被置位并且代码继续进入临界区位被置位并且代码继续进入临界区 ;相反;相反, 如果这个锁已经被别人获得如果这个锁已经被别人获得, 代码进入一个代码进入一个 忙循环中反复检查这个锁忙循环中反复检查这个锁, 直到它变为可用直到它变为可用. 这个循这个循 环就是自旋锁的环就是自旋锁的“自旋自旋”部分。自旋意味着部分。自旋意味着“抱着抱着 CPU空转空转” 也可不也可不“自旋自旋”,即让出即让出CPU,自已睡眠自已睡眠 等待等待,钥匙放出来时让别人唤醒。此即钥匙放出来时让别人唤醒。此即 为为“信号量信号量”,其可被称为其可被称为“睡眠锁睡眠锁” 自旋锁

14、与信号量孰优孰劣?自旋锁与信号量孰优孰劣? 短时用自旋锁,长时用信号量短时用自旋锁,长时用信号量 P5 ? 14 自旋锁自旋锁 在单处理器系统上,若停用内核抢占,自旋锁定义为在单处理器系统上,若停用内核抢占,自旋锁定义为 空操作,因为不存在几个空操作,因为不存在几个CPU同时进入临界区的情同时进入临界区的情 况;若启用了内核抢占,就跟况;若启用了内核抢占,就跟SMP系统类似,可能系统类似,可能 存在多个内核控制路径同时进入临界区的情况,自旋存在多个内核控制路径同时进入临界区的情况,自旋 锁不能定义为空操作,但锁不能定义为空操作,但spin_lock(基本上)等价于基本上)等价于 preempt

15、_disable,而,而spin_unlock则等价于则等价于 preempt_enable 自旋锁原语要求头文件自旋锁原语要求头文件 . 数据类数据类 型为型为 spinlock_t, 15 自旋锁自旋锁 与其他数据结构一样与其他数据结构一样, 自旋锁必须初始化。自旋锁必须初始化。 可以在编译时完成可以在编译时完成 (静态初如化)(静态初如化), 也可以在运行时完成(动态初始化)。也可以在运行时完成(动态初始化)。 静态初始化静态初始化 spinlock_t my_lock = SPIN_LOCK_UNLOCKED; 动态初始化动态初始化 void spin_lock_init(spinlo

16、ck_t *lock); 在进入一个临界区前在进入一个临界区前, 你的代码必须获得需要的锁你的代码必须获得需要的锁: void spin_lock(spinlock_t *lock); 为释放一个已获得的锁为释放一个已获得的锁, 必须调用下面函数必须调用下面函数: void spin_unlock(spinlock_t *lock); 16 自旋锁自旋锁 自旋锁的使用自旋锁的使用 spinlock_t mr_lock = SPIN_LOCK_UNLOCKED; spin_lock( /* critical region */ spin_unlock( 自旋锁使用规则自旋锁使用规则 拥有自旋锁的

17、代码必须是原子的拥有自旋锁的代码必须是原子的 拥有自旋锁时不能够睡眠拥有自旋锁时不能够睡眠 在中断处理程序中可使用自旋锁,但拥有前必须禁止在中断处理程序中可使用自旋锁,但拥有前必须禁止 本地中断本地中断 拥有自旋锁的时间必须尽可能短拥有自旋锁的时间必须尽可能短 17 自旋锁自旋锁 内核提供了获取锁前禁止中断的接口内核提供了获取锁前禁止中断的接口 接口一,禁止中断、保存中断当前状态、获取锁接口一,禁止中断、保存中断当前状态、获取锁 spinlock_t mr_lock = SPIN_LOCK_UNLOCKED; unsigned long flags; spin_lock_irqsave( /*

18、 critical region . */ spin_unlock_irqrestore( 接口二,禁止中断、获取锁(适用于中断打开的情况)接口二,禁止中断、获取锁(适用于中断打开的情况) spinlock_t mr_lock = SPIN_LOCK_UNLOCKED; spin_lock_irq( /* critical section . */ spin_unlock_irq( 18 自旋锁自旋锁 自旋锁函自旋锁函 void spin_lock(spinlock_t *lock); void spin_lock_irqsave(spinlock_t *lock, unsigned long

19、 flags); void spin_lock_irq(spinlock_t *lock); void spin_lock_bh(spinlock_t *lock) ;获得锁前禁止软中,硬;获得锁前禁止软中,硬 中保开中保开 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); 19 自

20、旋锁自旋锁 读读-写自旋锁写自旋锁 对于读、写可分开的场合,内核提供了读对于读、写可分开的场合,内核提供了读-写锁,允许写锁,允许 任意数量的读者同时进入临界区,但写者仍必须互斥任意数量的读者同时进入临界区,但写者仍必须互斥 访问临界区,类型为访问临界区,类型为 rwlock_t,定义在,定义在 中。中。为何要造这种锁?为何要造这种锁? 初始化初始化 静态静态 rwlock_t my_rwlock = RW_LOCK_UNLOCKED; 动态动态 rwlock_t my_rwlock; rwlock_init( 20 自旋锁自旋锁 读读-写自旋锁函数写自旋锁函数 void read_lock(

21、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_unl

22、ock_bh(rwlock_t *lock); 21 自旋锁自旋锁 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); int write_trylock(rwlock_t *lock); void write_unlock(rwlock_t *lock); void write_unlock_irqrestore

23、(rwlock_t *lock, unsigned long flags); void write_unlock_irq(rwlock_t *lock); void write_unlock_bh(rwlock_t *lock); 22 自旋锁自旋锁 顺序锁顺序锁seqlock 读读/写锁中,执行写锁中,执行read_lock的读者与执行的读者与执行write_lock 的写者具有相同的优先级:读者必须等待,直到写操的写者具有相同的优先级:读者必须等待,直到写操 作完成;同样,写者也必须等待,直到读操作完成。作完成;同样,写者也必须等待,直到读操作完成。 若读者多,写者得长时等待。若读者多,写

24、者得长时等待。 顺序锁中赋予写者较高的优先级:即使读者正在读的顺序锁中赋予写者较高的优先级:即使读者正在读的 时候也允许写者继续运行时候也允许写者继续运行 顺序锁定义在顺序锁定义在 中,数据类型为中,数据类型为 seqlock_t,包括两个字段:一个类型为,包括两个字段:一个类型为spinlock_t的的 lock字段与一个整型的字段与一个整型的sequence字段字段顺序计数器顺序计数器 typedef struct unsigned sequence; spinlock_t lock; seqlock_t; 23 写者进入临界区时通过写者进入临界区时通过write_seqlock()获取锁

25、,同时使顺序()获取锁,同时使顺序 计数器加计数器加1;退出时通过;退出时通过write_seqlock()释放锁,同时再使顺序释放锁,同时再使顺序 计数器加计数器加1 读者在进入读者在进入/退出临界区时均要读取顺计器的值,若两次读到的退出临界区时均要读取顺计器的值,若两次读到的 值不同或为奇数,说明读操作中有写者进入,因而数据无效,必值不同或为奇数,说明读操作中有写者进入,因而数据无效,必 须再次读取须再次读取 读者代码有如下面的形式读者代码有如下面的形式: unsigned int seq; do seq = read_seqbegin( /* Do what you need to do

26、 */ while read_seqretry( static _always_inline int read_seqretry(const seqlock_t *sl, unsigned start) smp_rmb(); return (sl-sequence != start); 24 自旋锁自旋锁 写者必须获取一个排他锁来进入由一个写者必须获取一个排他锁来进入由一个 seqlock 保护保护 的临界区的临界区. 为此为此, 调用调用: void write_seqlock(seqlock_t *lock); 写锁由一个自旋锁实现写锁由一个自旋锁实现, 因此所有的通常的限制都适用因此所有

27、的通常的限制都适用 调用下面函数来释放锁调用下面函数来释放锁: void write_sequnlock(seqlock_t *lock); 25 自旋锁自旋锁 seqlock 适宜要保护的资源小、简单、读多、写少的适宜要保护的资源小、简单、读多、写少的 场合。场合。 seqlock 用用 2 种通常的方法来初始化种通常的方法来初始化: 静态初始化静态初始化 seqlock_t lock1 = SEQLOCK_UNLOCKED; 动态初始化:动态初始化: seqlock_t lock2; seqlock_init( 26 自旋锁自旋锁 其他顺序锁函数其他顺序锁函数 void write_seq

28、lock_irqsave(seqlock_t *lock, unsigned long flags); void write_seqlock_irq(seqlock_t *lock); void write_seqlock_bh(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); unsigned in

29、t read_seqbegin_irqsave(seqlock_t *lock, unsigned long flags); int read_seqretry_irqrestore(seqlock_t *lock, unsigned int seq, unsigned long flags); 27 第第5 章章 并发和竞态并发和竞态 原子操作原子操作 自旋锁自旋锁 信号量信号量 scull的缺陷的缺陷 在在scull中使用信号量中使用信号量 28 信号量信号量 Linux 中的信号量是一种睡眠锁,如果一个任务试图中的信号量是一种睡眠锁,如果一个任务试图 获得一个已经被占用的信号量时,信号量

30、会将其推进获得一个已经被占用的信号量时,信号量会将其推进 一个等待队列,然后让其睡眠一个等待队列,然后让其睡眠 中断处理程序中不能使用信号量中断处理程序中不能使用信号量 信号量定义在信号量定义在,类型是,类型是 struct semaphore 信号量初始化信号量初始化 void sema_init(struct semaphore *sem, int val); val=1时为二值信号量、互斥体,类似锁时为二值信号量、互斥体,类似锁 val=0时同上,但初始状态为锁定时同上,但初始状态为锁定 val1时,信号量数量时,信号量数量 29 信号量信号量 互斥体的另一种初始化(静态)互斥体的另一种

31、初始化(静态) DECLARE_MUTEX(name); 1 DECLARE_MUTEX_LOCKED(name);0 互斥体的动态初始化互斥体的动态初始化 void init_MUTEX(struct semaphore *sem); void init_MUTEX_LOCKED(struct semaphore *sem); 信号量的申请信号量的申请 void down(struct semaphore *sem); int down_interruptible(struct semaphore *sem);中断返非中断返非0 int down_trylock(struct semapho

32、re *sem); 不休眠不休眠,返非返非0 30 信号量信号量 信号量的释放信号量的释放 void up(struct semaphore *sem); 信号量的使用信号量的使用 /* 定义并申明一个信号量,名为定义并申明一个信号量,名为mr_sem,初始值为初始值为1 */ static DECLARE_MUTEX(mr_sem); if (down_interruptible( 31 信号量信号量 读读-写信号量写信号量 允许多个读者拥有该信号量,但写者有更高优先权:允许多个读者拥有该信号量,但写者有更高优先权: 当一个写者进入临界区时当一个写者进入临界区时, 就不会允许读者进入直到写就

33、不会允许读者进入直到写 者完成了它们的工作。如果有大量的写者竞争该信号者完成了它们的工作。如果有大量的写者竞争该信号 量,则这种实现可能导致读者量,则这种实现可能导致读者“饿死饿死” ,长时间拒绝,长时间拒绝 读者访问。读者访问。 为此为此, 最好用在写者少且占用时间短的场最好用在写者少且占用时间短的场 合。合。 定义在定义在,数据类型,数据类型struct rw_semaphore 初始化初始化 void init_rwsem(struct rw_semaphore *sem); 32 信号量信号量 只读访问接口只读访问接口 void down_read(struct rw_semaphor

34、e *sem); int down_read_trylock(struct rw_semaphore *sem); void up_read(struct rw_semaphore *sem); 33 信号量信号量 写者接口写者接口 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); 3

35、4 信号量信号量 读读-写信号量使用写信号量使用 static DECLARE_RWSEM(mr_rwsem);static DECLARE_RWSEM(mr_rwsem); down_read( down_read( / /* * critical region (read only) . critical region (read only) . * */ / up_read( up_read( / /* * . . * */ / down_write( down_write( / /* * critical region (read and write) . critical regio

36、n (read and write) . * */ / up_write( up_write( 35 完成变量完成变量completion 如果在内核中一个任务需要发出信号通知另一个任务如果在内核中一个任务需要发出信号通知另一个任务 发生了某个特定事件,利用完成变量是使两个任务得发生了某个特定事件,利用完成变量是使两个任务得 以同步的简单方法。如果一个任务要执行一些工作时以同步的简单方法。如果一个任务要执行一些工作时 ,另一个任务就会在完成变量上等待。当这个任务完,另一个任务就会在完成变量上等待。当这个任务完 成工作后,会使用完成变量去唤醒在等待的任务。成工作后,会使用完成变量去唤醒在等待的任

37、务。 定义在定义在,类型,类型struct completion 初始化初始化 DECLARE_COMPLETION(mr_comp); init_completion() ; 36 第第5 章章 并发和竞态并发和竞态 原子操作原子操作 自旋锁自旋锁 信号量信号量 scull的缺陷的缺陷 在在scull中使用信号量中使用信号量 37 Scull的缺陷的缺陷 Scull_write中设有中设有A、B两进程同时到达下面的两进程同时到达下面的if语句语句 if (!dptr-datas_pos) dptr-datas_pos = kmalloc(quantum, GFP_KERNEL); if (!

38、dptr-datas_pos) goto out; struct scull_qset void *data; struct scull_qset *next; ; 如果如果dptr-datas_pos=NULL, 两者都会申请分配内存,结果都赋给两者都会申请分配内存,结果都赋给 dptr,后者将覆盖前者,前者分配到,后者将覆盖前者,前者分配到 的内存将丢失,造成内存泄漏的内存将丢失,造成内存泄漏 struct scull_qset *dptr 38 Scull的缺陷的缺陷 Scull中用什么工具来保护临界区?中用什么工具来保护临界区? 信号量?信号量? 自旋锁?自旋锁? scull只适合用信

39、号量只适合用信号量 struct scull_dev struct scull_qset *data; /* Pointer to first quantum set */ int quantum; /* the current quantum size */ int qset; /* the current array size */ unsigned long size; /* amount of data stored here */ unsigned int access_key; /* used by sculluid and scullpriv */ struct semaphor

40、e sem; /* mutual exclusion semaphore */ struct cdev cdev; /* Char device structure */ ; 39 第第5 章章 并发和竞态并发和竞态 原子操作原子操作 自旋锁自旋锁 信号量信号量 scull的缺陷的缺陷 在在scull中使用信号量中使用信号量 40 在在scull中使用信号中使用信号 信号量在使用前必须初始化。信号量在使用前必须初始化。 scull 在加载时进行这在加载时进行这 个初始化个初始化: for (i = 0; i sem) return -ERESTARTSYS; if (!dptr-datas_p

41、os) dptr-datas_pos = kmalloc(quantum, GFP_KERNEL); if (!dptr-datas_pos) goto out; out: up( return retval; 42 快速参考快速参考 #include DECLARE_MUTEX(name); DECLARE_MUTEX_LOCKED(name); void init_MUTEX(struct semaphore *sem); void init_MUTEX_LOCKED(struct semaphore *sem); void down(struct semaphore *sem); int

42、 down_interruptible(struct semaphore *sem); int down_trylock(struct semaphore *sem); void up(struct semaphore *sem); struct rw_semaphore; init_rwsem(struct rw_semaphore *sem); 43 快速参考快速参考 void down_read(struct rw_semaphore *sem); int down_read_trylock(struct rw_semaphore *sem); void up_read(struct r

43、w_semaphore *sem); void down_write(struct rw_semaphore *sem); int down_write_trylock(struct rw_semaphore *sem); void up_write(struct rw_semaphore *sem); #include DECLARE_COMPLETION(name); init_completion(struct completion *c); INIT_COMPLETION(struct completion c); 44 快速参考快速参考 void wait_for_completio

44、n(struct completion *c); void complete(struct completion *c); void complete_all(struct completion *c); void complete_and_exit(struct completion *c, long retval); #include spinlock_t lock = SPIN_LOCK_UNLOCKED; spin_lock_init(spinlock_t *lock); void spin_lock(spinlock_t *lock); void spin_lock_irqsave(

45、spinlock_t *lock, unsigned long flags); void spin_lock_irq(spinlock_t *lock); void spin_lock_bh(spinlock_t *lock); 45 快速参考快速参考 int spin_trylock(spinlock_t *lock); int spin_trylock_bh(spinlock_t *lock); void spin_unlock(spinlock_t *lock); void spin_unlock_irqrestore(spinlock_t *lock, unsigned long fl

46、ags); 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); 46 快速参考快速参考 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_lo

47、ck_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); 47 快速参考快速参考 void write_lock(rwlock_t *lock); void write_lock_irqsave(rwlock_t *lock, unsigned long fla

48、gs); 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); 48 快速参考快速参考 #include atomic_t v = ATOMIC_IN

49、IT(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_s

50、ub_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); 49 快速参考快速参考 #include 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); 50 快速参考快速参考 #include seqlo

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论