Linux并发与竞争

Linux并发与竞争

Linux并发

并发是指在操作系统中,一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是 在同一个处理机上运行,但任一时刻点上只有一个程序在处理机上运行。 并发容易导致竞争问题。

Linux竞争

竞争就是两个或者多个进程同时访问一个资源,从而引起资源的错误。为避免并发对系统资源的影响。可以采用原子变量操作,自旋锁,信号量和互斥信号量。

原子变量操作

原子变量:
原子操作只能对整形变量或者位进行保护。
原子变量操作所谓原子变量操作,就是该操作绝不会在执行完毕前被任何其他任务或事件打断。也就是说,原子变量操作是一种不可以被打断的操作。原子操作需要硬件的支持,它们是使用汇编语言实现的,C语言并不能实现这样的操作。原子操作(包括原子整形操作和原子位操作)

原子操作:
typedef struct {
	int counter;
}atomic_t;
atomic_t a;

原子整形操作 API 函数:
ATOMIC_INIT(int i) //定义原子变量的时候对其初始化。
int atomic_read(atomic_t *v) //读取 v 的值,并且返回。
void atomic_set(atomic_t *v, int i) //向 v 写入 i 值。
void atomic_add(int i, atomic_t *v) //给 v 加上 i 值。
void atomic_sub(int i, atomic_t *v) //从 v 减去 i 值。
void atomic_inc(atomic_t *v) //给 v 加 1,也就是自增。
void atomic_dec(atomic_t *v) //从 v 减 1,也就是自减
int atomic_dec_return(atomic_t *v) //从 v 减 1,并且返回 v 的值。
int atomic_inc_return(atomic_t *v) //给 v 加 1,并且返回 v 的值。
int atomic_sub_and_test(int i, atomic_t *v) //从 v 减 i,如果结果为 0 就返回真,否则返回假
int atomic_dec_and_test(atomic_t *v) //从 v 减 1,如果结果为 0 就返回真,否则返回假
int atomic_inc_and_test(atomic_t *v) //给 v 加 1,如果结果为 0 就返回真,否则返回假
int atomic_add_negative(int i, atomic_t *v) //给 v 加 i,如果结果为负就返回真,否则返回假

原子位操作 API 函数:
void set_bit(int nr, void *p) //将 p 地址的第 nr 位置 1。
void clear_bit(int nr,void *p) //将 p 地址的第 nr 位清零。
void change_bit(int nr, void *p) //将 p 地址的第 nr 位进行翻转。
int test_bit(int nr, void *p) //获取 p 地址的第 nr 位的值。
int test_and_set_bit(int nr, void *p) //将 p 地址的第 nr 位置 1,并且返回 nr 位原来的值。
int test_and_clear_bit(int nr, void *p) //将 p 地址的第 nr 位清零,并且返回 nr 位原来的值。
int test_and_change_bit(int nr, void *p) //将 p 地址的第 nr 位翻转,并且返回 nr 位原来的值。

自旋锁

自旋锁:
在实际使用环境中,不可能只有整形变量或者位这么简单的临界区。当一个临界区域要在多个函数之间来回运行时,原子操作就显得无能为力了。

自旋锁:
typedef struct spinlock {
	......
}spinlock_t;
spinlock_t lock;

自旋锁 API 函数:
DEFINE_SPINLOCK(spinlock_t lock) //定义并初始化一个自选变量。
int spin_lock_init(spinlock_t *lock) //初始化自旋锁。
void spin_lock(spinlock_t *lock) //获取指定的自旋锁,也叫做加锁。
void spin_unlock(spinlock_t *lock) //释放指定的自旋锁。
int spin_trylock(spinlock_t *lock) //尝试获取指定的自旋锁,如果没有获取到就返回 0
int spin_is_locked(spinlock_t *lock) //检查指定的自旋锁是否被获取,如果没有被获取就返回非 0,否则返回 0。

使用自旋锁保护临界资源:
spinlock_t lock;
spin_lock_init(&lock);
spin_lock(&lock);
临界资源代码
spin_unlock(&lock);

