《Linux内核深度解析》选载之引导内存分配器

原创 Linux阅码场 2022-07-22 11:00

Ftrace训练营火热报名中:Ftrace训练营:站在设计者的角度来理解ftrace(限50人)。训练营第一期报名已圆满成功,好评如潮。第二期课程正在进行中,第三期报名正在火爆进行中(咨询小月微信:linuxer2016)。


ARM安全架构训练营2期火热报名中:阅码场训练营:ARM安全架构之Trustzone/TEE实战--【介绍视频】。报名咨询客服(小月微信:linuxer2016)。


ARM架构与调优调试训练营火热报名中:阅码场训练营:ARM架构与调试调优。报名咨询客服(小月微信:linuxer2016)。



阅码场用户程磊对《Linux内核深度解析》推荐如下:

1.语言浅显易懂,内容深入浅出。

2.逻辑清晰,条理分明,逐步深入,层层递进。

3.基于较新的4.12内核版本,很多经典内核书籍虽然写的都非常好,但是都是基于2.6内核,很多在2.6之后引入的新技术并没有讲到,而本书对这些新技术都有非常详细的讲解。




作者简介:

余华兵,2005年毕业于华中科技大学计算机学院,取得硕士学位。毕业后的十余年一直在网络通信行业从事软件设计和开发工作,研究方向包括IPv4协议栈、IPv6协议栈和Linux内核。


目录

3.6 引导内存分配器 

3.6.1 bootmem分配器 
3.6.2 memblock分配器 
3.6.3 物理内存信息 


3.6 引导内存分配器


在内核初始化的过程中需要分配内存,内核提供了临时的引导内存分配器,在页分配器和块分配器初始化完毕后,把空闲的物理页交给页分配器管理,丢弃引导内存分配器。


早期使用的引导内存分配器是 bootmem,目前正在使用 memblock 取代 bootmem。如果开启配置宏 CONFIG_NO_BOOTMEMmemblock 就会取代 bootmem。为了保证兼容性,bootmem memblock 提供了相同的接口。

3.6.1 bootmem 分配器


bootmem 分配器使用的数据结构如下:


include/linux/bootmem.h 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;

下面解释结构体 bootmem_data 的成员。


1node_min_pfn 是起始物理页号。


2node_low_pfn 是结束物理页号。


3node_bootmem_map 指向一个位图,每个物理页对应一位,如果物理页被分配,把对应的位设置为 1


4last_end_off 是上次分配的内存块的结束位置后面一个字节的偏移。


5hint_idx 的字面意思是“暗示的索引”,是上次分配的内存块的结束位置后面的物理页在位图中的索引,下次优先考虑从这个物理页开始分配。


每个内存节点有一个 bootmem_data 实例:

include/linux/mmzone.h typedef struct pglist_data { #ifndef CONFIG_NO_BOOTMEM  struct bootmem_data *bdata; #endif } pg_data_t;


bootmem 分配器的算法如下。


1)只把低端内存添加到 bootmem 分配器,低端内存是可以直接映射到内核虚拟地址空间的物理内存。


2)使用一个位图记录哪些物理页被分配,如果物理页被分配,把这个物理页对应的位设置成 1


3)采用最先适配算法,扫描位图,找到第一个足够大的空闲内存块。


4)为了支持分配小于一页的内存块,记录上次分配的内存块的结束位置后面一个字节的偏移和后面一页的索引,下次分配时,从上次分配的位置后面开始尝试。如果上次分配的最后一个物理页的剩余空间足够,可以直接在这个物理页上分配内存。


bootmem 分配器对外提供的分配内存的函数是 alloc_bootmem 及其变体,释放内存的函数是 free_bootmem。分配内存的核心函数是源文件“mm/bootmem.c”中的函数 alloc_bootmem_bdata

ARM64 架构的内核已经不使用 bootmem 分配器,但是其他处理器架构还在使用 bootmem分配器。


3.6.2 memblock 分配器


1.数据结构


memblock 分配器使用的数据结构如下:


