并发和竞态

竞态:假设有两个进程(A和B),正在独立的尝试向同一片共享的数据进行操作,例如下面的示例代码中,假设在同一时间两个进程都到到了if判断的位置,那么两个进程都会分配内存,而每个进程都会将结果指针赋值给dptr->data[s_pos]。最终的结果就是谁后赋值谁会赋值成功,假设B赋值成功,那么A分配的内存将失去管理,造成内存泄露。

if (!dptr->data[s_pos]) {
	dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
	if (!dptr->data[s_pos])
		goto out;
}

 

信号量和互斥体

内核中信号量的逻辑和应用层的是一致的,分为P操作和V操作,P操作信号量减一(即等待信号),V操作信号量加一(即发送信号)
信号量包含的头文件<asm/semaphore.h>,相关的类型是struct semaphore;
创建信号量的函数为:void sema_init(struct semaphore *sem, int val);其中val是赋予信号量的初始值。

因为信号量通常被用于互斥模式。为了方便,内核提供了一组辅助的函数和宏,如下:
DECLARE_MUTEX(name); 将名称为name的信号量初始化为1。
DECLARE_MUTEX_LOCKED(name); 将名称为name的信号量初始化为0。
如果需要运行时初始化,运用下面的函数:
void init_MUTEX(struct semaphore *sem); 信号量初始化为1。
void init_METEX_LOCKED(struct semaphore *sem); 信号量初始化为0。

P操作函数:
void down(struct semaphore *sem);一直等待下去,直至信号到来。
int down_interruptible(struct semaphore *sem);也是一直等待下去,但是操作是可中断的,这个版本几乎是始终要使用的版本,它允许在等待某个信号量上的用户空间进程可以被用户终端。如果操作被中断,函数会返回非0值,调用者不会再拥有该信号量,所以使用的时候需要始终检查返回值。
int down_trylock(struct semaphore *sem);在无信号的时候,会立即返回。

V操作函数
void up(struct semaphore *sem);

注意:信号量不能在不能休眠的上下文中使用,例如中断上下文
 

完成量

完成量也是一种信号量,它适用的场景是,当某个线程需要等待另一个线程先执行完成才能进行的时候,适用完成量。包含头文件<linux/completion.h>。
静态创建:DECLARE_COMPLETION(my_completion);
动态创建:struct completion my_completion; init_completion(&my_completion);
等待完成量:void wait_for_completion(struct completion *c); (注意这是一个非中断的等待)。
触发完成量:void complete(struct completion *c); 唤醒一个等待线程 void complete_all(struct completion *c);唤醒全部等待线程。
 

自旋锁

自旋锁和信号量不同,自旋锁可以在不能休眠的代码中使用,例如中断处理程序。自旋锁所需要包含的头文件<linux/spinlock.h>,相关的类型是spinlock_t。因为自旋锁的特殊性,使用上必须遵循一些规则。
假定我们的驱动程序获得了一个自旋锁,然后在临界区开始了它的工作。在这个过程中,驱动程序丢掉了处理器。也许调用了一个函数,这个函数使得进程进入了休眠状态。或者发生了内核抢占,更高优先级的进程将我们的代码排挤到一边。这样我们的代码将拥有这个自旋锁,并且在可预见的未来,它不会释放。如果其他的进程试图获得相同的锁,在最好的状态下,该线程要等待很久。最坏的状态下,系统将进入死锁状态。所以适用自旋锁的的核心规则是:任何拥有自旋锁的代码都必须是原子的。它不能休眠,事实上,它不能因为任何原因放弃处理器,除了服务中断意外。拥有自旋锁的时候禁止中断拥有的时间必须尽可能的短

初始化函数
编译时初始化:spinlock_t my_lock = SPIN_LOCK_UNLOCKED;
运行时初始化:void spin_lock_init(spinlock_t *lock);

获取锁函数
void spin_lock(spinlock_t *lock);
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags); 获取自旋锁之前禁止中断(只在本地处理器上),而先前的中断状态保存在flags中。
void spin_lock_irq(spinlock_t *lock); 如果我们能够确保在释放自旋锁时应该启用中断,那么使用该函数,无需跟踪标志。
void spin_lock_bh(spinlock_t *lock); 获得锁之前禁止软件中断,但是会让硬件中断保持打开。

如果一个自旋锁,它可以被运行在(硬件或者软件)中断上下文中的代码获得,则必须使用某个禁止中断的spin_lock形式,因为其他的锁定函数迟早会导致系统死锁。如果我们不在硬件中断处理例程中访问自旋锁,但是可能在软件中断中访问,应该使用spin_lock_bh,以便在避免死锁的同时还能响应硬件中断。

释放锁函数
释放的函数,严格对应获取自旋锁的函数
void spin_unlock(spinlock_t *lock);
void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags); 必须和spin_lock_irqsave的flags是同一个变量。我们必须在同一个函数中调用spin_lock_irqsave和spin_unlock_irqrestore。
void spin_unlock_irq(spinlock_t *lock);
void spin_unlock_bh(spinlock_t *lock);

非阻塞版本函数
int spin_trylock(spinlock_t *lock);
int spin_trylock_bh(spinlock_t *lock);
成功获取自旋锁的时候返回非0,否则返回0.对于禁止中断的情况,没有对应的try版本。
 

原子变量

内核提供了一种原子的整数类型,称为atomic_t,定义在<asm/atomic.h>中。
一个atomic_t变量中不能记录大于24位的整数。操作函数如下:
void atomic_set(atomic_t *v, int i);
atomic_t v = ATOMIC_INIT(0);
将原子变量v的值设为整数值i,也可以在编译的时候利用ATOMIC_INIT宏来初始化原子变量的值。

int atomic_read(atomic_t *v); 返回v的当前值
void atomic_add(int i, atomic_t *v);将i累加到v所指向的原子变量。返回值是void,这是因为返回新的值将带来额外的成本,而大多数情况下没有必要知道累加的结果。
void atomic_sub(int i, atomic_t *v);从*v中减去i。
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);
执行特定操作并测试结果;如果在操作结束之后,原子值为0,则返回值为true;否则返回false。注意,不存在atomic_add_and_test函数。

int atomic_add_negative(int i, atomic_t *v); 将整数变量i累加到v,返回值在结果为负的时候为true,否则为false。

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_add及其变种,例外之处在于这些函数会将新的值返回给调用者。
注意:atomic_t数据项必须且只能通过上述函数来访问
 

位操作

atomic_t类型对执行整数运算来说比较有用,但是当需要以原子的形式来擦做某个位时,atomic_t类型就不适用了。内核提供了一组可原子的修改和测试单个位的函数。包含在头文件<asm/bitops.h>中。
void set_bit(int nr, void *addr); 设置addr指向的数据项的第nr位。
void clear_bit(int nr, void *addr); 清除addr指向的数据项的第nr位,其原语和set_bit相反。
test_bit(int nr, void *addr);该函数是唯一一个不必以原子方式实现的位操作函数,它仅仅返回指定位的当前值。
int test_and_set_bit(int nr, void *addr);
int test_and_clear_bit(int nr, void *addr);
int test_and_change_bit(int nr, void *addr);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值