一、linux应用编程-2.1-并发与竞争

目录

一、并发与竞争

二、处理并发与竞争

2.1、原子操作

2.1.1、原子整形操作

2.1.2、原子位操作

2.2、自旋锁

  2.2.1、自旋锁API

2.3、信号量

2.3.1、信号量API

2.4、互斥体

2.4.1、互斥体API


一、并发与竞争

       并发就是多任务同时访问同一片内存区域,这些任务可能相互覆盖这段内存中的数据,造成数据混乱。

        引起并发的原因大致可有分为以下几种:

        ①、多线程并发访问

        ②、抢占式并发访问,正常运行的线程可以被调度程序挂起,重而运行其他线程

        ③、中断并发访问

        ④、多核并发访问

       因为并发,带来的问题就是竞争同一片共享数据区域,这个共享数据区域就是需要保护的对象,一般指多个线程都会访问的共享数据,比如全局变量,设备结构体,或者其他数据,某个线程的局部变量不需要保护。

        linux内核提供多种方法处理并发与竞争。

二、处理并发与竞争

2.1、原子操作

        原子操作指不能再进行分割的操作,常用于变量或者位操作。利用原子操作,可以让语句作为一个整体执行完毕,不会被切割。

2.1.1、原子整形操作

        内核用atomic_t的结构体来完成整形数据的原子操作:

typedef struct {
	int counter;
} atomic_t;

#ifdef CONFIG_64BIT
typedef struct {
	long counter;
} atomic64_t;
#endif

        原子操作API函数:

     

 注意:atomic_dec_and_test函数,从v减1,如果结果为0就返回真,这样执行后,实际上v的值就减1了,并不是v的值没变。

 使用示例:

//定义原子变量b,并赋初始值
atomic_t b = ATOMIC_INIT(0);

//设置b=10
atomic_set(&b, 10);

//读取b的值,肯定为10
atomic_read(&b);

//b的值加1.b肯定为11
atomic_inc(&b);

2.1.2、原子位操作

        原子位操作是直接对内存进行操作,API函数如下:

2.2、自旋锁

       前面提到的原子操作,只能对整形变量和位进行保护,在实际开发中还有其他类型的数据,因此只用原子操作是远远不够的。

       锁:当一个线程要访问某个共享资源的时候,首先要获取相应的锁,锁只能被一个线程持有,只要此线程不释放持有的锁,那么其他线程就不能获取此锁。自旋:就是原地打转的意思,一直等待锁释放

        使用自旋锁的缺点:等待自旋锁的线程会一直处于自旋状态,浪费处理器事件,降低系统性能,因此自旋锁持有的时间不能太长,适用于短时间的轻量加锁。

        自旋锁结构体定义如下:

typedef struct spinlock {
	union {
		struct raw_spinlock rlock;

#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
		struct {
			u8 __padding[LOCK_PADSIZE];
			struct lockdep_map dep_map;
		};
#endif
	};
} spinlock_t;

  2.2.1、自旋锁API

        以下API函数使用与多核或者支持抢占的单核下线程之间的并发访问:

使用注意事项:

       被自旋锁保护的临界区内一定不能调用任何能够引起睡眠和阻塞的API函数,否则可能会导致死锁现象。

         中断与线程之间的并发访问:

 注意事项:

①、不推荐使用spin_lock_irq/spin_unlock_irq

②、一般在线程中使用spin_lock_irqsave/spin_unlock_irqrestore,在中断中使用spin_lock/spin_unlock

2.3、信号量

        信号量的特点:

        ①、信号量可以使等待资源的线程休眠,适用那些占用资源比较久的场合

        ②、不能用于中断中,因为信号量会引起休眠,中断需要快进快出

        ③、如果共享资源持有时间比较短,就不适合适用信号量,因为频繁线程休眠会切换,引起的开销远大于信号量带来的优势。

        信号量有一个信号值,来控制访问共享资源的线程数,在初始化的时候将信号量的值设置大于1,那么这个信号量就是计数型信号量,即允许多个线程同时访问共享资源,因此不能用于互斥访问;如果要实现互斥访问,那么信号量的值设置为1,也称为二值信号量

2.3.1、信号量API

        信号量结构体如下:

struct semaphore {
    raw_spinlock_t lock;
    unsigned int count;
    struct list_head wait_list;
};

        系统API如下: 

 

 使用示例:

struct semaphore sem;     /* 定义信号量 */
sema_init(&sem, 1);         /* 初始化信号量 */

down(&sem);             /* 申请信号量 */
/* 临界区 */
up(&sem);             /* 释放信号量 */

2.4、互斥体

        将信号量的值设置为1就可以用来进行互斥访问,但更专业的就是用互斥体mutex,表示一次只有一个线程来访问共享资源。

        特点:

        ①、mutex可以导致休眠,因此不能在中断中使用mutex,中断中只能使用自旋锁

        ②、和信号量一样,mutex保护的临界区可以调用引起阻塞的api函数

        ③、因为一次只有一个线程可以持有mutex,因此必须由mutex的持有者释放mutex

2.4.1、互斥体API

        使用示例:

struct mutex lock; /* 定义一个互斥体 */
mutex_init(&lock); /* 初始化互斥体 */

mutex_lock(&lock); /* 上锁 */
/* 临界区 */
mutex_unlock(&lock); /* 解锁 */

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值