include/linux/memblock.h struct memblock {  bool bottom_up; /* 是从下向上的方向?*/  phys_addr_t current_limit;  struct memblock_type memory;  struct memblock_type reserved; #ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP  struct memblock_type physmem; #endif };

成员 bottom_up 表示分配内存的方式,值为真表示从低地址向上分配,值为假表示从高地址向下分配。


成员 current_limit 是可分配内存的最大物理地址。


接下来是 3 种内存块:memory 是内存类型(包括已分配的内存和未分配的内存),reserved 是预留类型(已分配的内存),physmem 是物理内存类型。物理内存类型和内存类型的区别是:内存类型是物理内存类型的子集,在引导内核时可以使用内核参数“mem=nn[KMG]指定可用内存的大小,导致内核不能看见所有内存;物理内存类型总是包含所有内存范围,内存类型只包含内核参数“mem=”指定的可用内存范围。

内存块类型的数据结构如下:


include/linux/memblock.h struct memblock_type {  unsigned long cnt; /* 区域数量 */  unsigned long max; /* 已分配数组的大小 */  phys_addr_t total_size; /* 所有区域的长度 */  struct memblock_region *regions;  char *name; };

内存块类型使用数组存放内存块区域,成员 regions 指向内存块区域数组,cnt 是内存块区域的数量,max 是数组的元素个数,total_size 是所有内存块区域的总长度,name 是内存块类型的名称。


内存块区域的数据结构如下:


include/linux/memblock.h struct memblock_region {  phys_addr_t base;  phys_addr_t size;  unsigned long flags; #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP  int nid; #endif }; /* memblock标志位的定义. */ enum {  MEMBLOCK_NONE = 0x0, /* 无特殊要求 */  MEMBLOCK_HOTPLUG = 0x1, /* 可热插拔区域 */  MEMBLOCK_MIRROR = 0x2, /* 镜像区域 */  MEMBLOCK_NOMAP = 0x4, /* 不添加到内核直接映射 */ };

成员 base 是起始物理地址,size 是长度,nid 是节点编号。成员 flags 是标志,可以是MEMBLOCK_NONE 或其他标志的组合。

1MEMBLOCK_NONE 表示没有特殊要求的区域。


2MEMBLOCK_HOTPLUG 表示可以热插拔的区域,即在系统运行过程中可以拔出或插入物理内存。


3MEMBLOCK_MIRROR 表示镜像的区域。内存镜像是内存冗余技术的一种,工作原理与硬盘的热备份类似,将内存数据做两个复制,分别放在主内存和镜像内存中。


4MEMBLOCK_NOMAP 表示不添加到内核直接映射区域(即线性映射区域)。


2.初始化


源文件“mm/memblock.c”定义了全局变量 memblock,把成员 bottom_up 初始化为假,表示从高地址向下分配。


ARM64 内核初始化 memblock 分配器的过程是:


1)解析设备树二进制文件中的节点“/memory”,把所有物理内存范围添加到 memblock.memory,具体过程参考 3.6.3 节。

2)在函数 arm64_memblock_init 中初始化 memblock


函数 arm64_memblock_init 的主要代码如下:


start_kernel() ->setup_arch() -> arm64_memblock_init() arch/arm64/mm/init.c1 void __init arm64_memblock_init(void) 2 { 3 const s64 linear_region_size = -(s64)PAGE_OFFSET; 4 5 fdt_enforce_memory_region(); 6 7 memstart_addr = round_down(memblock_start_of_DRAM(), 8 ARM64_MEMSTART_ALIGN); 146 3.6 引导内存分配器9 10 memblock_remove(max_t(u64, memstart_addr + linear_region_size, 11 __pa_symbol(_end)), ULLONG_MAX); 12 if (memstart_addr + linear_region_size < memblock_end_of_DRAM()) { 13 /* 确保memstart_addr严格对齐 */ 14 memstart_addr = round_up(memblock_end_of_DRAM() - linear_region_size, 15 ARM64_MEMSTART_ALIGN); 16 memblock_remove(0, memstart_addr); 17 } 18 19 if (memory_limit != (phys_addr_t)ULLONG_MAX) { 20 memblock_mem_limit_remove_map(memory_limit); 21 memblock_add(__pa_symbol(_text), (u64)(_end - _text)); 22 } 23 24 25 memblock_reserve(__pa_symbol(_text), _end - _text); 26 27 28 early_init_fdt_scan_reserved_mem(); 29 30 }

