卷一 内核源代码分析 第一章 linux内核对cortex A9多核处理器的支持 1.2 Percpu内存管理 图书试读版-请勿转载

基于aarch64分析kernel源码 三:启动代码分析 1、通过__HEAD将启动代码链接到文件开始位置。2、调用y ,进行初始化相关操作。3、调用调用函数。4、函数进入C语言环境初始化内核。 阅读详情


作者 crosskernel@gmail.com




1.2 Percpu内存管理


随着处理器核心的增加,内核中系统中并发的线程也随之增加,这样对一些共享数据的同时访问机率也就增加,就避免不了spin_lock的使用,而且往往处理器核心越多造成的麻烦越大。Percpu内存对这种数据无能为力,但是内核中有些数据只是处理器局部可见,这种数据不会被别的处理器访问到,不需要加以spin_lock而直接访问。Percpu内存适用于这种数据,而实际上处理器局部数据的实际分配还是通过内核基本slab进行,而Percp内存则是用来存放指向处理器局部数据的指针。


1.2.1 处理器静态定义局部数据


首先在链接文件arch/arm/kernel/vmlinux.lds.S中包含了:
PERCPU_SECTION(32)


展开该宏:


#define PERCPU_SECTION(cacheline) \
. = ALIGN(PAGE_SIZE);\
.data..percpu : AT(ADDR(.data..percpu) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__per_cpu_load) = .;\
PERCPU_INPUT(cacheline)\
}




#define PERCPU_INPUT(cacheline) \
VMLINUX_SYMBOL(__per_cpu_start) = .;\
*(.data..percpu..first)\
. = ALIGN(PAGE_SIZE);\
*(.data..percpu..page_aligned)\
. = ALIGN(cacheline);\
*(.data..percpu..readmostly)\
. = ALIGN(cacheline);\
*(.data..percpu)\
*(.data..percpu..shared_aligned)\
VMLINUX_SYMBOL(__per_cpu_end) = .;


该宏的作用是声明了以 “.data..percpu..first”,“.data..percpu..readmostly”,“.data..percpu”等为名字的数据段,内核代码里如果有变量的属性指出放在该数据段的时候,链接时将其分配到对应的数据段。而且为了能够在内核里直接访问这段区域,定义两个变量__per_cpu_start,和__per_cpu_end。


而另一方面:
#define DEFINE_PER_CPU(type, name) \
DEFINE_PER_CPU_SECTION(type, name, "")


#define DEFINE_PER_CPU_SECTION(type, name, sec) \
__PCPU_ATTRS(sec) PER_CPU_DEF_ATTRIBUTES\
__typeof__(type) name


#define __PCPU_ATTRS(sec) \
__percpu __attribute__((section(PER_CPU_BASE_SECTION sec)))\
PER_CPU_ATTRIBUTES


#define PER_CPU_BASE_SECTION ".data..percpu"


根据以上宏定义展开之:可以得到
__attribute__((section(.data..percpu))) __typeof__(type) name


可见宏“DEFINE_PER_CPU(type, name)”的作用就是将类型为“type”的“name”变量放倒“.data..percpu”数据段。


同样方法可以推出:宏“DECLARE_PER_CPU_READ_MOSTLY(type, name)” 的作用就是将类型为“type”的“name”变量放倒“.data..percpu..readmostly”数据段。这种数据段是cacheline对齐,可以大大提高cache利用率,很适合频繁读取数据,不过在笔者分析的这个内核版本3.0.13中,内核里这种数据还没有大规模使用的。而且cacheline定义为32,对于不同架构可以使用时可以适当调整。


另外宏“DECLARE_PER_CPU_FIRST(type, name)”对应“.data..percpu..first”数据段。
宏“DECLARE_PER_CPU_PAGE_ALIGNED”对应“.data..percpu..first”数据段。…






然后在内核初始化过程中:


void __init setup_per_cpu_areas(void)
{
rc = pcpu_embed_first_chunk(PERCPU_MODULE_RESERVE,
   PERCPU_DYNAMIC_RESERVE, PAGE_SIZE, NULL,
   pcpu_dfl_fc_alloc, pcpu_dfl_fc_free);

delta = (unsigned long)pcpu_base_addr - (unsigned long)__per_cpu_start;
   /*设置每个cpu的__per_cpu_offset 指针*/
for_each_possible_cpu(cpu)
__per_cpu_offset[cpu] = delta + pcpu_unit_offsets[cpu];
}


再看对percpu数据的引用:


#define per_cpu(var, cpu) \
(*SHIFT_PERCPU_PTR(&(var), per_cpu_offset(cpu)))


#define SHIFT_PERCPU_PTR(__p, __offset) ({\
__verify_pcpu_ptr((__p));\
RELOC_HIDE((typeof(*(__p)) __kernel __force *)(__p), (__offset)); \
})


#define per_cpu_offset(x) (__per_cpu_offset[x])


/*选用一个简单易看得RELOC_HIDE 宏定义*/
# define RELOC_HIDE(ptr, off) \
  ({ unsigned long __ptr; \
     __ptr = (unsigned long) (ptr); \
(typeof(ptr)) (__ptr + (off)); })


可见是以当前cpu局部数据“__per_cpu_offset[x]”为基地址的相对寻址。




1.2.2 percpu内存管理的建立


在建立percpu内存管理机制之前要整理出该架构下的处理器信息,包括处理器如何分组、每组对应的处理器位图、静态定义的percpu变量占用内存区域、每颗处理器percpu虚拟内存递进基本单位等信息。本文仅以双核CA9处理器作为分析目标。


对于处理器的分组信息,内核使用“struct pcpu_group_info”结构表示:
struct pcpu_group_info {
    /* 该组的处理器数目,对于双核CA9处理器,该值为2 */
int nr_units;
    /*组内处理器数目×处理器percpu虚拟内存递进基本单位*/
unsigned long base_offset;
   /*组内处理器对应数组,双核CA9架构下该数组长度为2*/
unsigned int *cpu_map;  
};


整体的percpu内存管理信息被收集在“struct pcpu_alloc_info”结构中:
struct pcpu_alloc_info {
    //静态定义的percpu变量占用内存区域长度
size_t static_size;
   /*预留区域,在percpu内存分配指定为预留区域分配时,将使用该区域*/
size_t reserved_size;
size_t dyn_size;
    /*每颗处理器的percpu虚拟内存递进基本单位*/
size_t unit_size;
…
    /*该架构下的处理器分组数目,CA9双核架构下该值为1*/
int nr_groups;
/*该架构下的处理器分组信息,CA9双核架构下该数组长度为1*/
struct pcpu_group_infogroups[];
};


接下来构建静态定义的percpu变量创建percpu区域


