内核同步技术二
来源:互联网 发布:软件著作权 技术入股 编辑:程序博客网 时间:2024/05/21 19:42
序言
在前面,我们介绍了常用两种内核同步技术:自旋锁和信号量。这里我们接着介绍其他的内核同步技术。
内核同步技术
前面我们提到,信号量是基于原子操作的,它的信号初始值count是一个原子类型,下面我们就对它做详细的介绍,同时我们还会讲解其他的内核同步技术。
- 原子操作
- 原子整数操作
// 大多数平台定义(ARM、MIPS,...)
typedef struct { volatile int counter; } atomic_t;
// x86平台定义
typedef struct { int counter; } atomic_t;
我们先来看一个使用原子操作的一个例子,我们有一个数据data,因为有多个进程在使用它,因此对它的访问必须是原子的,要么完成,要么不完成,而不会操作一半。我们使用原子操作来实现它:
#include <asm/atomic.h>
// A 进程初始化数据位0
atomic_t data;
data = ATOMIC_INIT(0);
// B进程需要设置它的数据为 6
atomic_set(&data);
// C进程每次操作会把数据增加10
atomic_add(10, &data);
// D进程每次操作会自动把数据递增1
atomic_inc(&data);
#define ATOMIC_INIT(i);
#define atomic_read(v);
#define atomic_set(v,i);
static __inline__ void atomic_add(int i, atomic_t *v);
static __inline__ void atomic_inc(atomic_t *v);
static __inline__ void atomic_sub(int i, atomic_t *v);
static __inline__ void atomic_dec(atomic_t *v);
static __inline__ int atomic_inc_and_test(atomic_t *v)
static __inline__ int atomic_sub_and_test(int i, atomic_t *v);
static __inline__ int atomic_dec_and_test(atomic_t *v);
static __inline__ int atomic_add_negative(int i, atomic_t *v);
static __inline__ int atomic_add_return(int i, atomic_t *v);
static __inline__ int atomic_sub_return(int i, atomic_t *v);
static __inline__ int atomic_add_unless(atomic_t *v, int a, int u);
#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
#define atomic_inc_return(v) (atomic_add_return(1,v))
#define atomic_dec_return(v) (atomic_sub_return(1,v))
- 原子位操作
由于原子位操作的操作对象仅仅是普通的内存地址,因此它的使用就有很大的灵活性,你可以在任何地址上使用原子位操作,以下是原子位操作的函数列表:
static inline void set_bit(int nr, volatile unsigned long * addr);
static inline void clear_bit(int nr, volatile unsigned long * addr);
static inline void clear_bit_unlock(unsigned long nr, volatile unsigned long *addr);
static inline void change_bit(int nr, volatile unsigned long * addr);
static inline int test_and_set_bit(int nr, volatile unsigned long * addr);
static inline int test_and_set_bit_lock(int nr, volatile unsigned long *addr);
static inline int test_and_clear_bit(int nr, volatile unsigned long * addr);
static inline int test_and_change_bit(int nr, volatile unsigned long* addr);
static inline int find_first_zero_bit(const unsigned long *addr, unsigned size);
int find_next_zero_bit(const unsigned long *addr, int size, int offset);;
static inline unsigned find_first_bit(const unsigned long *addr, unsigned size);
static inline unsigned long ffz(unsigned long word);
static inline int ffs(int x);
static inline int fls(int x);
- 内存屏蔽指令
为了解决这个问题,Linux提供了内存屏蔽指令。插入内存屏蔽指令可以保证指令前后的执行顺序不会改变。根据平台的不同,内存屏蔽指令被定义在<asm/system.h>或<asm/barrier.h>头文件中。内存屏蔽指令API定义如下:
#include <asm/system.h>
#define mb()
#define rmb()
#define wmb()
#define smp_mb()
#define smp_rmb()
#define smp_wmb()
内存屏蔽指令的使用很简单,知需要在需要屏蔽的两条指令之间加上内存屏蔽指令即可。
- 完成变量
目前来说我们还无法用前面介绍的技术来解决这个问题。还好,内核提供了完成变量(completions)这种技术,它是基于等待队列的一种技术(关于等待队列,我们会在后面Linux的等待队列一章在详细讲解)。
完成变量对象是一个completion结构,它定义在<linux/completion.h>头文件中,显然它是一个平台无关的技术。完成变量的API也很简单,初始化、等待、完成通知,如下表:
#include <linux/completion.h>
struct completion {
unsigned int done;
wait_queue_head_t wait;
};
DECLARE_COMPLETION(work);
static inline void init_completion(struct completion *x);
void wait_for_completion(struct completion *);
int wait_for_completion_interruptible(struct completion *x);
unsigned long wait_for_completion_timeout(struct completion *x,
unsigned long timeout);
unsigned long wait_for_completion_interruptible_timeout(
struct completion *x, unsigned long timeout);
extern void complete(struct completion *);
extern void complete_all(struct completion *);
- 使用完成变量
- 大内核锁(BKL)
它的使用很简单:
lock_kernel();
...
unlock_kernel()
如果系统配置为支持可抢占的大内核锁,则它的实现时基于互斥信号量的,如果不是,则它的实现时基于自旋锁的。
由于内核不在推荐使用大内核锁,尽管它很简单,也不要在你的代码里使用大内核锁。
- seq锁
seq锁定义在<linux/seqlock.h>头文件中,它是平台无关的,下面时它的API接口。
#include <linux/seqlock.h>
SEQLOCK_UNLOCKED
DEFINE_SEQLOCK(x);
static inline void write_seqlock(seqlock_t *);
static inline void write_sequnlock(seqlock_t *);
static __always_inline unsigned read_seqbegin(const seqlock_t *);
static __always_inline int read_seqretry(const seqlock_t *, unsigned );
// 初始化
seqlock_t seq_lock = SEQLOCK_UNLOCKED;
// 写进程写操作
write_seqlock(&seq_lock);
......
write_sequnlock(&seq_lock);
// 读进程读操作
unsigned long seq;
do {
seq = read_seqbegin(&seq_lock);
...... // 读数据
}while (read_seqretry(&seq_lock, seq));
seq锁是写优先锁,只要没有其他写进程持有该锁,它总能被成功持有。只要有写进程持有该锁,读进程就会不断的循环读数据,直到写进程释放该锁。
后记
到现在为止我们已经介绍了内核中驱动程序常用的同步操作技术。在后面的例子中,我们就会用到内核同步技术来完善我们的“hello world”驱动程序支持多同步操作。现在来看看我们的驱动程序,它已经支持读写数据了,不过好像是少了什么功能(好好想想)?是设备控制,我们还无法通过应用程序来控制我们的“Hello World”设备。在这里我们先透露一点就是,Linux中设备的控制接口就是ioctl,在下一章我们将扩展我们的驱动来支持设备控制接口。
- 内核同步技术二
- 内核同步技术
- 内核同步方法二
- Java同步技术(二)
- 线程同步技术(二)
- Java同步技术(二)
- Java同步技术(二)
- (转)线程同步技术(二)
- Linux驱动程序开发005 - 内核同步技术
- Linux驱动程序开发 005- 内核同步技术
- Linux驱动程序开发 006- 内核同步技术
- 内核中的同步问题(二)
- 内核中的同步机制(二)
- 线程同步技术二:Mutex的使用
- C++windows内核编程笔记day14 其他线程同步技术
- 内核同步
- 内核同步
- 内核同步
- BREW的MIF编辑器学习
- PHP 制作通讯录(六)
- Nightmare hdu 1072 bfs (可以往回走的bfs,hash有点帅)
- 敏捷开发宣言
- unbuntu 安装打开 rar文件
- 内核同步技术二
- (转)UDP协议的两个主要方法sendto和recvfrom详解
- BREW的resource编辑器学习
- LTP测试流程及分析
- BREW的基本机制小结
- SqlCommand对象
- Spring初始了解笔记
- BREW开发俄罗斯方块(一)——设计
- sls ~简单的逻辑服务器