5 行代码,调用函数 fdt_enforce_memory_region 解析设备树二进制文件中节点“/chosen的属性“linux,usable-memory-range”,得到可用内存的范围,把超出这个范围的物理内存范围从 memblock.memory 中删除。

7 行和第 8 行代码,全局变量 memstart_addr 记录内存的起始物理地址。


1017 行代码,把线性映射区域不能覆盖的物理内存范围从 memblock.memory 中删除。


1922 行代码,设备树二进制文件中节点“/chosen”的属性“bootargs”指定的命令行中,可以使用参数“mem”指定可用内存的大小。如果指定了内存的大小,那么把超过可用长度的物理内存范围从 memblock.memory 中删除。因为内核镜像可以被加载到内存的高地址部分,并且内核镜像必须是可以通过线性映射区域访问的,所以需要把内核镜像占用的物理内存范围重新添加到 memblock.memory 中。

25 行代码,把内核镜像占用的物理内存范围添加到 memblock.reserved 中。


28 行代码,从设备树二进制文件中的内存保留区域(memory reserve map,对应设备树源文件的字段“/memreserve/”)和节点“/reserved-memory”读取保留的物理内存范围,添加到 memblock.reserved 中。

3.编程接口


memblock 分配器对外提供的接口如下。


1memblock_add:添加新的内存块区域到 memblock.memory 中。

2memblock_remove:删除内存块区域。

3memblock_alloc:分配内存。

4memblock_free:释放内存。


为了兼容 bootmem 分配器,memblock 分配器也实现了 bootmem 分配器提供的接口。如果开启配置宏 CONFIG_NO_BOOTMEMmemblock 分配器就完全替代了 bootmem 分配器。

4.算法


memblock 分配器把所有内存添加到 memblock.memory 中,把分配出去的内存块添加memblock.reserved 中。内存块类型中的内存块区域数组按起始物理地址从小到大排序。

函数 memblock_alloc 负责分配内存,把主要工作委托给函数 memblock_alloc_range_nid算法如下。


1)调用函数 memblock_find_in_range_node 以找到没有分配的内存块区域,默认从高地址向下分配。


函数 memblock_find_in_range_node 有两层循环,外层循环从高到低遍历 memblock.memory的内存块区域数组;针对每个内存块区域 M1,执行内层循环,从高到低遍历 memblock.reserved的内存块区域数组。针对每个内存块区域 M2,目标区域是内存块区域 M2 和前一个内存块区域之间的区域,如果目标区域属于内存块区域 M1,并且长度大于或等于请求分配的长度,那么可以从目标区域分配内存。


2)调用函数 memblock_reserve,把分配出去的内存块区域添加到 memblock.reserved 中。


函数 memblock_free 负责释放内存,只需要把内存块区域从 memblock.reserved 中删除。


3.6.3 物理内存信息


在内核初始化的过程中,引导内存分配器负责分配内存,问题是:引导内存分配器怎么知道内存的大小和物理地址范围?


ARM64 架构使用扁平设备树(Flattened Device TreeFDT)描述板卡的硬件信息,好处是可以把板卡特定的代码从内核中删除,编译生成通用的板卡无关的内核。驱动开发者编写设备树源文件(Device Tree SourceDTS),存放在目录“arch/arm64/boot/dts”下,然后使用设备树编译器(Device Tree CompilerDTC)把设备树源文件转换成设备树二进制文件(Device Tree BlobDTB),接着把设备树二进制文件写到存储设备上。设备启动时,引导程序把设备树二进制文件从存储设备读到内存中,引导内核的时候把设备树二进制文件的起始地址传给内核,内核解析设备树二进制文件后得到硬件信息。


设备树源文件是文本文件,扩展名是“.dts”,描述物理内存布局的方法如下:


