【内核】04.并发机制
【嵌入式八股】一、语言篇https://www.nowcoder.com/creation/manager/columnDetail/mwQPeM
【嵌入式八股】二、计算机基础篇https://www.nowcoder.com/creation/manager/columnDetail/Mg5Lym
【嵌入式八股】三、硬件篇https://www.nowcoder.com/creation/manager/columnDetail/MRVDlM
【嵌入式八股】四、嵌入式Linux篇(本专栏)https://www.nowcoder.com/creation/manager/columnDetail/MQ2bb0
并发机制
40.驱动里面为什么要有并发、互斥的控制?如何实现?讲个例子?
并发,指的是多个执行单元伪同时、并行被执行,而并发的执行单元对共享资源(硬件资源和软件上的全局变量、静态变量等)的访问则很容易导致竞态。 解决竞态问题的途径是保证对共享资源的互斥访问,所谓互斥访问就是指一个执行单元在访问共享资源的时候,其他的执行单元都被禁止访问,其他执行单元等待释放资源并依次顺序访问就是同步。 访问共享资源的代码区域被称为临界区,临界区需要以某种互斥机制加以保护。中断屏蔽,原子操作,自旋锁,和信号量都是linux设备驱动中可采用的互斥途径。
一个简单的例子是实现一个驱动程序来控制一个LED灯的开关。假设这个LED灯是通过GPIO口控制的,而驱动程序需要读写GPIO口的寄存器。如果多个进程同时打开这个驱动程序并尝试操作GPIO口的寄存器,就可能导致竞争条件和不可预测的结果。因此,在驱动程序中必须实现并发和互斥的控制,以确保只有一个进程在任何时候都可以操作GPIO口的寄存器。
例如,可以使用Linux内核提供的信号量机制来实现互斥和同步。驱动程序中创建一个信号量,当进程打开驱动程序时获取该信号量,当进程关闭驱动程序时释放该信号量。在驱动程序中访问GPIO口的寄存器之前,首先获取该信号量,完成访问之后再释放信号量,这样就能够确保只有一个进程在任何时候都可以操作GPIO口的寄存器。
41.同步并发的区别和联系?
同步和并发都是指多个任务或进程之间的关系,但是它们的概念和意义不同。
同步是指协调多个任务或进程之间的行为,使得它们按照一定的规则、次序或条件来执行。同步是为了管理共享资源的并发访问,防止多个任务或进程之间出现竞争、冲突等问题,保证系统的正确性和可靠性。常见的同步机制包括锁、信号量、条件变量等。
并发是指多个任务或进程在同一时间段内执行,利用计算机资源提高系统的效率和响应速度。并发是为了充分利用系统资源,提高系统的并发性和效率。常见的并发机制包括进程、线程、协程、并行算法等。
42.Linux内核中的并发机制有哪些
并发机制是指同时执行多个任务或进程的能力。在操作系统中,为了充分利用计算机资源,提高系统的并发性和效率,需要使用并发机制来实现任务的并发执行。常用的并发机制包括:
- 进程:进程是计算机中运行的程序实例。每个进程都有自己的地址空间、文件描述符、信号处理器等,相互独立,可以并发执行。进程之间可以通过IPC(进程间通信)机制来进行通信和同步。
- 线程:线程是轻量级的进程,是操作系统进行任务调度的基本单位。多个线程可以共享同一个进程的地址空间和文件描述符,可以并发执行,相互之间可以通过共享内存等方式进行通信和同步。
- 协程:协程是一种用户态的轻量级线程,相对于线程而言更加轻量级、资源消耗更小。协程可以在同一个线程中实现多个任务的并发执行,相互之间可以通过协作式的方式进行通信和同步。
- 并行算法:并行算法是一种通过将任务分解为子任务,利用多个处理器并行执行的算法。通过并行计算可以提高算法的效率和并发性。
- 同步机制:如前面所述,同步机制是管理共享资源的并发访问的一种方式,可以通过锁、信号量、条件变量等机制实现并发访问的同步。
在实际应用中,需要根据具体的应用场景选择合适的并发机制,以提高系统的并发性和效率。比如,在需要处理大量请求的Web服务器中,可以通过线程池和协程来提高并发性能;在需要处理密集计算的应用中,可以通过并行计算来提高算法的效率。
43.Linux内核有哪些同步方式?
共享资源防冲突使用有什么手段?
原子操作
不被打断,放在硬件驱动最底层,只有一个进程能用,释放后其他进程才能用
中断屏蔽
进程中一段程序中不想被中断打断(可能这段程序和中断程序都会操作一个设备),可用中断屏蔽,时间短,最好一个函数内使用
- local_irq_disable(); //屏蔽所有中断
- local_irq_save(flags); //屏蔽所有中断,可恢复
- disable_irq(int irq); //屏蔽指定中断号的中断
自旋锁
一个线程操作一个设备,另一个线程也操作这个设备,那就得一直等第一个用完,但是等待时间不长
时间短,最好一个函数内使用,while循环等待释放锁,一个进程用完下一个进程用。不能睡眠,可以中断中使用。
- spin_lock(&slock);
- spin_try_lock(); //为避免很卡
- spin_lock_irqsave(&db->lock, flags); //自旋锁 + 中断屏蔽,防锁状态变换时进入中断,浪费时间
互斥体
一个线程操作一个设备,另一个线程也操作这个设备,那得等第一个用完,但中间可以睡眠,去做其他事。不能中断中使用。
也叫互斥锁,互斥量,会阻塞睡眠,可长时间,不能用于中断中,cpu去处理其他进程了,等下一次轮过来再检测好了没
- mutex_init(&lock); //初始化互斥锁
- mutex_lock(&lock); //上锁 (无法获得,则阻塞睡眠)
- mutex_unlock(&lock); //解锁
信号量
【操作系统,这个底层实现应该是靠记录型信号量】
变量+1-1,和互斥体差不多,资源数不为1时可以做资源的计数,而互斥体不行
可以同时给多个进程用,但是计数到了就不能让其他进程再用了,可以睡眠
读写锁
如视频会议
摄像头,网卡,GPU往内存中写,要求写写互斥
读写也要互斥,要不每个人读的可能不一样,
如果想要收到的都是美颜后的,那要写优先(顺序锁),如果追求速度,那要读优先(RCU)
读读可以不互斥,各自读各自的就行
读写优先级相同,
无法保证读优先。写饥饿。
顺序锁
写优先
把负担丢个读者(重读,冲突判断)
读拷贝更新RCU
主要是读写互斥,读优先
写时先拷贝,合适时再更新,让读者优先,不用等待,读的时候直接指针指向更新好的。
中原互旋号,顺序读写更新
44.Linux原子操作是怎么实现的?
Linux内核同步机制原子操作 - 知乎 (zhihu.com)
ARMv6之前的实现原理是通过关闭CPU中断实现的,ARMv6之后的实现是通过新增加的两个CPU指令ldrex、strex
实现的。 通过下面的代码可以具体的看到实现的细节:
prefetchhw
是预取操作和cache有关,主要是为了提高性能。__volatile__
主要是用来防止编译器优化的。在编译c代码的时候,如果使用优化选项(-O)进行编译,对于那些没有声明__volatile__
的嵌入式汇编代码,编译器有可能会对其进行编译优化,编译的结果可能不是原来的汇编代码,有了__volatile__
之后,编译器就会停止对该段代码的任何优化。
独占访问指令ldrex和strex
ldrex/strex是ARMv6架构及之后架构的同步原语,属于硬件层面的同步机制。只要某个时刻只允许一个执行单元访问共享资源那么就必须进行同步,共享资源可以是内存、外设设备,执行单元可以是处理器、进程或者线程。
ldrex/strex这两个指令配合独占监控器(独占监控器会跟踪独占内存访问)可以实现原子地更新内存数据。
45.信号量机制中的PV操作是原子操作吗?
信号量是用来表示资源的可使用个数,最常见的莫过于二值信号量,即信号量的取值为0或者1,第二种就是通用信号量,通用信号量即指信号量的取值是不仅仅是0或者1,而是用信号量表示当前资源的个数。谈到信号量,不得不说一下原子操作,所谓的原子操作就指不能被中断的操作。对于信号量的PV操作均属于原子操作。
- P(S):先将信号量的值S减1,如果S >=0,则表明此资源满足进程可以执行,如果S < 0那么表示需要等待资源,进程进入阻塞状态
- V(S): 先将信号量的值S加1,如果S > 0,那么将会唤醒一个被阻塞的进程。
46.自旋锁和信号量有什么区别
(2条消息) 信号量以及信号量和自旋锁的区别_Y~哈哈哈的博客-CSDN博客_2. 简述信号量和自旋锁的相同点和不同点 以及使用时的注意事项
自旋锁和信号量都是 Linux 内核中常用的同步机制,它们的主要区别在于它们的实现方式和适用
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
查阅整理上千份嵌入式面经,将相关资料汇集于此,主要包括: 0.简历面试 1.语言篇 2.计算机基础 3.硬件篇 4.嵌入式Linux【本专栏】 (建议PC端查看)