/*该函数被“void __init setup_per_cpu_areas(void)”调用,对于CA9架构其参数“cpu_distance_fn”为“NULL”,参数“alloc_fn”为“pcpu_dfl_fc_alloc”*/
int __init pcpu_embed_first_chunk(size_t reserved_size, size_t dyn_size,
 size_t atom_size,
 pcpu_fc_cpu_distance_fn_t cpu_distance_fn,
 pcpu_fc_alloc_fn_t alloc_fn,
 pcpu_fc_free_fn_t free_fn)
{
void *base = (void *)ULONG_MAX;
void **areas = NULL;
struct pcpu_alloc_info *ai;
size_t size_sum, areas_size, max_distance;
int group, i, rc;
    /*收集整理该架构下的percpu信息,结果放在“struct pcpu_alloc_info”结构中*/
ai = pcpu_build_alloc_info(reserved_size, dyn_size, atom_size,
  cpu_distance_fn);
…
    //静态定义变量占用空间+reserved空间+动态分配空间
    size_sum = ai->static_size + ai->reserved_size + ai->dyn_size;
…
/*针对每个group操作 */
for (group = 0; group < ai->nr_groups; group++) {
struct pcpu_group_info *gi = &ai->groups[group];
unsigned int cpu = NR_CPUS;
void *ptr;


…


/* 为该group分配percpu内存区域。长度为处理器数目X每颗处理器的percpu递进单位。函数“pcpu_dfl_fc_alloc”是从bootmem里取得内存,得到的是物理内存 */
ptr = alloc_fn(cpu, gi->nr_units * ai->unit_size, atom_size);
…
areas[group] = ptr;


base = min(ptr, base);
       //为每颗处理器建立其percpu区域
for (i = 0; i < gi->nr_units; i++, ptr += ai->unit_size) {
if (gi->cpu_map[i] == NR_CPUS) {
/*检查组内处理器器,对于没有用到处理器释放其percpu区域 */
free_fn(ptr, ai->unit_size);
continue;
}
/*将定态定义的percpu变量拷贝到每颗处理器percpu区域*/
memcpy(ptr, __per_cpu_load, ai->static_size);
           /*为每颗处理器释放掉多余的空间,多余的空间是指ai->unit_size 减去静态定义变量占用空间+reserved空间+动态分配空间*/
free_fn(ptr + size_sum, ai->unit_size - size_sum);
}
}


/* 处理器架构相关的计算,对于CA9双核架构,ai->nr_groups 为1,且“ai->groups[group].base_offset”和“max_distance” 这里的计算结果都为“0”*/
max_distance = 0;
for (group = 0; group < ai->nr_groups; group++) {
ai->groups[group].base_offset = areas[group] - base;
max_distance = max_t(size_t, max_distance,
    ai->groups[group].base_offset);
}
   
max_distance += ai->unit_size;
…
    //建立可动态分配的percpu内存区域
rc = pcpu_setup_first_chunk(ai, base);
…
}




//建立可动态分配的percpu内存区域
int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
 void *base_addr)
{
static char cpus_buf[4096] __initdata;
static int smap[PERCPU_DYNAMIC_EARLY_SLOTS] __initdata;
static int dmap[PERCPU_DYNAMIC_EARLY_SLOTS] __initdata;
….

for (cpu = 0; cpu < nr_cpu_ids; cpu++)
unit_map[cpu] = UINT_MAX;
pcpu_first_unit_cpu = NR_CPUS;


   /*针对每一group的每一颗处理器,对于对于双核CA9,“ai->nr_groups”值为“0”“gi->nr_units”值为2*/
for (group = 0, unit = 0; group < ai->nr_groups; group++, unit += i) {
const struct pcpu_group_info *gi = &ai->groups[group];
        
        //该组处理器的percpu偏移量,对于双核CA9,该值为“0”
group_offsets[group] = gi->base_offset;
       //该组处理器占用的虚拟地址空间
group_sizes[group] = gi->nr_units * ai->unit_size;


       //针对组内的每颗处理器
for (i = 0; i < gi->nr_units; i++) {
cpu = gi->cpu_map[i];
if (cpu == NR_CPUS)
continue;


…
            unit_map[cpu] = unit + i;
           //计算每颗处理器的percpu虚拟空间偏移量
unit_off[cpu] = gi->base_offset + i * ai->unit_size;
…
}
}
pcpu_nr_units = unit;


…
    //记录下全局参数,留在“pcpu_alloc”时使用
pcpu_nr_groups = ai->nr_groups;
    …
pcpu_unit_offsets = unit_off;


/* determine basic parameters */
pcpu_unit_pages = ai->unit_size >> PAGE_SHIFT;
pcpu_unit_size = pcpu_unit_pages << PAGE_SHIFT;
pcpu_atom_size = ai->atom_size;
pcpu_chunk_struct_size = sizeof(struct pcpu_chunk) +
BITS_TO_LONGS(pcpu_unit_pages) * sizeof(unsigned long);


/*
构建“pcpu_slot”数组,不同size的chunck挂在不同“pcpu_slot”项目中
*/
pcpu_nr_slots = __pcpu_size_to_slot(pcpu_unit_size) + 2;
pcpu_slot = alloc_bootmem(pcpu_nr_slots * sizeof(pcpu_slot[0]));
   //初始化“pcpu_slot”数组链头
for (i = 0; i < pcpu_nr_slots; i++)
INIT_LIST_HEAD(&pcpu_slot[i]);


/*
构建静态chunck即“pcpu_reserved_chunk”,该区域的物理内存以及虚拟地址都在“int __init pcpu_embed_first_chunk(…)”里分配了。
*/
schunk = alloc_bootmem(pcpu_chunk_struct_size);
…
schunk->immutable = true;
    //物理内存已经分配这里标志之
bitmap_fill(schunk->populated, pcpu_unit_pages);
…
if (ai->reserved_size) {
       //reserved的空间,在指定reserved分配时使用
schunk->free_size = ai->reserved_size;
pcpu_reserved_chunk = schunk;
        //定义的静态变量的空间也算进来
pcpu_reserved_chunk_limit = ai->static_size + ai->reserved_size;
} else {
schunk->free_size = dyn_size;
dyn_size = 0; /* dynamic area covered */
}
schunk->contig_hint = schunk->free_size;


schunk->map[schunk->map_used++] = -ai->static_size;




if (schunk->free_size)
schunk->map[schunk->map_used++] = schunk->free_size;


/* 动态分配空间,这里构建第一个chunck,该chunk是第一次步进时静态变量空间和reserved空间使用后剩下的*/
if (dyn_size) {
dchunk = alloc_bootmem(pcpu_chunk_struct_size);
INIT_LIST_HEAD(&dchunk->list);
…
        //记录下来分配的物理页
bitmap_fill(dchunk->populated, pcpu_unit_pages);


dchunk->contig_hint = dchunk->free_size = dyn_size;
        /*map指针更新将静态变量空间和reserved空间甩在后面*/
dchunk->map[dchunk->map_used++] = -pcpu_reserved_chunk_limit;
dchunk->map[dchunk->map_used++] = dchunk->free_size;
}


/* 把第一个chunk链接进对应的slot链表,reserverd 的空间有自己单独的chunk:pcpu_reserved_chunk */
pcpu_first_chunk = dchunk ?: schunk;
pcpu_chunk_relocate(pcpu_first_chunk, -1);


/* we're done */
pcpu_base_addr = base_addr;
return 0;
}