/ {  #address-cells = <2>;  #size-cells = <2>;  memory@80000000 {  device_type = "memory";  reg = <0x00000000 0x80000000 0 0x80000000>,  <0x00000008 0x80000000 0 0x80000000>;  }; };


/”是根节点。


属性“#address-cells”定义一个地址的单元数量,属性“#size-cells”定义一个长度的单元数量。单元(cell)是一个 32 位数值,属性“#address-cells = <2>”表示一个地址由两个单元组成,即地址是一个 64 位数值;属性“#size-cells = <2>”表示一个长度由两个单元组成,即长度是一个 64 位数值。

memory”节点描述物理内存布局,“@”后面的设备地址用来区分名字相同的节点,如果节点有属性“reg”,那么设备地址必须是属性“reg”的第一个地址。如果有多块内存,可以使用多个“memory”节点来描述,也可以使用一个“memory”节点的属性“reg”的地址/长度列表来描述。

属性“device_type”定义设备类型,“memory”节点的属性“device_type”的值必须是memory”。

属性“reg”定义物理内存范围,值是一个地址/长度列表,每个地址包含的单元数量是由根节点的属性“#address-cells”定义的,每个长度包含的单元数量是由根节点的属性#size-cells”定义的。在上面的例子中,第一个物理内存范围的起始地址是“0x00000000 0x80000000”,长度是“0 0x80000000”,即起始地址是 2GB,长度是 2GB;第二个物理内存范围的起始地址是“0x00000008 0x80000000”,长度是“0 0x80000000”,即起始地址是34GB,长度是 2GB

内核在初始化的时候调用函数 early_init_dt_scan_nodes 以解析设备树二进制文件,从而得到物理内存信息。


start_kernel() ->setup_arch() ->setup_machine_fdt() ->early_init_dt_scan() ->early_init_dt_scan_nodes() drivers/of/fdt.c1 void __init early_init_dt_scan_nodes(void) 2 { 3 4 /* 初始化size-cells和address-cells信息 */ 5 of_scan_flat_dt(early_init_dt_scan_root, NULL); 6 7 /* 调用函数early_init_dt_add_memory_arch设置内存 */ 8 of_scan_flat_dt(early_init_dt_scan_memory, NULL); 9 }

5 行代码,调用函数 early_init_dt_scan_root,解析根节点的属性“#address-cells”得到地址的单元数量,保存在全局变量 dt_root_addr_cells 中;解析根节点的属性“#size-cells得到长度的单元数量,保存在全局变量 dt_root_size_cells 中。

8 行代码,调用函数 early_init_dt_scan_memory,解析“memory”节点得到物理内存布局。


函数 early_init_dt_scan_memory 负责解析“memory”节点,其主要代码如下:


drivers/of/fdt.c1 int __init early_init_dt_scan_memory(unsigned long node, const char *uname, 2 int depth, void *data) 3 { 4 const char *type = of_get_flat_dt_prop(node, "device_type", NULL); 5 const __be32 *reg, *endp; 6 int l; 7 8 9 /* 只扫描 "memory" 节点 */ 10 if (type == NULL) { 11 /* 如果没有属性“device_type”,判断节点名称是不是“memory@0”*/ 12 if (!IS_ENABLED(CONFIG_PPC32) || depth != 1 || strcmp(uname, "memory@0") != 0) 13 return 0; 14 } else if (strcmp(type, "memory") != 0) 15 return 0; 1617 reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l); 18 if (reg == NULL) 19 reg = of_get_flat_dt_prop(node, "reg", &l); 20 if (reg == NULL) 21 return 0; 22 23 endp = reg + (l / sizeof(__be32)); 24 25 26 while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { 27 u64 base, size; 28 29 base = dt_mem_next_cell(dt_root_addr_cells, ®); 30 size = dt_mem_next_cell(dt_root_size_cells, ®); 31 32 if (size == 0) 33 continue; 34 35 early_init_dt_add_memory_arch(base, size); 36 37 } 38 39 return 0; 40 }

4 行代码,解析节点的属性“device_type”。


14 行代码,如果属性“device_type”的值是“memory”,说明这个节点描述物理内存信息。


1719 行代码,解析属性“linux,usable-memory”,如果不存在,那么解析属性“reg”。这两个属性都用来定义物理内存范围。


2637 行代码,解析出每块内存的起始地址和大小后,调用函数 early_init_dt_add_memory_arch

函数 early_init_dt_add_memory_arch 的主要代码如下:

drivers/of/fdt.cvoid __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) {  const u64 phys_offset = MIN_MEMBLOCK_ADDR;  if (!PAGE_ALIGNED(base)) {  if (size < PAGE_SIZE - (base & ~PAGE_MASK)) {  pr_warn("Ignoring memory block 0x%llx - 0x%llx\n",  base, base + size);  return;  }  size -= PAGE_SIZE - (base & ~PAGE_MASK);  base = PAGE_ALIGN(base);  }  size &= PAGE_MASK;  if (base > MAX_MEMBLOCK_ADDR) {  pr_warning("Ignoring memory block 0x%llx - 0x%llx\n",  base, base + size);  return;  }  if (base + size - 1 > MAX_MEMBLOCK_ADDR) {  pr_warning("Ignoring memory range 0x%llx - 0x%llx\n",  ((u64)MAX_MEMBLOCK_ADDR) + 1base + size);  size = MAX_MEMBLOCK_ADDR - base + 1;  }  if (base + size < phys_offset) {  pr_warning("Ignoring memory block 0x%llx - 0x%llx\n",  base, base + size);  return;  }  if (base < phys_offset) {  pr_warning("Ignoring memory range 0x%llx - 0x%llx\n",  base, phys_offset);  size -= phys_offset - base;  base = phys_offset;  } memblock_add(base, size); }

函数 early_init_dt_add_memory_arch 对起始地址和长度做了检查以后,调用函数memblock_add 把物理内存范围添加到 memblock.memory 中。


感兴趣想进群者可以添加小月微信(linuxer2016)邀请进群一起交流学习




连载已发布文章:

《Linux内核深度解析》选载之物理内存组织

《Linux内核深度解析》选载之内存映射

《Linux内核深度解析》选载之内存地址空间


精华文章:【精华】Linux阅码场原创精华文章汇总



阅码场付费会员专业交流群

会员招募:各专业群会员费为88元/季度,权益包含群内提问,线下活动8折,全年不定期群技术分享(普通用户直播免费,分享后每次点播价为19元/次),有意加入请私信客服小月(小月微信号:linuxer2016)


专业群介绍:

彭伟林-阅码场内核性能与稳定性
本群定位内核性能与稳定性技术交流,覆盖云/网/车/机/芯领域资深内核专家,由阅码场资深讲师彭伟林主持。


甄建勇-性能优化与体系结构

本群定位Perf、cache和CPU架构技术交流,覆盖云/网/车/机/芯领域资深用户,由阅码场资深讲师甄建勇主持。


李春良-Xenomai与实时优化

本群定位Xenomai与实时优化技术交流,覆盖云/网/车/机/芯领域资深用户,由阅码场资深讲师李春良和彭伟林共同主持。


周贺贺-Tee和ARM架构

本群定位Tee和ARM架构技术交流,覆盖云/网/车/机/芯领域资深用户,由阅码场资深讲师周贺贺主持。


谢欢-Linux tracers

本群定位Linux tracers技术交流,覆盖云/网/车/机/芯领域资深用户,由阅码场资深讲师谢欢主持。







