现在的位置: 首页 > 综合 > 正文

linux Kernel 多核多线程编程

2018年04月16日 ⁄ 综合 ⁄ 共 3116字 ⁄ 字号 评论关闭

一.线程/进程

进程:资源管理的最小单位

线程:执行任务的最小单位

内核线程:内核空间创建,类似用户进程,但运行期间不能被抢断

用户线程:用户空间创建,拥有进程上下文,可调度可睡眠

 

.内核线程的同步原因

@中断:几乎可以在任何时刻发生,随时打断当前执行的代码;

@睡眠和与用户空间的同步:内核线程的睡眠,会唤醒调度程序,从而导致新用户进程执行;

@对称对处理:多个cpu可以同时执行代码;

@内核抢断:2.6以后,内核具有抢占性,内核中的任务可以被另一任务抢占;

 

三.工作队列和tasklet的选择

需要睡眠的任务—wq

需要延时的任务—wq

需要在1tick内处理的任务—tasklet

对执行时间没有需求的任务—wq

 

工作队列的本质就是将任务交给内核线程处理,它实现了内核线程的封装。

一个工作队列对应拥有n个(cpu个数)内核线程,每个线程对应运行在单独的cpu上。

一个tasklet只能运行在一个cpu上,不同的tasklet可以运行在不同的cpu上。

 

四.信号量和自旋锁的选择

低开销加锁—spinlock优先

短期加锁—spinlock优先

长期加锁---mutex/semp

中断上下文加锁—spinlock

持有锁者需要调度/睡眠—mutex/semp

 

 

五.利用其它机制来保证非重入

1.利用电源管理机制来实现操作的不可重入

2.原子操作;

 

 

电源管理休眠/唤醒流程:

 

涉及到的文件:

linux_source/kernel/power/main.c

linux_source/kernel/power/earlysuspend.c

linux_source/kernel/power/wakelock.c

 

1.      
用户空间写入mem或者standbySYSFS节点/sys/power/statekernel调用state_store(),进入以后会调用request_suspend_state(),紧接着early_suspend_work会被调用,进入early_suspend状态;

2.      
early_suspend()里面,检查现在的请求状态是否仍然还是suspend状态,防止用户取消suspend请求;如果还是suspend状态,就会把各个驱动注册的early_suspend回调函数执行一遍,然后sys_sync(),在此时,用户进程还在运行;最后释放main_wake_lock,准备进入Linux
kernel
suspend

3.      
当进入到suspend_prepare()中以后,它会给suspend分配一个虚拟终端来输出信息,然后广播一个系统要进入suspendNotify,关闭掉用户态的helper进程,然后一次调用suspend_freeze_processes()冻结所有的进程,这里会保存所有进程当前的状态,也许有一些进程会拒绝进入冻结状态,当有这样的进程存在的时候,会导致冻结失败,此函数就会放弃冻结进程,并且解冻刚才冻结的所有进程。

4.      
现在,所有的进程(也包括workqueue/kthread)
都已经停止了,内核态任务有可能在停止的时候握有一些信号量,所以如果这时候在外设里面去解锁这个信号量有可能会发生死锁,所以在外设的suspend()函数里面作lock/unlock锁要非常小心,这里建议设计的时候就不要在suspend()里面等待锁。最后会调用suspend_devices_and_enter()来把所有的外设休眠,在这个函数中,如果平台注册了suspend_pos(通常是在板级定义中定义和注册),这里就会调用suspend_ops->begin(),然后driver/base/power/main.c
中的 device_suspend()->dpm_suspend()
会被调用,他们会依次调用驱动的suspend()
回调来休眠掉所有的设备。当所有的设备休眠以后,suspend_ops->prepare()会被调用,这个函数通常会作一些准备工作来让板机进入休眠。

5.      
接下来Linux,在多核的CPU中的非启动CPU会被关掉,通过注释看到是避免这些其他的CPU造成race
condion
,接下来的以后只有一个CPU在运行了。suspend_ops
是板级的电源管理操作,通常注册在文件 arch/xxx/mach-xxx/pm.c
中。接下来,suspend_enter()会被调用,这个函数会关闭arch irq,调用 device_power_down(),它会调用suspend_late()函数,这个函数是系统真正进入休眠最后调用的函数,通常会在这个函数中作最后的检查。如果检查没问题,接下来休眠所有的系统设备和总线,并且调用
suspend_pos->enter()
来使CPU进入省电状态。这时候,就已经休眠了,代码的执行也就停在这里了。

 

 

2.为什么在sensor的各个操作函数里面增加mutex保护?

AnsMTKsensor驱动架构设计是使用hwm来实现的,所有的sensor驱动是一个子模块,挂载在hwmsen这个虚拟的input设备上,在enable
sensor
之后,它启动一个timer,在timer的回调里面使用work队列来读取sensordataenable/disable/setdelayread
sensordata
等各个功能调用在不同的线程上执行,具体sensorsensor_operaton()实现会在不同的线程被同时调用。

另外,如问题一所述,在suspend/resume执行之前,所有的进程已经被冻结,所以suspend/resume操作不必考虑重入问题。

 

 

 

相关的一些知识点:

1.      
spinlock
只能有一个所有者,semaphore可以有多个所有者;

2.      
spinlock
获取到之前,会一直处于忙等待状态,semaphore则会排队等待,进入睡眠;

3.      
同一个tasklet/timer不会在2cpu上运行,即不会重入;

4.      
不同的tasklet/timer如果要共享数据,必须使用spinlock来避免重入;

5.      
在中断之间共享数据的话,必须使用spin_lock_irq()来做保护;

6.      
不要在中断和用户上下文里使用同一个spinlock

7.      
不要在回调/hook函数里面加锁;

8.      
不要在中断/持有spinlock的时候调用运行休眠的函数;

9.      
不能在不同线程里释放当前线程获取的mutex,即获取与释放必须配对;

10.  
MP(多核)环境下,work队列是可以重入的;

11.  
kernel3.0
以后,中断线程化以后,中断也是可以重入的;

12.  
suspend/resume
early_suspend/late_resume; wakelock概念理解

13.  
HZ/tick/jiffies
概念

14.  
和应用(java)层有关的一些东西:TimerTimerTask在系统休眠以后会停掉,若需要可以使用系统的AlarmService来执行轮询(功耗);Socket长连接系统休眠以后会断开,如需要可以使用PARTIAL_WAKE_LOCK(功耗);插着usb线调试时候,系统不会进入休眠状态。

15.  
一个出了问题的进程是不会影响其它的进程的,因为它们分别在不同的进程空间进行自己的操作。同一个进程中的某个线程的故障可以影响其它的线程,因为所有的线程共享同一个虚拟内存空间以及其他资源。

抱歉!评论已关闭.