使用自旋锁注意事项:
1.自旋锁是一种忙等待。Linux中,自旋锁当条件不满足时,会一直不断地循环条件是否被满足。如果满足,就解锁,继续运行下面的代码。这种忙等待机制对系统的性能是有影响的。所以在实际编程中,程序员应该注意自旋锁不应该长时间地持有,它是一种适合短时间锁定的轻量级的加锁机制。
2.自旋锁不能递归使用,这是因为,自旋锁被设计成在不同线程或者函数之间同步。如果一个线程在已经持有自旋锁时,其处于忙等待状态,则已经没机会释放自己持有的锁。如果这时再调用自身,则自旋锁永远没有执行的机会了。

信号量

信号量:
信号量与自旋锁的不同之处在于,自旋锁会忙等待。
而信号量会将进程加入到一个等待队列中去睡眠,直到拥有信号量的进程释放信号后,处于等待队列中的那个进程才会被唤醒。
当进程被唤醒后,就立刻重新从睡眠的地方开始执行,又一次试图获得信号量,当获取信号量后,程序继续执行。
从信号量的原理上来说,没有获得信号量的函数可能睡眠。这就要求只有能够睡眠的进程才能够使用信号量,不能睡眠的进程不能使用信号量。
例如在中断处理程序中,由于中断需要立刻完成,所以不能休眠,也就是说在中断处理函数程序中是不能使用信号量的。

信号量:
struct semaphore {
	raw_spinlock_t lock;
	unsigned int count;
	struct list_head wait_list;
};
struct semaphore sem;

信号量 API 函数:
DEFINE_SEAMPHORE(name) //定义一个信号量,并且设置信号量的值为 1。
void sema_init(struct semaphore *sem, int val) //初始化信号量 sem,设置信号量值为 val。
void down(struct semaphore *sem) //获取信号量,因为会导致休眠,因此不能在中断中使用。
int down_trylock(struct semaphore *sem) //尝试获取信号量,如果能获取到信号量就获取,并且返回 0。如果不能就返回非 0,并且不会进入休眠。
int down_interruptible(struct semaphore *sem) //获取信号量,和 down 类似,只是使用 down 进入休眠状态的线程不能被信号打断。而使用此函数进入休眠以后是可以被信号打断的。
void up(struct semaphore *sem) //释放信号量

使用信号量保护临界资源:
struct semaphore sem;
sema_init(&sem, 1);
down(&sem);
临界资源代码
up(&sem);

信号量可以用于同步操作。
自旋锁和信号量的对比:
当被包含的代码能够在很短的时间内执行完成时,那么使用自旋锁是一种很好的选择。因为自旋锁只是忙等待,不会进入睡眠。睡眠是一种非常浪费时间的操作。
信号量用来在多个进程之间互斥。信号量的执行可能引起进程的睡眠,睡眠需要进程上下文的切换,这是非常浪费时间的一项工作。所以只有在一个进程对被保护的资源占用时间比进程切换的时间长很多时,信号量才是一种更好的选择。

互斥信号量

互斥信号量:

互斥体:
struct mutex {
	atomic_t count;
	spinlock_t wait_lock;
};
struct mutex lock;

互斥体 API 函数:
DEFINE_MUTEX(name) //定义并初始化一个 mutex 变量。
void mutex_init(mutex *lock) //初始化 mutex。
void mutex_lock(struct mutex *lock)//获取 mutex,也就是给 mutex 上锁。如果获取不到就进休眠。
void mutex_unlock(struct mutex *lock) //释放 mutex,也就给 mutex 解锁。
int mutex_trylock(struct mutex *lock)//尝试获取 mutex,如果成功就返回 1,如果失败就返回 0。
int mutex_is_locked(struct mutex *lock)//判断 mutex 是否被获取,如果是的话就返回1,否则返回 0。
int mutex_lock_interruptible(struct mutex *lock) //使用此函数获取信号量失败进入休眠以后可以被信号打断。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Paper_Love

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值