Linux阅码场 专业的Linux技术社区和Linux操作系统学习平台,内容涉及Linux内核,Linux内存管理,Linux进程管理,Linux文件系统和IO,Linux性能调优,Linux设备驱动以及Linux虚拟化和云计算等各方各面.
评论
  • 「他明明跟我同梯进来,为什么就是升得比我快?」许多人都有这样的疑问:明明就战绩也不比隔壁同事差,升迁之路却比别人苦。其实,之间的差异就在于「领导力」。並非必须当管理者才需要「领导力」,而是散发领导力特质的人,才更容易被晓明。许多领导力和特质,都可以通过努力和学习获得,因此就算不是天生的领导者,也能成为一个具备领导魅力的人,进而被老板看见,向你伸出升迁的橘子枝。领导力是什么?领导力是一种能力或特质,甚至可以说是一种「影响力」。好的领导者通常具备影响和鼓励他人的能力,并导引他们朝着共同的目标和愿景前
    优思学院 2025-01-08 14:54 93浏览
  • 根据环洋市场咨询(Global Info Research)项目团队最新调研,预计2030年全球中空长航时无人机产值达到9009百万美元,2024-2030年期间年复合增长率CAGR为8.0%。 环洋市场咨询机构出版了的【全球中空长航时无人机行业总体规模、主要厂商及IPO上市调研报告,2025-2031】研究全球中空长航时无人机总体规模,包括产量、产值、消费量、主要生产地区、主要生产商及市场份额,同时分析中空长航时无人机市场主要驱动因素、阻碍因素、市场机遇、挑战、新产品发布等。报告从中空长航时
    GIRtina 2025-01-09 10:35 67浏览
  • 本文介绍编译Android13 ROOT权限固件的方法,触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。关闭selinux修改此文件("+"号为修改内容)device/rockchip/common/BoardConfig.mkBOARD_BOOT_HEADER_VERSION ?= 2BOARD_MKBOOTIMG_ARGS :=BOARD_PREBUILT_DTB
    Industio_触觉智能 2025-01-08 00:06 111浏览
  • HDMI 2.2 规格将至,开启视听新境界2025年1月6日,HDMI Forum, Inc. 宣布即将发布HDMI规范2.2版本。新HDMI规范为规模庞大的 HDMI 生态系统带来更多选择,为创建、分发和体验理想的终端用户效果提供更先进的解决方案。新技术为电视、电影和游戏工作室等内容制作商在当前和未来提供更高质量的选择,同时实现多种分发平台。96Gbps的更高带宽和新一代 HDMI 固定比率速率传输(Fixed Rate Link)技术为各种设备应用提供更优质的音频和视频。终端用户显示器能以最
    百佳泰测试实验室 2025-01-09 17:33 80浏览
  • 故障现象一辆2017款东风风神AX7车,搭载DFMA14T发动机,累计行驶里程约为13.7万km。该车冷起动后怠速运转正常,热机后怠速运转不稳,组合仪表上的发动机转速表指针上下轻微抖动。 故障诊断 用故障检测仪检测,发动机控制单元中无故障代码存储;读取发动机数据流,发现进气歧管绝对压力波动明显,有时能达到69 kPa,明显偏高,推断可能的原因有:进气系统漏气;进气歧管绝对压力传感器信号失真;发动机机械故障。首先从节气门处打烟雾,没有发现进气管周围有漏气的地方;接着拔下进气管上的两个真空
    虹科Pico汽车示波器 2025-01-08 16:51 108浏览
  • 村田是目前全球量产硅电容的领先企业,其在2016年收购了法国IPDiA头部硅电容器公司,并于2023年6月宣布投资约100亿日元将硅电容产能提升两倍。以下内容主要来自村田官网信息整理,村田高密度硅电容器采用半导体MOS工艺开发,并使用3D结构来大幅增加电极表面,因此在给定的占位面积内增加了静电容量。村田的硅技术以嵌入非结晶基板的单片结构为基础(单层MIM和多层MIM—MIM是指金属 / 绝缘体/ 金属) 村田硅电容采用先进3D拓扑结构在100um内,使开发的有效静电容量面积相当于80个
    知白 2025-01-07 15:02 155浏览
  • 在当前人工智能(AI)与物联网(IoT)的快速发展趋势下,各行各业的数字转型与自动化进程正以惊人的速度持续进行。如今企业在设计与营运技术系统时所面临的挑战不仅是技术本身,更包含硬件设施、第三方软件及配件等复杂的外部因素。然而这些系统往往讲究更精密的设计与高稳定性,哪怕是任何一个小小的问题,都可能对整体业务运作造成严重影响。 POS应用环境与客户需求以本次分享的客户个案为例,该客户是一家全球领先的信息技术服务与数字解决方案提供商,遭遇到一个由他们所开发的POS机(Point of Sal
    百佳泰测试实验室 2025-01-09 17:35 71浏览
  • 在过去十年中,自动驾驶和高级驾驶辅助系统(AD/ADAS)软件与硬件的快速发展对多传感器数据采集的设计需求提出了更高的要求。然而,目前仍缺乏能够高质量集成多传感器数据采集的解决方案。康谋ADTF正是应运而生,它提供了一个广受认可和广泛引用的软件框架,包含模块化的标准化应用程序和工具,旨在为ADAS功能的开发提供一站式体验。一、ADTF的关键之处!无论是奥迪、大众、宝马还是梅赛德斯-奔驰:他们都依赖我们不断发展的ADTF来开发智能驾驶辅助解决方案,直至实现自动驾驶的目标。从新功能的最初构思到批量生
    康谋 2025-01-09 10:04 67浏览
  • 光伏逆变器是一种高效的能量转换设备,它能够将光伏太阳能板(PV)产生的不稳定的直流电压转换成与市电频率同步的交流电。这种转换后的电能不仅可以回馈至商用输电网络,还能供独立电网系统使用。光伏逆变器在商业光伏储能电站和家庭独立储能系统等应用领域中得到了广泛的应用。光耦合器,以其高速信号传输、出色的共模抑制比以及单向信号传输和光电隔离的特性,在光伏逆变器中扮演着至关重要的角色。它确保了系统的安全隔离、干扰的有效隔离以及通信信号的精准传输。光耦合器的使用不仅提高了系统的稳定性和安全性,而且由于其低功耗的
    晶台光耦 2025-01-09 09:58 49浏览
  • 1月7日-10日,2025年国际消费电子产品展览会(CES 2025)盛大举行,广和通发布Fibocom AI Stack,赋智千行百业端侧应用。Fibocom AI Stack提供集高性能模组、AI工具链、高性能推理引擎、海量模型、支持与服务一体化的端侧AI解决方案,帮助智能设备快速实现AI能力商用。为适应不同端侧场景的应用,AI Stack具备海量端侧AI模型及行业端侧模型,基于不同等级算力的芯片平台或模组,Fibocom AI Stack可将TensorFlow、PyTorch、ONNX、
    物吾悟小通 2025-01-08 18:17 63浏览
  • 一个真正的质量工程师(QE)必须将一件产品设计的“意图”与系统的可制造性、可服务性以及资源在现实中实现设计和产品的能力结合起来。所以,可以说,这确实是一种工程学科。我们常开玩笑说,质量工程师是工程领域里的「侦探」、「警察」或「律师」,守护神是"墨菲”,信奉的哲学就是「墨菲定律」。(注:墨菲定律是一种启发性原则,常被表述为:任何可能出错的事情最终都会出错。)做质量工程师的,有时会不受欢迎,也会被忽视,甚至可能遭遇主动或被动的阻碍,而一旦出了问题,责任往往就落在质量工程师的头上。虽然质量工程师并不负
    优思学院 2025-01-09 11:48 90浏览
  • 在智能网联汽车中,各种通信技术如2G/3G/4G/5G、GNSS(全球导航卫星系统)、V2X(车联网通信)等在行业内被广泛使用。这些技术让汽车能够实现紧急呼叫、在线娱乐、导航等多种功能。EMC测试就是为了确保在复杂电磁环境下,汽车的通信系统仍然可以正常工作,保护驾乘者的安全。参考《QCT-基于LTE-V2X直连通信的车载信息交互系统技术要求及试验方法-1》标准10.5电磁兼容试验方法,下面将会从整车功能层面为大家解读V2X整车电磁兼容试验的过程。测试过程揭秘1. 设备准备为了进行电磁兼容试验,技
    北汇信息 2025-01-09 11:24 77浏览
  • 职场是人生的重要战场,既是谋生之地,也是实现个人价值的平台。然而,有些思维方式却会悄无声息地拖住你的后腿,让你原地踏步甚至退步。今天,我们就来聊聊职场中最忌讳的五种思维方式,看看自己有没有中招。1. 固步自封的思维在职场中,最可怕的事情莫过于自满于现状,拒绝学习和改变。世界在不断变化,行业的趋势、技术的革新都在要求我们与时俱进。如果你总觉得自己的方法最优,或者害怕尝试新事物,那就很容易被淘汰。与其等待机会找上门,不如主动出击,保持学习和探索的心态。加入优思学院,可以帮助你快速提升自己,与行业前沿
    优思学院 2025-01-09 15:48 60浏览
  •  在全球能源结构加速向清洁、可再生方向转型的今天,风力发电作为一种绿色能源,已成为各国新能源发展的重要组成部分。然而,风力发电系统在复杂的环境中长时间运行,对系统的安全性、稳定性和抗干扰能力提出了极高要求。光耦(光电耦合器)作为一种电气隔离与信号传输器件,凭借其优秀的隔离保护性能和信号传输能力,已成为风力发电系统中不可或缺的关键组件。 风力发电系统对隔离与控制的需求风力发电系统中,包括发电机、变流器、变压器和控制系统等多个部分,通常工作在高压、大功率的环境中。光耦在这里扮演了
    晶台光耦 2025-01-08 16:03 86浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