1.2.3 percpu动态分配内存空间


关于percpu动态分配内存空间有以下基本概念


每颗处理器的自己percpu动态分配内存空间都有不同的虚拟地址空间。否则同一线程在不同处理器上运行时修改页表页目录项的开销太大。
Chunk记录每颗处理器一次步进得到虚拟地址空间,对于一颗处理器来说一次步进长度是“ai->unit_size”,percpu内存的虚拟地址以chunk为基础
Chunk中每一次配出的内存用其map[]指向
Chunk根据其free的内存长度挂到Slot数组的对应链表上
每次步进仅得到虚拟地址空间,在完成一次分配时才得到对应的物理内存
本书仅考虑percpu-vm的情况


/*动态分配函数 */
static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved)
{
static int warn_limit = 10;
struct pcpu_chunk *chunk;
const char *err;
int slot, off, new_alloc;
unsigned long flags;


mutex_lock(&pcpu_alloc_mutex);
spin_lock_irqsave(&pcpu_lock, flags);


/* 指定reserved分配,从pcpu_reserved_chunk进行 ,较简单不讨论*/
if (reserved && pcpu_reserved_chunk) {
…
}


restart:
/* 根据需要分配内存块的大小索引slot数组找到对应链表 */
for (slot = pcpu_size_to_slot(size); slot < pcpu_nr_slots; slot++) {
list_for_each_entry(chunk, &pcpu_slot[slot], list) {
           //在该链表中进一步寻找符合尺寸要求的chunk
if (size > chunk->contig_hint)
continue;
           
/*chunck用数组“int *map”记录每次分配的内存块,若该数组用完(该chunk仍然还有自由空间),则需要增长该“int *map”数组。*/
new_alloc = pcpu_need_to_extend(chunk);
if (new_alloc) {
spin_unlock_irqrestore(&pcpu_lock, flags);
              //扩展“int *map”数组。
if (pcpu_extend_area_map(chunk,
new_alloc) < 0) {
…
}
spin_lock_irqsave(&pcpu_lock, flags);
goto restart;
}
            /*在该chunk里分配虚拟内存空间:分割最后一段自由空间,然后重新将该chunk挂到slot数组对应链表中*/
off = pcpu_alloc_area(chunk, size, align);
           //off大于0表示分配成功
if (off >= 0)
goto area_found;
}
}


spin_unlock_irqrestore(&pcpu_lock, flags);
    /*创建一个新的chunk,这里进行的是虚拟地址空间的分配 */
chunk = pcpu_create_chunk();
…
spin_lock_irqsave(&pcpu_lock, flags);
    //把一个全新的chunk挂到slot数组对应链表中
pcpu_chunk_relocate(chunk, -1);
goto restart;


area_found:
spin_unlock_irqrestore(&pcpu_lock, flags);


/* 一次percpu内存分配成功,这里要检查该段区域对应物理页是否已经分配,否者将为该区域分配对应的物理页并作填充L1 L2页表项*/
if (pcpu_populate_chunk(chunk, off, size)) {
…
}


mutex_unlock(&pcpu_alloc_mutex);


/* return address relative to base address */
return __addr_to_pcpu_ptr(chunk->base_addr + off);


…
}

