1. 原子操作
原子操作指的是在执行过程中不会被别的代码路径所中断的操作。
常用原子操作函数举例:
atomic_t v = ATOMIC_INIT(0); //定义原子变量v并初始化为0
atomic_read(atomic_t *v); //返回原子变量的值
void atomic_inc(atomic_t *v); //原子变量增加1
void atomic_dec(atomic_t *v); //原子变量减少1
int atomic_dec_and_test(atomic_t *v); //自减操作后测试其是否为0,为0则返回true,否则返回false。
2. 信号量
信号量(semaphore)是用于保护临界区的一种常用方法,只有得到信号量的进程才能执行临界区代码。
当获取不到信号量时,进程进入休眠等待状态。
定义信号量
struct semaphore sem;
初始化信号量
void sema_init (struct semaphore *sem, int val);
void init_MUTEX(struct semaphore *sem);//初始化为0
static DECLARE_MUTEX(button_lock); //定义互斥锁
获得信号量
void down(struct semaphore * sem); //休眠
int down_interruptible(struct semaphore * sem);
int down_trylock(struct semaphore * sem);
释放信号量
void up(struct semaphore * sem); // 唤醒
static DECLARE_MUTEX(button_lock); //定义互斥锁
驱动 open中
if (file->f_flags & O_NONBLOCK)
{
if (down_trylock(&button_lock)) //如果没有阻塞的话,应用层一读就会立马返回
return -EBUSY;
}
else
{
/* 获取信号量 */
down(&button_lock);
}
驱动 release
up(&button_lock);
3. 阻塞
阻塞操作
是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。
被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。
非阻塞操作
进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。
fd = open("...", O_RDWR | O_NONBLOCK);
---------------------------------------------------------------------------------------------
come from: https://blog.csdn.net/xiaodingqq/article/details/80353639
并发(concurrency):在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。其中两种并发关系分别是同步和互斥。
所谓互斥,是指分布在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行。
所谓同步,是指分布在不同进程之间的若干程序片断,它们的运行必须严格按照规定的 某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。 显然,同步是一种更为复杂的互斥,而互斥是一种特殊的同步。也就是说互斥是两个线程之间不可以同时运行,他们会相互排斥,必须等待一个线程运行完毕,另一个才能运行,而同步也是不能同时运行,但他是必须要安照某种次序来运行相应的线程(也是一种互斥)!
总结:
互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。
异步(asynchronous):异步和同步是相对的,同步就是顺序执行,执行完一个再执行下一个,需要等待、协调运行。异步就是彼此独立,在等待某事件的过程中继续做自己的事,不需要等待这一事件完成后再工作。线程就是实现异步的一个方式。异步是让调用方法的主线程不需要同步等待另一线程的完成,从而可以让主线程干其它的事情。
同步和异步是针对应用程序和内核的交互而言的,
同步指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪,而异步是指用户进程触发IO操作以后便开始做自己的事情,
而当IO操作已经完成的时候会得到IO完成的通知(异步的特点就是通知)。
异步和多线程并不是一个同等关系,异步是最终目的,多线程只是我们实现异步的一种手段。异步是当一个调用请求发送给被调用者,而调用者不用等待其结果的返回而可以做其它的事情。实现异步可以采用多线程技术或则交给另外的进程来处理。
3.阻塞,非阻塞
而阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作函数的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入函数会立即返回一个状态值。
一般来说IO模型可以分为:同步阻塞,同步非阻塞,异步阻塞,异步非阻塞。
同步阻塞IO
在此种方式下,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。
同步非阻塞IO
在此种方式下,用户进程发起一个IO操作以后可返回做其它事情,
但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费。
异步阻塞IO
此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,
等内核完成IO操作以后会通知应用程序,这其实就是同步和异步最关键的区别同步必须等待或者主动的去询问IO是否完成,
异步非阻塞IO
在此种模式下,用户进程只需要发起一个IO操作然后立即返回,
等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,
此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作因为真正的IO读取或者写入操作已经由内核完成了。
同步与异步是对应的,它们是线程之间的关系,两个线程之间要么是同步的,要么是异步的。
阻塞与非阻塞是对同一个线程来说的,在某个时刻,线程要么处于阻塞,要么处于非阻塞。
阻塞是使用同步机制的结果,非阻塞则是使用异步机制的结果。
---------------------------------------------------------------------------------------------------------------------------
come from:https://blog.csdn.net/yikai2009/article/details/8650259
信号量:
Linux 内核的信号量在概念和原理上与用户态的信号量是一样的,
但是它不能在内核之外使用,只能在内核中,它是一种睡眠锁.
如果有一个任务想要获得 已经被占用 的信号量时,信号量会将这个进程放入一个等待队列, 然后让其睡眠,
当持有信号量的进程将信号释放后,处于等待队列中的任务将被唤醒,并让其获得信号量.
1,信号量在创建时需要设置一个初始值,表示允许有几个任务同时访问该信号量保护的资源.
初始值为 1 就变成互斥锁 ( Mutex ) ,即同时只能有一个任务可以访问信号量保护的资源.
2,当任务访问完被信号量保护的共享资源后,必须释放信号量,释放信号量通过把信号量的值加 1 实现.
如果释放后信号量的值为非正数,表明有任务等待当前信号量,因此要唤醒等待该信号量的任务.
定义信号量 struct semaphore:
信号量的实现也是与体系结构相关的,定义在 < asm/semaphore.h > 中,
struct semaphore 类型用来表示信号量.
eg: struct semaphore sem;
初始化信号量 sema_init init_MUTEX init_MUTEX_LOCKED :
void sema_init ( struct semaphore *sem , int val )
该函数用于初始化设置 信号量 的初值,它设置信号量 sem 的值 为 val .
初始化一互斥锁 MUTEX:
void init_MUTEX ( struct semaphore *sem )
该函数用于初始化一个互斥锁,即它把信号量 sem 的值设置为 1.
void init_MUTEX_LOCKED ( struct semaphore *sem)
该函数也用于初始化一个互斥锁,但是它把信号量 sem 的值设置为 0,即一开始就处在已锁状态.
初始化 定义一步完成 DECLARE_MUTEX DECLARE_MUTEX_LOCKED :
定义与初始化的工作可由如下宏一步完成:
DECLARE_MUTEX ( name )
定义一个信号量 name,并初始化她的值为 1 .
DECLARE_MUTEX_LOCKED ( name )
定义一个信号量 name ,但把它的初始化设置为 0 ,即锁在创建时就处在已锁状态.
获取信号量 down down_interruptible down_trylock :
1, void down ( struct semaphore * sem )
获得信号量 sem . 可能会导致进程睡眠,因此不能在中断上下文使用该函数.
该函数将把 sem 的值减 1,如果信号量 sem 的值非负,就直接返回,
否则调用者将被挂起,直到别的任务释放该信号量才能继续使用.
2, int down_interruptible ( struct semaphore * sem )
获取信号量 sem . 如果信号量不可用,进程将被置为 TASK_INTERRUPTIBLE 类型的睡眠状态.
该函数由返回值来区分是正常返回还是被信号中断返回,
如果返回 0 ,表示获得信号量正常返回,
如果被信号打断,返回 ——EINTR .
3, down_trylock ( struct semaphore * sem )
获取信号量 sem . 如果信号量不可获取,down_trylock 会立即返回 一个 非零值. down_trylock 它永远不会休眠
注:
down () 函数现已不建议继续使用,建议使用 down_killable ()或 down_interruptible ()
释放信号量 up :
void up ( struct semaphore *sem )
该函数释放信号量 sem ,即把 sem 的值加 1,如果 sem 的值为非负数,
表明有任务等待该信号量,因此唤醒这些等待者.
实例 --- 按键信号量使用:
1,初始化,定义信号量:
在驱动程序开头初始化,定义信号量:
static DECLARE_MUTEX(button_lock); //定义互斥锁
2,获取信号量:
在 open 函数中获取信号量,获取到就继续执行,否则休眠;
/* 获取信号量 */
down(&button_lock);
3,释放信号量:
程序退出,close 释放信息量:
up(&button_lock); /* 释放信号量 */
return 0;