完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
2个回答
|
|
1 SoC中断
1.1 ARMv8中断 IPI:inter-processer interrupt 中断号0 - 15 PPI:per processor interrupts 中断号16 - 31 SPI:shared processor interrupts 中断号 32 - 224 SGI:software generated interrupts (SGI) 设备树是用来描述硬件信息的,因此里面不涉及软件中断SGI,在arm-gic.h文件中定义的只有SPI和PPI。 #define GIC_SPI 0 #define GIC_PPI 1 一般设备树中的中断都是SPI,那么interrupts = <0 208 1>;是什么意思呢? X:GIC_SPI或者GIC_PPI Y:物理中断号 - 32,SA8155 Z:触发方式 1 = low-to-high edge triggered 2 = high-to-low edge triggered (invalid for SPIs) 4 = active high level-sensitive 8 = active low level-sensitive (invalid for SPIs) 1.2 临界区开关CPU中断API arm:local_irq_disable() / local_irq_enable() 在ARM V6以上使用汇编指令CPSID / CPSIE(Current Program Status Interrupt Disable / Enable)用于快速的开关中断,早期版本使用汇编指令mrs和msr去更新cpsr。 x86:local_irq_disable调用汇编指令CLI(clear interrupt-enable flag,IF);local_irq_enable调用汇编指令STI(set interrupt-enable flag,IF)。 arm的cpsid和x86的CLI同时禁止了核间中断(IPI),CPU-x还会用IPI通知CPU-y进行resched,但是CPU-y可能已经禁用了中断而不会响应。 在中断中不能产生调度,在中断返回时才可能发生调度。事实上,早期的内核很大程度上是依赖local_irq_disable来做资源保护,这个看看2.4的内核源码就很清楚了,里面有大量的对local_irq_disable函数的直接调用。 1.3 Linux Kernel中断处理 request_threaded_irq() - 实时中断处理,比较好。 如果成功注册了一个中断(中断号为300,中断名字为oem),那么通过ps命令可以看到该线程 irq/300-oem 中断优化: echo 1 > /proc/sys/kernel/sched_boost 2 Linux内存管理 2.1 free命令 swapoff -a && swapon -a free -h 显示的cache是指disk cache,也即是磁盘文件在内存中的缓存。 1) Clear PageCache only # sync; echo 1 > /proc/sys/vm/drop_caches 2) Clear dentries and inodes # sync; echo 2 > /proc/sys/vm/drop_caches 3) Clear PageCache, dentries and inodes # sync; echo 3 > /proc/sys/vm/drop_caches 2.2 内存页表 CONFIG_PTDUMP_CORE CONFIG_PTDUMP_DEBUGFS /sys/kernel/debug/kernel_page_tables page-types工具 tools/vm/page-types.c make -C tools/vm 2.3 栈空间 Linux查看修改线程默认栈空间大小(ulimit -s,单位KB),Linux默认是8MB。 2.3.1 kmalloc 用于kmalloc可分配的内存大小范围在32~131027(128k)字节,并且由于它用slab分配器来分配内存的,所以得到的内存大小可能比你申请的要大一些(它向上取2的N次幂整数)。而且如果开启了CONFIG_LARGE_ALLOCS选项,这个值可以更大,可以达到了32M。 2.3.2 malloc malloc申请的空间大于128KB的话,使用mmap系统调用在stack 和 heap中间的区域(共享内存映射区域)进行虚拟内存分配,其大小应该就是栈底与heap顶之间的空间最大值。 此时mmap()使用MAP_ANONYMOUS,并且不关心文件描述符fd的值;通过strace跟踪进程可以看到大量的带有MAP_ANONYMOUS的mmap()。 dlopen isn't a system call, it's a library function in the libdl library. Only system calls show up in strace. On Linux and on many other platforms (especially those that use the ELF format for executables), dlopen is implemented by opening the target library with open() and mapping it into memory with mmap(). mmap() is really the critical part here, it's what incorporates the library into the process' address space, so the CPU can execute its code. But you have to open() the file before you can mmap() it! mmap vs read()/write()/lseek() 2.4 buddy and slab Buddy APIs:alloc_pages()和__get_free_pages()等。 因为内存初始化的时间比分配和释放时间长好多,slab增加一个类似对象池的特性,slab会缓存已经被“释放”的对象,以便下次重用,不必再初始化。对象用该高速缓存结构kmem_cache描述。通过kmem_cache_create创建。虽然名为高速缓存,但是它不是硬件上的高速缓存,它只是主存上的区域,但是和硬件上的高速缓存高度相关。 2.5 ZONE_HIGHMEM 当内核空间大小是3GB – 4GB时,并不是所有的物理内存都是映射的,而是部分映射,需要时再动态将需要访问的物理内存映射到(3.896GB - 4GB)地址空间。使用CONFIG_HIGHMEM来配置。 vmalloc和kmalloc的区别: - vmalloc分配的一般为高端内存,只有当内存不够的时候才分配低端内存;kmalloc从低端内存分配 - vmalloc分配的物理地址一般不连续,而kmalloc分配的物理地址连续,两者分配的虚拟地址都是连续的 - vmalloc分配的一般为大块内存,而kmalloc一般分配的为小块内存(不超过128k) 2.6 64位CPU地址空间布局 查看物理和虚拟地址位数:/proc/cpuinfo 2.6.1 x86_64地址空间布局 - 64bit = 16MegaT - x86_64使用了40位物理地址线,48位虚拟地址空间 - 用户空间128T:0x0000,0000,0000,0000到0x0000,7fff,ffff,f000(最后12位不是fff,保留了一个页面) - 内核空间128T:0xffff,8000,0000,0000到0xffff,ffff,ffff,ffff;注意:该地址前4个都是f,这是因为目前实际上只用了64位地址中的48位(高16位是没有用的) - 从地址0x0000,7fff,ffff,ffff到0xffff,8000,0000,0000中间是一个巨大的空洞,是为以后的扩展预留的 2.6.2 ARM64地址空间布局 - ARM64使用了48位物理地址线,64位虚拟地址空间 - 用户空间128T:0x0000,0000,0000,0000到0x0000,7fff,ffff,ffff - 内核空间256T:0xffff,0000,0000,0000到0xffff,ffff,ffff,ffff 3 Linux scheduler 3.1 time slice SCHED_NORMAL time_slice = (int)((1 - priority / 140.0) x 290 + 0.5) + 10 time_slice range [10, 300] ms scheduler_tick() #include int sched_rr_get_interval(pid_t pid, struct timespec *tp); SCHED_RR = 100 ms context switch: 0.1 - 1 ms 3.2 调度策略 linux的实时进程有两种调度策略,SCHED_FIFO和SCHED_RR(类似于打麻将,轮流坐庄),SCHED_FIFO是简单的队列调度,并且没有timeslice的限制,谁先来谁运行,除非是阻塞或者主动yield,否者将一直占用CPU,即使关中断也不能阻止优先级高的进程被调度运行。 实时进程从不激活到激活状态时,根据进程的优先级,找到对应的优先级列表头,然后插入到该优先级列表尾部。多个实时进程在SMP之间采用push和pull调度机制。 对于普通进程,是通过nice系统调用来调整优先级的。从内核角度讲[100,139]是普通进程的优先级的范围,100最高,139最低,默认是120。普通进程的优先级的作用和实时进程不同,普通进程优先级表示的是占用CPU的时间。深入linux内核架构中提到,普通优先级越高(100最高,139最低),享受的CPU time越多,相邻的两个优先级,高一级的进程比低一级的进程多占用10%的CPU,比如内核优先级数值为120的进程要比数值是121的进程多占用10%的CPU。 我们知道,linux的进程调度时机有: 1、进程状态转换的时刻:进程终止、进程睡眠 2、当前的进程的时间片用完(current->counter = 0) 3、设备驱动程序主动调用schedule 4、进程从中断、异常及系统调用返回用户态 struct sched_param { /* ... */ int sched_priority; /* ... */ }; int sched_setscheduler (pid_t pid, int policy, const struct sched_param *sp); sched_setscheduler函数的第二个参数调度方法 : #define SCHED_NORMAL 0 #define SCHED_FIFO 1 #define SCHED_RR 2 #ifdef __USE_GNU # define SCHED_BATCH 3 #endif SCHED_NORMAL(以前叫SCHED_OTHER)表示普通进程,对于普通进程,第三个参数sp->sched_priority只能是0 SCHED_FIFO 和SCHED_RR表示实时进程的调度策略,第三个参数的取值范围为[1,99]。 如果sched_setscheduler 优先级设置的值和调度策略不符合的话,会返回失败的。 3.3 ARM big.LITTLE ARM的big.LITTLE架构的调度器经历了IKS(In Kernel Switcher)-> HMP/GTS(Global Task Scheduler)-> EAS(Energy Aware Scheduler,based on CFS)。 /proc/sys/kernel/sched_downmigrate 这个值是个百分比,任务需求相对cpu能力的百分比。降低此值将会阻止任务迁移到小核上,对性能有利。 /proc/sys/kernel/sched_upmigrate 这个值是个百分比,任务需求相对cpu能力的百分比。提高这个值将会阻止任务往大核迁移,有利于功耗,不利于性能。 echo 1 > /proc/sys/kernel/sched_boost 3.4 进程抢占 在任何情况下使用spin_lock_irq都是安全的。因为它既禁止本地中断,又禁止内核抢占。 spin_lock比spin_lock_irq速度快,但是它并不是任何情况下都是安全的。 举个例子:进程A中调用了spin_lock(&lock)然后进入临界区,此时来了一个中断(interrupt),该中断也运行在和进程A相同的CPU上,并且在该中断处理程序中恰巧也会spin_lock(&lock)试图获取同一个锁。由于是在同一个CPU上被中断,进程A会被设置为TASK_INTERRUPT状态,中断处理程序无法获得锁,会不停的忙等,由于进程A被设置为中断状态,schedule()进程调度就无法再调度进程A运行,这样就导致了死锁! 但是如果该中断处理程序运行在不同的CPU上就不会触发死锁。 因为在不同的CPU上出现中断不会导致进程A的状态被设为TASK_INTERRUPT,只是换出。当中断处理程序忙等被换出后,进程A还是有机会获得CPU,执行并退出临界区。 所以在使用spin_lock时要明确知道该锁不会在中断处理程序中使用。 3.5 Linux ps命令返回参数解释 PPID:Parent Process ID VSIZE:Virtual Size,进程的虚拟内存大小 RSS:Resident Set Size,实际驻留“在内存中”的内存大小 WCHAN:Wait Channel(as an address),休眠进程在内核中的地址 PC:Program Counter,程序CPU指针 3.6 kworker是什么 显示格式:kworker/%d:CPU_ID u:是unbound的缩写,代表没有绑定特定的CPU,kworker/u8:0中的8是work_pool的ID。不带u的就是绑定特定cpu的workerq,它在init_workqueues中初始化,给每个cpu分配worker,如果该worker的nice小于0,说明它的优先级很高,所以就加了H(High)属性。通过ps可以看到内核创建的所有kworker。 譬如为CPU0创建的3个kworker:kworker/0:0、kworker/0:0H、kworker/u8:0 4 RTLinux 4.1 prio和rt_priority的区别 - 实时进程的优先级(rt_priority)数字越大则优先级越高,99最高,0最低;而普通进程正好相反,优先级(static_prio)数字越大则优先级越低,100最高,139最低(对应nice值-20 ~ 19) - 内核调度是按照task_struct中的prio来调度的,prio的值越小,优先级就越高。实时线程的rt_priority转换成prio:prio = MAX_RT_PRIO - 1 - p->rt_priority;普通线程的static_prio转换成prio:prio = static_prio = MAX_RT_PRIO + nice + 20 - 进程创建时的默认优先级是120,对应的nice值为0 4.2 priority inheritance in kernel/locking/rtmutex.c rt_mutex_adjust_prio() - 适用于优先级值小于100的实时任务 in kernel/sched/core.c rt_mutex_setprio() 4.3 中断线程化 in kernel/irq/manage.c setup_irq() 5 STM32中断 5.1 STM32中断编号 CMSIS IRQn = NVIC IRQn - 16 1)NVIC IRQn从1开始,所以NVIC中断1(Reset异常)对应CMSIS中断编号-15,NVIC中断16对应CMSIS中断编号0。 2)NVIC IRQn of SysTick等于15。 3)NVIC IRQn of PendSV等于14。 STM32的中断服务程序的名字不能自定义,必须使用官方已经定义好的名字,名字可参考如下的文件。 @ DriversCMSISDeviceSTSTM32L0xxSourceTemplatesiarstartup_stm32l061xx.s 当然服务程序的具体内容还是自己写,放在stm32f10x_it.c里。 5.2 BASEPRI FreeRTOS中进入临界区时没有关闭所有中断,而是使用优先级屏蔽寄存器BASEPRI(= configMAX_SYSCALL_INTERRUPT_PRIORITY)关闭了部分中断;这个寄存器最多有9位(由表达优先级的位数决定)。它定义了被屏蔽优先级的阈值。当它被设成某个值后,所有优先级号大于等于此值的中断都被关(优先级号越大,优先级越低)。但若被设成0,则不关闭任何中断,0也是缺省值。 FreeRTOS任务代码中临界段的进入和退出主要是通过操作寄存器BASEPRI实现的。进入临界区BASEPRI关闭了所有大于等于宏定义configMAX_SYSCALL_INTERRUPT_PRIORITY所定义的中断优先级,这样临界段代码就不会被中断干扰到,而且实现任务切换功能的 PendSV 中断和SysTick滴答定时器中断是最低优先级中断,所以此任务在执行临界段代码期间是不会被其它高优先级任务打断的。退出临界段时重新操作BASEPRI寄存器,即打开被关闭的中断(这里我们不考虑不受FreeRTOS 管理的更高优先级中断)。 Cortex-M内核的“中断优先级寄存器”是以最高位(MSB)对齐的。STM32使用了优先级寄存器中的4位,则这4个位位于中断优先级寄存器的bit 4、bit5、bit6、bit7位。剩余的bit0 ~ bit3可以设置成任何值。所以FreeRTOS中的中断优先级计算是有移位操作的。 CMSIS以及不同的微控制器供应商提供了可以设置某个中断优先级的库函数。一些库函数的参数使用最低位对齐,另一些库函数的参数可能使用最高位对齐,所以,使用时应该查阅库函数的应用手册进行正确设置。 可以在FreeRTOSConfig.h中设置宏configMAX_SYSCALL_INTERRUPT_PRIORITY和configKERNEL_INTERRUPT_PRIORITY的值。(关于这两个宏可以参考参数设置一章,网址:http://openmcu.net/post/kernel-config.html)。这两个宏需要根据Cortex-M内核自身的情况进行设置,要以最高有效位对齐。比如某MCU使用中断优先级寄存器中的4位,设置configKERNEL_INTERRUPT_PRIORITY的值为5,则代码为: #define configKERNEL_INTERRUPT_PRIORITY (5<<(8-4)) 对于每一个官方FreeRTOS演示例程,这也是在FreeRTOSConfig.h中要设置宏configKERNEL_INTERRUPT_PRIORITY为最低优先级时,为什么要将它设置为255(1111 1111B)的原因。使用这种方式指定这个值的原因是:FreeRTOS内核是直接在Cortex-M内核硬件上运行的(没有使用第三方接口库函数),要比大多数库函数先运行。现在也有开发第一个Cortex-M库函数计划。 |
|
|
|
|
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1614 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1541 浏览 1 评论
970 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
682 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1592 浏览 2 评论
1863浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
644浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
515浏览 3评论
531浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
504浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 01:37 , Processed in 0.894443 second(s), Total 80, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号