卷一 内核源代码分析 第一章 linux内核对cortex A9多核处理器的支持 1.1 CPU的基本管理 图书试读版-请勿转载 1 多核处理器 计算性能是处理器演进的第一动力,然而,尽管各种架构的高性能处理器层出不穷,真正大规模普及开来的似乎只有Intel和ARM体系。我们观察其中的现象不难发现如下规律: 如果处理器性能得到大幅改善,但是无法得到现有主流操作系统的支持,就无法大规模应用。进一步来讲,即使某种处理器得到主流操作系统的支持,但是由于其指令集的不兼容性,导致大量的应用无法运行,这种处理器也是难以普及的 阅读详情

相关推荐

Linux中提取内核vmlinux并转化为带有symbol name的可分析elf

最近的task需要逆向分析某些Linux系统的内核,第一步从系统中提取出内核镜像文件就给我卡了半天,搜了国内基本没有能用的,这里总结一下: 安装extract-vmlinux 首先,需要获得extract-vmlinux脚本,这样就可以解压缩并提取Linux内核映像: $ wget -O extract-vmlinux https://raw.githubusercontent.com/torvalds/linux/master/scripts/extract-vmlinux 这个脚本不太可能会更改,但是

qq_40421991的博客 5703

Linux 内存管理

详解Linux内核纷繁复杂的内存管理,未完待续

5371

第十五天:Cortex-A9配置Linux应用开发环境

一、宿主机开发环境: 1、主机系统及内核版本 :Centos 6.5  2.6.32-431.el6.i686 2、 gcc版本:gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-4)3、交叉编译器版本:arm-linux-gcc (ctng-1.8.1-FA) 4.5.1 二、开发板介绍        1、开发板型号:Tiny4412SDK-1312-S7

snailr 4549

linux percpu机制解析

一、概述 每cpu变量是最简单也是最重要的同步技术。每cpu变量主要是数据结构数组,系统的每个cpu对应数组的一个元素。一个cpu不应该访问与其它cpu对应的数组元素,另外,它可以随意读或修改它自己的元素而不用担心出现竞争条件,因为它是唯一有资格这么做的cpu。这也意味着每cpu变量基本上只能在特殊情况下使用,也就是当它确定在系统的cpu上的数据在逻辑上是独立的时候。 每个处理器访问自

华的专栏 1万+

Linux内存管理(19):percpu 分配器——动态分配

上一篇博文我们剖析了 percpu 分配器的整个框架,包括percpu 中每个 cpu unit 的管理、内存区间的分配、静态percpu 变量的访问、reserved chunk和first chunk 的创建等流程。

私房菜 1733

Ubuntu22.04下ROS2与PX4仿真环境搭建全流程解析

本文详细解析了在Ubuntu22.04系统下搭建ROS2与PX4仿真环境的全流程,包括系统准备、ROS2 Humble安装、PX4飞控环境部署、ROS2与PX4通信桥梁搭建等关键步骤。针对常见问题提供了解决方案,并分享了性能优化和开发调试技巧,帮助开发者快速构建无人机仿真开发环境。

