- 2025-01-18
-
回复了主题帖:
《深度学习的数学——使用Python语言》第6章 线性代数进阶学习笔记
Jacktang 发表于 2025-1-18 09:52
线性代数还可以这样编程学习,厉害
这个Python做线性代数太方便了 太高效了
-
回复了主题帖:
《Linux内核深度解析》第4章 异常与中断学习
hellokitty_bean 发表于 2025-1-17 21:19
楼主让人敬佩呀。。。。。。。。。。。。。。
不做内核开发的方向,还能沉下心来研究。。。。。。。
太难了 基本放弃了 完全搞不明白
- 2025-01-17
-
发表了主题帖:
《Linux内核深度解析》第4章 异常与中断学习
在ARM64和MIPS这些精简指令集计算机(RISC)体系结构中,中断系统调用和其他打断程序正常执行流的事件统称为异常,这是广义的异常。
异常级别,4个异常级别0-3
异常分为同步异常和异步异常
同步异常是试图执行指令时产生的异常,或是作为指令的执行结果生成的异常。
同步异常包括:
(1)系统调用,例如异常级别0使用SVC指令陷入异常级别1
(2)数据中止,即访问数据时的页错误异常,虚拟地址没有映射到物理地址,或者没有写权限
(3)指令中止,即取指令时的页错误异常,虚拟地址没有映射到物理地址,或没有执行权限
(4)栈指针或指令地址没有对齐
(5)没有定义的指令
(6)调试异常
异步异常,不是由正在执行的指令生成的,和正在执行的指令没有关联
中断
快速中断
系统错误
异步异常感觉更像是MCU里面的一些异常,中断类似于STM32的NVIC中断
系统错误也是类似于STM32的一些HARDFAULT、总线fault或者NMI等
LINUX的异常和MCU也是一样,有一些异常向量表。
异常处理的流程
把当前的处理器状态保存在寄存器SPSR_EL1程序状态寄存器中
把返回地址保存在寄存器ELR_EL1异常链接寄存器中
把处理器状态的DAIF这4个异常掩码位都设置为1.禁止这4种异常
如果是同步异常或系统错误异常,把生成异常的原因保存在寄存器ESR_EL1异常症状寄存器中
如果是同步异常,把错误地址保存在寄存器FAR_EL1错误地址寄存器中
根据向量基准地址寄存器异常类型和生成异常的异常级别额计算出异常向量的虚拟地址,执行异常向量。
这段异常处理流程,其实和CORTEX M3的异常处理流程也是有类似的
LINUX中断处理
第一层处理函数是中断描述符的成员handle_irq()
第二层处理函数是设备驱动程序注册的处理函数。中断描述符由一个中断处理链表(irq_desc.action),每个中断处理描述符(irq_action)保存设备驱动程序注册的处理函数。因为多个设备可以共享同一个硬件中断号,所以中断处理链表可能挂载多个中断处理描述符。
源码在kerne/irq/irqdesc.c函数文件里
用来存用户注册的中断处理函数,一个中断可以有多个处理函数 ,当一个中断有多个处理函数,说明这个是共享中断,所谓共享中断就是一个中断的来源有很多,这些来源共享同一个引脚。所以在irq_desc结构体中的action成员是个链表,以action为表头,若是一个以上的链表就是共享中断
struct irq_desc {
struct irq_data irq_data;
unsigned int __percpu *kstat_irqs;------IRQ的统计信息
irq_flow_handler_t handle_irq;--------(1)
struct irqaction *action; -----------(2)
unsigned int status_use_accessors;-----中断描述符的状态,参考IRQ_xxxx
unsigned int core_internal_state__do_not_mess_with_it;----(3)
unsigned int depth;----------(4)
unsigned int wake_depth;--------(5)
unsigned int irq_count; ---------(6)
unsigned long last_unhandled;
unsigned int irqs_unhandled;
raw_spinlock_t lock;-----------(7)
struct cpumask *percpu_enabled;-------(8)
#ifdef CONFIG_SMP
const struct cpumask *affinity_hint;----和irq affinity相关,后续单独文档描述
struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
#endif
unsigned long threads_oneshot; -----(9)
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;--------该IRQ对应的proc接口
#endif
int parent_irq;
struct module *owner;
const char *name;
} ____cacheline_internodealigned_in_smp
在Linux内核中,中断处理流程通常涉及多个函数,其中handle_irq_event_percpu是处理中断事件的关键函数之一。该函数通过调用在request_irq中注册的中断处理程序(ISR)来处理中断事件。具体来说,当CPU接收到一个中断时,会调用handle_irq_event函数,该函数进一步调用handle_irq_event_percpu来处理每个CPU上的中断事件
中断描述符的初始化的接口kerne/irq/irqdesc.c函数文件里,在这个文件里有两个Irq的初始化,越往下看越复杂,完全超出了我的能力了,不做内核开发方向的,看这些真的很费劲很难。
- 2025-01-15
-
加入了学习《深入理解无刷直流电机(BLDC)原理以及控制》,观看 BLDC电机驱动的基本需求
-
回复了主题帖:
新年新挑战,任务打卡赢好礼!
-
加入了学习《深入理解无刷直流电机(BLDC)原理以及控制》,观看 BLDC电机结构和优势
-
回复了主题帖:
[极海APM32M3514电机]+PWM使用及电机运行
楼主 你的电机和驱动用的是啥型号
-
回复了主题帖:
[树莓派5测评]⑤安装SSD并从SSD启动+闭坑指南
微雪现在配件好多啊
-
回复了主题帖:
为什么单片机会有3.3V和5V电压等级的区分?
和工艺有一定关系
- 2025-01-14
-
发表了主题帖:
《深度学习的数学——使用Python语言》第6章 线性代数进阶学习笔记
方阵学习
方阵在线性代数的世界里有特殊的地位。
转置
对军阵进行转置是指将矩阵中的行元素和列元素关于主对角线对调。
在numpy中,我们可以通过调用transpose函数对数组进行转置
a2 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(a2)
print(a2.transpose())
print("转置矩阵矩阵是:\n")
print(a2.T)
执行结果
矩阵的迹
例程
b = np.array([[1, 2], [5, 6]])
print(np.diag(b))
print("矩阵的迹是:\n")
print(np.trace(b))
结果
矩阵的矩阵幂运算
调用matrix_power进行幂运算
yu = np.array([[1, 2], [5, 6]])
print("矩阵的幂是:\n")
print(matrix_power(yu, 2))
结果
特征向量和特征值
我们可以通过np.linalg.eig函数得到矩阵的特征向量和特征值
yi = np.array([[1, 2], [-2, -3]])
print("矩阵的特征值和特征向量是:\n")
print(np.linalg.eig(yi)[0])
print(np.linalg.eig(yi)[1])
Np.linalg.eig函数将返回一个列表,其中的第一项是由矩阵的所有特征值构成的向量,第二项则是一个矩阵,其中的每一列元素是与各个特征值对应的特征向量。
-
回复了主题帖:
【STM32H7S78-DK测评】1.收到板卡,简单测评
恭喜恭喜
我的还没收到
- 2025-01-13
-
回复了主题帖:
安装e2 studio时出错
1、通过搜索引擎查找“api-ms-win-core-path-l1-1-0.dll”文件资源;
2、检查从可信赖的网站,根据电脑操作系统版本(32位或64位)下载对应的DLL文件资源;
3、将下载的文件,根据操作系统版本放在对应的文件中:
(1)32个的操作系统,放置在C:\Windows\System32目录下;
(2)64个的操作系统,放置在C:\Windows\System64目录下;
-
回复了主题帖:
被甲方客户甩锅,灵机一动再甩回去
是的
做项目得留好各种记录,PM一有问题各种追责
- 2025-01-12
-
回复了主题帖:
《Linux内核深度解析》第三章 内存学习笔记2
Jacktang 发表于 2025-1-8 07:32
bootmem和memblock在内存管理上区别比较大
是的 我也是刚学习到这个
- 2025-01-10
-
回复了主题帖:
【回顾2024,展望2025】新年抢楼活动来啦!
立一个新年Flag
新的一年越来越好吧!
-
回复了主题帖:
【回顾2024,展望2025】新年抢楼活动来啦!
最想要什么支持
希望2025 EE有更丰富多样的测评板卡
-
回复了主题帖:
【回顾2024,展望2025】新年抢楼活动来啦!
最想关注什么技术?
最想关注带AI的MCU 想看看到底哪些产品和行业需要这种
- 2025-01-08
-
回复了主题帖:
【测评入围名单(第一批)】年终回炉:FPGA、AI、高性能MCU、书籍等65个测品邀你来~
个人信息无误,可按时完成测评计划
- 2025-01-07
-
发表了主题帖:
《Linux内核深度解析》第三章 内存学习笔记2
Linux内存管理是指操作系统对于计算机系统中的内存资源进行有效利用和管理的过程。它包括了内存分配、内存映射等一些方面
一 引导内存分配器
在内核初始化的过程中需要分配内存,内核提供了临时的引导内存分配器,在页分配器和块分配器初始化完毕后,把空闲的物理页交给页分配器管理,丢弃引导内存分配器
早期使用的引导分配器是bootmem,目前正在使用memblock取代bootmem
查看bootmem_data数据结构
typedef struct bootmem_data {
unsigned long node_min_pfn;
unsigned long node_low_pfn;
void *node_bootmem_map;
unsigned long last_end_off;
unsigned long hint_idx;
struct list_head list;
} bootmem_data_t;
Memblock数据结构
truct memblock_type {
unsigned long cnt; /* number of regions */
unsigned long max; /* size of the allocated array */
phys_addr_t total_size; /* size of all regions */
struct memblock_region *regions;
char *name;
};
二伙伴分配器
内核初始化完毕后,使用页分配器管理物理页,当前使用的页分配器是伙伴分配器,伙伴分配器的特点是算法简单
Memblock(引导内存分配器)完成对应的工作后,将未使用的内存释放至伙伴系统,完成伙伴系统的初始化工作。start_kernel()->mm_init()->mem_init()->memblock_free_all()。
伙伴系统是一个结合了2的方幂个分配器和空闲缓冲区合并计技术的内存分配方案。
typedef struct pglist_data {
//... ...
//存储与当前节点相关的内存区域数组
struct zone node_zones[MAX_NR_ZONES];
//MAX_ZONELISTS个备用区域数组
struct zonelist node_zonelists[MAX_ZONELISTS];
int nr_zones; /* 当前节点中填充的区域数量 */
//... ...
} pg_data_t;
struct zonelist {
struct zoneref _zonerefs[MAX_ZONES_PER_ZONELIST + 1];
};
struct zoneref {
struct zone *zone; /* Pointer to actual zone */
int zone_idx; /* zone_idx(zoneref->zone) */
};
网上大佬的分析:内存被分成含有很多页面的大块, 每一块都是2个页面大小的方幂。如果找不到想要的块, 一个大块会被分成两部分, 这两部分彼此就成为伙伴。其中一半被用来分配, 而另一半则空闲。这些块在以后分配的过程中会继续被二分直至产生一个所需大小的块。当一个块被最终释放时, 其伙伴将被检测出来, 如果伙伴也空闲则合并两者。
三 块分配器
为了解决小块内存的分配问题,LINUX内核提供了块分配器,最早实现的块分配器是SLAB分配器。
因为上一个伙伴分配器,buddy是以页框为分配单元,那对于小于一页的内存需求又该如何处理呢,如果直接分配一页,就浪费了宝贵的内存空间,形成了内碎片。
SLAB分配器的作用不仅仅是分配小块内存,更重要的作用是针对经常分配和释放的对象充当缓存。
通用的内存缓存的编程接口
- 2025-01-04
-
发表了主题帖:
《Linux内核深度解析》第三章 内存学习笔记1
内存学习之内存映射学习笔记
内存管理架构可以分为:用户空间、内核空间及硬件部分3个层面:
用户空间:应用程序使用malloc()申请内存资源/free()释放内存资源。
内核空间:内核总是驻留在内存中,是操作系统的一部分。内核空间为内核保留,不允许应用程序读写该区域的内容或直接调用内核代码定义的函数。
硬件:处理器包含一个内存管理单元Memory Management Uint,MMU的部件,负责把虚拟地址转换为物理地址。内存管理单元包含一个页表缓存的部件,保存最近使用过的页表映射,避免每次把虚拟地址转换成物理地址都需要查询内存中的页表。
Linux内核的核心功能:
不管是用户空间还是内核空间,使用的地址都是虚拟地址,当需进程要实际访问内存的时候,会由内核的「请求分页机制」产生「缺页异常」调入物理内存页
程序只能通过虚拟地址访问外设寄存器,内核提供了一下函数来把外设寄存器的物理地址映射到虚拟地址空间
(1)ioremap()把外设寄存器的物理地址映射到内核虚拟地址空间
(2)io_remap_pfn_range()把外设寄存器的物理地址映射到进程的用户虚拟地址空间
内存映射是在进程的虚拟地址空间中创建一个映射,分为2种
文件映射:文件支持的内存映射,把文件的一个区间映射到进程的虚拟地址空间,数据源是存储设备上的文件
匿名映射:没有文件支持的内存映射,把物理内存映射到进程的虚拟地址空间,没有数据源
也可以说内存映射就是磁盘文件的数据映射到内存,用户可以通过修改内存就能修改磁盘文件。简单来说就是将同一个文件存储映射到不同的进程中,两个进程通过改变文件内容(读写内存)来实现通信,不必在使用read和write函数等系统调用,加快文件的读取和写入。
原型: void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offsize);
返回值: 成功则返回映射区起始地址, 失败则返回MAP_FAILED(-1).
参数:
1.addr: 指定映射的起始地址, 通常设为NULL, 由系统指定.
2.length: 将文件的多大长度映射到内存.
3.prot: 映射区的保护方式, 可以是:
PROT_EXEC: 映射区可被执行.
PROT_READ: 映射区可被读取.
PROT_WRITE: 映射区可被写入.
PROT_NONE: 映射区不能存取.
4.flags: 映射区的特性, 可以是:
MAP_SHARED: 对映射区域的写入数据会复制回文件, 且允许其他映射该文件的进程共享.
MAP_PRIVATE: 对映射区域的写入操作会产生一个映射的复制(copy-on-write), 对此区域所做的修改不会写回原文件.
fd: 由open返回的文件描述符, 代表要映射的文件.
offset: 以文件开始处的偏移量, 必须是分页大小的整数倍, 通常为0, 表示从文件头开始映射.
测试例程
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/mman.h>
int main(int argc, const char* argv[]) {
int fd = -1;
int ret = -1;
void* addr = NULL;
// 1.以读写的方式打开txt文件
fd = open("txt", O_RDWR);
if (-1 == fd) {
perror("open");
return 1;
}
// 2.将文件映射到进程的虚拟地址空间
addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
perror("mmap");
return 1;
}
printf("映射成功.\n");
// 3.关闭文件
close(fd);
// 4.写文件
memcpy(addr, "123456", 6);
// 5.断开存储映射
munmap(addr, 1024);
return 0;
}
执行结果