weixin_29248653的博客 608

Linux Per-cpu

目录 1.    Per-cpu重要数据结构    2 2.    Per-cpu Setup    3 2.1 Per-cpu软件框架建立流程    3 2.2 pcpu_alloc_info创建    3 2.3 pcpu_first_chunk创建    5 2.4 设置__per_cpu_offset    9 3.    pcpu_allo

chenying126的博客 2587

CAN基础知识

CAN基础知识

shouchen1的博客 9152

Linux内核中常用的C语言技巧(一)

Linux内核是基于C语言编写的,熟练掌握C语言是深入学习Linux内核的基本要求。 # GNU C语言的扩展 GCC的C编译器除了支持ANSI C标准之外,还对C语言进行了很多的扩充。 **这些扩充对代码优化、目标代码布局以及安全检查等方面提供了很强的支持,因此支持GNU扩展的C语言称为GNU C语言**。 Linux内核采用GCC编译器,所以Linux内核的代码自然使用了很多GCC的新扩充特性。 本章介绍一些GCC C语言扩充的新特性,希望读者在学习Linux内核时特别留意。

叫好与叫座虽然不是对立面,但想在同一个作品中达到双重效果很难。 635

主流仿真软件Sigrity/HFSS/Siwave/Hyperlynx/ADS差异

在电子设计自动化(EDA)领域,各种仿真软件如Cadence Sigrity、Mentor Graphics的HyperLynx、Keysight的ADS以及ANSYS的SIwave等,都广泛应用于不同的应用场景中。综上所述,这些EDA仿真软件在各自的应用场景中发挥着重要作用,帮助工程师在设计阶段就能够评估和优化电子产品的性能,提高设计的可靠性和效率。:Sigrity由全球最大的EDA公司Cadence推出,Cadence还推出了Orcad和Allegro等著名软件。:由安捷伦(现是德科技)公司开发。

Geektec的专栏 9408

在Matlab中开发AutoSAR模型的流程(自下而上)

前面介绍了AutoSAR软件组件开发的两种方式,其中自下向上的方式,是从模型出发,流程如下。

小胡同学的工程师学习之路 5367

Linux内核中CPU管理,读《ARM Linux 内核源代码剖析》.......第10章 在CPU位图中注册当前运行CPU &nbsp...

本章主要是这两步:boot_cpu_init();在CPU位图中注册当前运行CPUpage_address_init();初始化HIGHMEM管理boot_cpu_initkernel/cpu.cstatic DECLARE_BITMAP(cpu_possible_bits,CONFIG_NR_CPUS) __read_mostly;const struct cpumask *const cpu_...

weixin_39956451的博客 183

基于Pycatia的智能孔特征识别技术:无参模型圆心自动化提取方案

基于Pycatia的智能孔特征识别技术:无参模型圆心自动化提取方案

KE17RS的博客 2883

Linux内核模块的注册与管理

linux内核版本:linux-4.2.4 记录Linux内核组织形式,内核加载/卸载过程等。

CX19970的博客 544

【学习系列】SAP RAP 1:关于RAP的一些基本介绍

摘要:SAP RAP和CAP是两种不同的开发模型。RAP(RESTful ABAP)用于在S/4HANA等ABAP平台上构建现代业务应用,基于CDS+OData,适合ABAP开发者扩展核心业务。CAP(云应用编程模型)是BTP上的云原生框架,支持Node.js/Java,适合构建跨系统集成应用。RAP优势在于与S/4HANA深度集成,CAP则更灵活,支持多云环境。选择依据主要取决于开发平台(S/4HANA或BTP)、技术栈(ABAP或Node.js/Java)及应用场景(核心扩展或云原生开发)。

DeveloperMrMeng的博客 1169

基于 Mask R-CNN 的实例分割:从零开始构建一个强大的分割模型

在本文中,我们将介绍如何使用 Mask R-CNN(Region-based Convolutional Networks with Masks)实现实例分割。实例分割是计算机视觉中的一种任务,旨在识别并分割图像中的各个对象。我们将讨论如何准备数据集、配置 Mask R-CNN 模型并在自定义数据集上进行训练。

斌擎科技 1244

第一个linux驱动模块分析:

我们先看一下代码: #include <linux/module.h> #include <linux/init.h> // 模块安装函数 static int __init chrdev_init(void) { printk(KERN_INFO “chrdev_init helloworld init\n”); return 0; } // 模块卸载函数 stat...

qq_33472414的博客 563

3步!Spring Boot+OCR引擎秒提身份证/营业执照?手把手教你从零实现!

OCR技术革新文档识别效率 本文演示了如何利用Spring Boot+Tesseract OCR+OpenCV构建自动化文档识别系统。相比传统人工录入,OCR技术可实现: 5秒极速提取身份证/营业执照关键信息 精准识别中文文本(准确率90%+) 正则过滤自动匹配结构化数据 技术要点: OpenCV图像预处理(灰度化+二值化) Tesseract多语言OCR识别 正则表达式精准提取证件号码 效率提升达100倍,错误率降低90%,适用于金融、政务等需要批量处理证件的场景。

java专栏 906

Linux源码学习三、vmlinux.lds.S、MRC/MCR协处理器寄存器指令和head.S文件分析

①vmlinux.lds.S文件在目录arch/arm/kernel/vmlinux.lds.S下 分析VMLINUX_SYMBOL(__proc_info_begin) = .; vmlinux.lds.S文件的内容为: #include <asm-generic/vmlinux.lds.h> #include <asm/thread_info.h> #incl...

lbh19630726的博客 794

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

vmlinux_32.lds.S

本文简单的介绍了理解内核链接脚本需要的链接器知识和链接脚本语法,从vmlinux_32.lds.S文件分析了内核image的构成,并着重讲解了使用自定义section配合链接脚本动态创建表的方法。这里或许有一些你经常看到但不了解原理的东西,例如“内核导出符号表是如何创建的?”、“__initcall修饰的函数在什么时候被内核调用?”、“__initdata是否会被释放?”等。 1. 什么是链

u013489359的专栏 660

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

【linux kernel】基于ARM64分析linux内核的链接脚本vmlinux.lds.S

本文基于ARM64分析linux内核的链接脚本。

iriczhao的博客 2277

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

vmlinux.lds.S文件初探

/* ld script to make ARM Linux kernel * taken from the i386 version by Russell King * Written by Martin Mares &lt;mj@atrey.karlin.mff.cuni.cz&gt; */ #include &lt;asm-generic/vmlinux.lds.h&gt; #inc...

qq_36412526的博客 1009

linux内核导出函数

linux内核导出函数

1232

linux symbol文件,Linux内核模块文件组成介绍

Linux驱动开发主要的工作就是编写模块,一个典型的Linux内核模块文件.ko 主要由以下几个部分组成。模块加载函数(必须)当通过insmod或modprobe命令加载内核模块时,模块的加载函数会自动被内核执行,完成本模块的相关初始化工作。Linux内核模块加载函数一般用static 关键字声明为内部链接,并以__init 标识。之所以标识为__init ,用途是如果编译内核时模块是以静态方式包...

weixin_39907939的博客 566

[kernel 启动流程] 前篇——vmlinux.lds分析

以下例子都以project X项目tiny210(s5pv210平台,armv7架构)为例 一、基础部分 1、段说明 text段 代码段,通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定。 data段 数据段,通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。 bss段 通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域。BSS段属于静态内存分配。 init段 linux定义的一种初始化过程中才会用到的段,一旦初始化完成,那么这些

u012787604的博客 1421
上一篇: 卷一 内核源代码分析 第一章 linux内核对cortex A9多核处理器的支持 1.1 CPU的基本管理 图书试读版-请勿转载
下一篇: 卷二 Dalvik与Android源码分析 第二章 进程与线程 2.1 Dalvik虚拟机的进程创建机制 图书版试读--请勿转发
crosskernel
博客等级 码龄18年 14粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值