日期内核版本架构作者GitHubCSDN
2016-09-01Linux-4.7X86 & armgatiemeLinuxDeviceDriversLinux内存管理


在内存管理的上下文中, 初始化(initialization)可以有多种含义. 在许多CPU上, 必须显式设置适用于Linux内核的内存模型. 例如在x86_32上需要切换到保护模式, 然后奇偶内核才能检测到可用内存和寄存器.

1 前景回顾


1.1 Linux内存管理的层次结构


Linux把物理内存划分为三个层次来管理

层次描述
存储节点(Node)CPU被划分为多个节点(node), 内存则被分簇, 每个CPU对应一个本地物理内存, 即一个CPU-node对应一个内存簇bank,即每个内存簇被认为是一个节点
管理区(Zone)每个物理内存节点node被划分为多个内存管理区域, 用于表示不同范围的内存, 内核可以使用不同的映射方式映射物理内存
页面(Page)内存被细分为多个页面帧, 页面是最基本的页面分配的单位 |


为了支持NUMA模型,也即CPU对不同内存单元的访问时间可能不同,此时系统的物理内存被划分为几个节点(node), 一个node对应一个内存簇bank,即每个内存簇被认为是一个节点

  • 首先, 内存被划分为结点. 每个节点关联到系统中的一个处理器, 内核中表示为pg_data_t的实例. 系统中每个节点被链接到一个以NULL结尾的pgdat_list链表中<而其中的每个节点利用pg_data_tnode_next字段链接到下一节.而对于PC这种UMA结构的机器来说, 只使用了一个成为contig_page_data的静态pg_data_t结构.

  • 接着各个节点又被划分为内存管理区域, 一个管理区域通过struct zone_struct描述, 其被定义为zone_t, 用以表示内存的某个范围, 低端范围的16MB被描述为ZONE_DMA, 某些工业标准体系结构中的(ISA)设备需要用到它, 然后是可直接映射到内核的普通内存域ZONE_NORMAL,最后是超出了内核段的物理地址域ZONE_HIGHMEM, 被称为高端内存. 是系统中预留的可用内存空间, 不能被内核直接映射.

  • 最后页帧(page frame)代表了系统内存的最小单位, 堆内存中的每个页都会创建一个struct page的一个实例. 传统上,把内存视为连续的字节,即内存为字节数组,内存单元的编号(地址)可作为字节数组的索引. 分页管理时,将若干字节视为一页,比如4K byte. 此时,内存变成了连续的页,即内存为页数组,每一页物理内存叫页帧,以页为单位对内存进行编号,该编号可作为页数组的索引,又称为页帧号.

1.2 今日内容(启动过程中的内存初始化)


在初始化过程中, 还必须建立内存管理的数据结构, 以及很多事务. 因为内核在内存管理完全初始化之前就需要使用内存. 在系统启动过程期间, 使用了额外的简化悉尼股市的内存管理模块, 然后在初始化完成后, 将旧的模块丢弃掉.

因此我们可以把linux内核的内存管理分三个阶段。

阶段起点终点描述
第一阶段系统启动bootmem或者memblock初始化完成此阶段只能使用memblock_reserve函数分配内存, 早期内核中使用init_bootmem_done = 1标识此阶段结束
第二阶段bootmem或者memblock初始化完buddy完成前引导内存分配器bootmem或者memblock接受内存的管理工作, 早期内核中使用mem_init_done = 1标记此阶段的结束
第三阶段buddy初始化完成系统停止运行可以用cache和buddy分配内存

1.3 start_kernel系统启动阶段的内存初始化过程


首先我们来看看start_kernel是如何初始化系统的, start_kerne定义在init/main.c?v=4.7, line 479

其代码很复杂, 我们只截取出其中与内存管理初始化相关的部分, 如下所示

asmlinkage __visible void __init start_kernel(void)
{setup_arch(&command_line);mm_init_cpumask(&init_mm);setup_per_cpu_areas();build_all_zonelists(NULL, NULL);page_alloc_init();/** These use large bootmem allocations and must precede* mem_init();* kmem_cache_init();*/mm_init();kmem_cache_init_late();kmemleak_init();setup_per_cpu_pageset();rest_init();
}
函数功能
setup_arch是一个特定于体系结构的设置函数, 其中一项任务是负责初始化自举分配器
mm_init_cpumask初始化CPU屏蔽字
setup_per_cpu_areas函数(查看定义)给每个CPU分配内存,并拷贝.data.percpu段的数据. 为系统中的每个CPU的per_cpu变量申请空间.
在SMP系统中, setup_per_cpu_areas初始化源代码中(使用per_cpu宏)定义的静态per-cpu变量, 这种变量对系统中每个CPU都有一个独立的副本.
此类变量保存在内核二进制影像的一个独立的段中, setup_per_cpu_areas的目的就是为系统中各个CPU分别创建一份这些数据的副本
在非SMP系统中这是一个空操作
build_all_zonelists建立并初始化结点和内存域的数据结构
mm_init建立了内核的内存分配器,
其中通过mem_init停用bootmem分配器并迁移到实际的内存管理器(比如伙伴系统)
然后调用kmem_cache_init函数初始化内核内部用于小块内存区的分配器
kmem_cache_init_late在kmem_cache_init之后, 完善分配器的缓存机制, 当前3个可用的内核内存分配器slab, slob, slub都会定义此函数
kmemleak_initKmemleak工作于内核态,Kmemleak 提供了一种可选的内核泄漏检测,其方法类似于跟踪内存收集器。当独立的对象没有被释放时,其报告记录在 /sys/kernel/debug/kmemleak中, Kmemcheck能够帮助定位大多数内存错误的上下文
setup_per_cpu_pageset初始化CPU高速缓存行, 为pagesets的第一个数组元素分配内存, 换句话说, 其实就是第一个系统处理器分配
由于在分页情况下,每次存储器访问都要存取多级页表,这就大大降低了访问速度。所以,为了提高速度,在CPU中设置一个最近存取页面的高速缓存硬件机制,当进行存储器访问时,先检查要访问的页面是否在高速缓存中.

1.4 setup_arch函数初始化内存流程


前面我们的内核从start_kernel开始, 进入setup_arch(), 并完成了早期内存分配器的初始化和设置工作.

void __init setup_arch(char **cmdline_p)
{/*  初始化memblock  */arm64_memblock_init( );/*  分页机制初始化  */paging_init();bootmem_init();
}
流程描述
arm64_memblock_init初始化memblock内存分配器
paging_init初始化分页机制
bootmem_init初始化内存管理

该函数主要执行了如下操作

  1. 使用arm64_memblock_init来完成memblock机制的初始化工作, 至此memblock分配器接受系统中系统中内存的分配工作

  2. 调用paging_init来完成系统分页机制的初始化工作, 建立页表, 从而内核可以完成虚拟内存的映射和转换工作

  3. 最后调用bootmem_init来完成实现buddy内存管理所需要的工作

1.5 (第一阶段)启动过程中的内存分配器


在初始化过程中, 还必须建立内存管理的数据结构, 以及很多事务. 因为内核在内存管理完全初始化之前就需要使用内存. 在系统启动过程期间, 使用了额外的简化悉尼股市的内存管理模块, 然后在初始化完成后, 将旧的模块丢弃掉.

这个阶段的内存分配其实很简单, 因此我们往往称之为内存分配器(而不是内存管理器), 早期的内核中内存分配器使用的bootmem引导分配器, 它基于一个内存位图bitmap, 使用最优适配算法来查找内存, 但是这个分配器有很大的缺陷, 最严重的就是内存碎片的问题, 因此在后来的内核中将其舍弃《而使用了新的memblock机制. memblock机制的初始化在arm64上是通过arm64_memblock_init函数来实现的

```cpp
start_kernel()|---->page_address_init()|     考虑支持高端内存|     业务:初始化page_address_pool链表;|          将page_address_maps数组元素按索引降序插入|          page_address_pool链表; |          初始化page_address_htable数组.| |---->setup_arch(&command_line);|     初始化特定体系结构的内容||---->arm64_memblock_init( );|     初始化引导阶段的内存分配器memblock||---->paging_init();|     分页机制初始化||---->bootmem_init();   [当前位置]|     始化内存数据结构包括内存节点, 内存域和页帧page||---->arm64_numa_init();|     支持numa架构||---->zone_sizes_init(min, max);来初始化节点和管理区的一些数据项||---->free_area_init_node|   初始化内存节点||---->free_area_init_core|   初始化zone||---->memmap_init|   初始化page页面||---->memblock_dump_all();|   初始化完成, 显示memblock的保留的所有内存信息||---->build_all_zonelist()|     为系统中的zone建立后备zone的列表.|     所有zone的后备列表都在|     pglist_data->node_zonelists[0]中;||     期间也对per-CPU变量boot_pageset做了初始化. |

1.6 今日内容(第二阶段(一)–初始化内存管理数据结构)


我们之前讲了在memblock完成之后, 内存初始化开始进入第二阶段, 第二阶段是一个漫长的过程, 它执行了一系列复杂的操作, 从体系结构相关信息的初始化慢慢向上层展开, 其主要执行了如下操作

特定于体系结构的设置

在完成了基础的内存结点和内存域的初始化工作以后, 我们必须克服一些硬件的特殊设置

  • 在初始化内存的结点和内存区域之前, 内核先通过pagging_init初始化了内核的分页机制, 这样我们的虚拟运行空间就初步建立, 并可以完成物理地址到虚拟地址空间的映射工作.

在arm64架构下, 内核在start_kernel()->setup_arch()中通过arm64_memblock_init( )完成了memblock的初始化之后, 接着通过setup_arch()->paging_init()开始初始化分页机制

paging_init负责建立只能用于内核的页表, 用户空间是无法访问的. 这对管理普通应用程序和内核访问内存的方式,有深远的影响

  • 在分页机制完成后, 内核通过setup_arch()->bootmem_init开始进行内存基本数据结构(内存结点pg_data_t, 内存域zone和页帧)的初始化工作, 就是在这个函数中, 内核开始从体系结构相关的部分逐渐展开到体系结构无关的部分, 在zone_sizes_init->free_area_init_node中开始, 内核开始进行内存基本数据结构的初始化, 也不再依赖于特定体系结构无关的层次
bootmem_init()
始化内存数据结构包括内存节点, 内存域和页帧page
|
|---->arm64_numa_init();
|     支持numa架构
|
|---->zone_sizes_init(min, max);来初始化节点和管理区的一些数据项||---->free_area_init_node|   初始化内存节点||---->free_area_init_core|   初始化zone||---->memmap_init|   初始化page页面
|
|---->memblock_dump_all();
|   初始化完成, 显示memblock的保留的所有内存信息

建立内存管理的数据结构

对相关数据结构的初始化是从全局启动函数start_kernel中开始的, 该函数在加载内核并激活各个子系统之后执行. 由于内存管理是内核一个非常重要的部分, 因此在特定体系结构的设置步骤中检测并确定系统中内存的分配情况后, 会立即执行内存管理的初始化.

移交早期的分配器到内存管理器

最后我们的内存管理器已经初始化并设置完成, 可以投入运行了, 因此内核将内存管理的工作从早期的内存分配器(bootmem或者memblock)移交到我们的buddy伙伴系统.

2 初始化前的准备工作


2.1 回到setup_arch函数(当前已经完成的工作)


现在我们回到start_kernel()->setup_arch()函数

void __init setup_arch(char **cmdline_p)
{/*  初始化memblock  */arm64_memblock_init( );/*  分页机制初始化  */paging_init();bootmem_init();
}

到目前位置我们已经完成了如下工作

  • memblock已经通过arm64_memblock_init完成了初始化, 至此系统中的内存可以通过memblock分配了

  • paging_init完成了分页机制的初始化, 至此内核已经布局了一套完整的虚拟内存空间

至此我们所有的内存都可以通过memblock机制来分配和释放, 尽管它实现的笨拙而简易, 但是已经足够我们初始化阶段使用了, 反正内核页不可能指着它过一辈子, 而我们也通过pagging_init创建了页表, 为内核提供了一套可供内核和进程运行的虚拟运行空间, 我们可以安全的进行内存的分配了

因此该是时候初始化我们强大的buddy系统了.

内核接着setup_arch()->bootmem_init()函数开始执行

体系结构相关的代码需要在启动期间建立如下信息

  • 系统中各个内存域的页帧边界,保存在max_zone_pfn数组

早期的内核还需记录各结点页帧的分配情况,保存在全局变量early_node_map中

zone_sizes_init函数

内核提供了一个通用的框架, 用于将上述信息转换为伙伴系统预期的节点和内存域数据结构, 但是在此之前各个体系结构必须自行建立相关结构.

2.2 bootmem_init函数初始化内存结点和管理域


arm64架构下, 在setup_arch中通过paging_init函数初始化内核分页机制之后, 内核通过bootmem_init()开始完成内存结点和内存区域的初始化工作, 该函数定义在arch/arm64/mm/init.c, line 306

void __init bootmem_init(void)
{unsigned long min, max;min = PFN_UP(memblock_start_of_DRAM());max = PFN_DOWN(memblock_end_of_DRAM());early_memtest(min << PAGE_SHIFT, max << PAGE_SHIFT);max_pfn = max_low_pfn = max;arm64_numa_init();/** Sparsemem tries to allocate bootmem in memory_present(), so must be* done after the fixed reservations.*/arm64_memory_present();sparse_init();zone_sizes_init(min, max);high_memory = __va((max << PAGE_SHIFT) - 1) + 1;memblock_dump_all();
}

2.3 zone_sizes_init函数


在初始化内存结点和内存域之前, 内核首先通过setup_arch()–>bootmem_init()–>zone_sizes_init()来初始化节点和管理区的一些数据项, 其中关键的是初始化了系统中各个内存域的页帧边界,保存在max_zone_pfn数组.

zone_sizes_init函数定义在arch/arm64/mm/init.c?v=4.7, line 92, 由于arm64支持NUMA和UMA两种存储器架构, 因此该函数依照NUMA和UMA, 有两种不同的实现.

#ifdef CONFIG_NUMAstatic void __init zone_sizes_init(unsigned long min, unsigned long max)
{unsigned long max_zone_pfns[MAX_NR_ZONES]  = {0};if (IS_ENABLED(CONFIG_ZONE_DMA))max_zone_pfns[ZONE_DMA] = PFN_DOWN(max_zone_dma_phys());max_zone_pfns[ZONE_NORMAL] = max;free_area_init_nodes(max_zone_pfns);
}#elsestatic void __init zone_sizes_init(unsigned long min, unsigned long max)
{struct memblock_region *reg;unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];unsigned long max_dma = min;memset(zone_size, 0, sizeof(zone_size));/* 4GB maximum for 32-bit only capable devices */
#ifdef CONFIG_ZONE_DMAmax_dma = PFN_DOWN(arm64_dma_phys_limit);zone_size[ZONE_DMA] = max_dma - min;
#endifzone_size[ZONE_NORMAL] = max - max_dma;memcpy(zhole_size, zone_size, sizeof(zhole_size));for_each_memblock(memory, reg) {unsigned long start = memblock_region_memory_base_pfn(reg);unsigned long end = memblock_region_memory_end_pfn(reg);if (start >= max)continue;#ifdef CONFIG_ZONE_DMAif (start < max_dma) {unsigned long dma_end = min(end, max_dma);zhole_size[ZONE_DMA] -= dma_end - start;}
#endifif (end > max_dma) {unsigned long normal_end = min(end, max);unsigned long normal_start = max(start, max_dma);zhole_size[ZONE_NORMAL] -= normal_end - normal_start;}}free_area_init_node(0, zone_size, min, zhole_size);
}#endif /* CONFIG_NUMA */

在获取了三个管理区的页面数后, NUMA架构下通过free_area_init_nodes()来完成后续工作, 其中核心函数为free_area_init_node(),用来针对特定的节点进行初始化, 由于UMA架构下只有一个内存结点, 因此直接通过free_area_init_node来完成内存结点的初始化

截至到目前为止, 体系结构相关的部分已经结束了, 各个体系结构已经自行建立了自己所需的一些底层数据结构, 这些结构建立好以后, 内核将繁重的内存数据结构创建和初始化的工作交给free_area_init_node(s)函数来完成,

3 free_area_init_nodes初始化NUMA管理数据结构


注意

此部分内容参照

Linux内存管理伙伴算法

linux 内存管理 - paging_init 函数

free_area_init_nodes初始化了NUMA系统中所有结点的pg_data_t和zone、page的数据, 并打印了管理区信息, 该函数定义在mm/page_alloc.c?v=4.7, line 6460

3.1 代码注释


//  初始化各个节点的所有pg_data_t和zone、page的数据
void __init free_area_init_nodes(unsigned long *max_zone_pfn)
{unsigned long start_pfn, end_pfn;int i, nid;/* Record where the zone boundaries are* 全局数组arch_zone_lowest_possible_pfn* 用来存储各个内存域可使用的最低内存页帧编号   */memset(arch_zone_lowest_possible_pfn, 0,sizeof(arch_zone_lowest_possible_pfn));/* 全局数组arch_zone_highest_possible_pfn* 用来存储各个内存域可使用的最高内存页帧编号   */memset(arch_zone_highest_possible_pfn, 0,sizeof(arch_zone_highest_possible_pfn));/* 辅助函数find_min_pfn_with_active_regions* 用于找到注册的最低内存域中可用的编号最小的页帧 */arch_zone_lowest_possible_pfn[0] = find_min_pfn_with_active_regions();/*  max_zone_pfn记录了各个内存域包含的最大页帧号  */arch_zone_highest_possible_pfn[0] = max_zone_pfn[0];/*  依次遍历,确定各个内存域的边界    */for (i = 1; i < MAX_NR_ZONES; i++) {/*  由于ZONE_MOVABLE是一个虚拟内存域*  不与真正的硬件内存域关联*  该内存域的边界总是设置为0 */if (i == ZONE_MOVABLE)continue;/*  第n个内存域的最小页帧*  即前一个(第n-1个)内存域的最大页帧  */arch_zone_lowest_possible_pfn[i] =arch_zone_highest_possible_pfn[i-1];/*  不出意外,当前内存域的最大页帧*  由max_zone_pfn给出  */arch_zone_highest_possible_pfn[i] =max(max_zone_pfn[i], arch_zone_lowest_possible_pfn[i]);}arch_zone_lowest_possible_pfn[ZONE_MOVABLE] = 0;arch_zone_highest_possible_pfn[ZONE_MOVABLE] = 0;/* Find the PFNs that ZONE_MOVABLE begins at in each node */memset(zone_movable_pfn, 0, sizeof(zone_movable_pfn));/*  用于计算进入ZONE_MOVABLE的内存数量  */find_zone_movable_pfns_for_nodes();/* Print out the zone ranges* 将各个内存域的最大、最小页帧号显示出来  */pr_info("Zone ranges:\n");for (i = 0; i < MAX_NR_ZONES; i++) {if (i == ZONE_MOVABLE)continue;pr_info("  %-8s ", zone_names[i]);if (arch_zone_lowest_possible_pfn[i] ==arch_zone_highest_possible_pfn[i])pr_cont("empty\n");elsepr_cont("[mem %#018Lx-%#018Lx]\n",(u64)arch_zone_lowest_possible_pfn[i]<< PAGE_SHIFT,((u64)arch_zone_highest_possible_pfn[i]<< PAGE_SHIFT) - 1);}/* Print out the PFNs ZONE_MOVABLE begins at in each node */pr_info("Movable zone start for each node\n");for (i = 0; i < MAX_NUMNODES; i++) {/*  对每个结点来说,zone_movable_pfn[node_id]*  表示ZONE_MOVABLE在movable_zone内存域中所取得内存的起始地址*  内核确保这些页将用于满足符合ZONE_MOVABLE职责的内存分配 */if (zone_movable_pfn[i]){/*  显示各个内存域的分配情况  */pr_info("  Node %d: %#018Lx\n", i,(u64)zone_movable_pfn[i] << PAGE_SHIFT);}}/* Print out the early node map */pr_info("Early memory node ranges\n");for_each_mem_pfn_range(i, MAX_NUMNODES, &start_pfn, &end_pfn, &nid)pr_info("  node %3d: [mem %#018Lx-%#018Lx]\n", nid,(u64)start_pfn << PAGE_SHIFT,((u64)end_pfn << PAGE_SHIFT) - 1);/* Initialise every node */mminit_verify_pageflags_layout();setup_nr_node_ids();/*  代码遍历所有的活动结点,*  并分别对各个结点调用free_area_init_node建立数据结构,*  该函数需要结点第一个可用的页帧作为一个参数,*  而find_min_pfn_for_node则从early_node_map数组提取该信息   */for_each_online_node(nid) {pg_data_t *pgdat = NODE_DATA(nid);free_area_init_node(nid, NULL,find_min_pfn_for_node(nid), NULL);/* Any memory on that node* 根据node_present_pages字段判断结点具有内存* 则在结点位图中设置N_HIGH_MEMORY标志* 该标志只表示结点上存在普通或高端内存* 因此check_for_regular_memory* 进一步检查低于ZONE_HIGHMEM的内存域中是否有内存* 并据此在结点位图中相应地设置N_NORMAL_MEMORY   */if (pgdat->node_present_pages)node_set_state(nid, N_MEMORY);check_for_memory(pgdat, nid);}
}

free_area_init_nodes函数中通过循环遍历各个节点,循环中调用了free_area_init_node函数初始化该节点对应的pg_data_t和zone、page的数据.

3.2 设置可使用的页帧编号


free_area_init_nodes首先必须分析并改写特定于体系结构的代码提供的信息。其中,需要对照在zone_max_pfn和zone_min_pfn中指定的内存域的边界,计算各个内存域可使用的最低和最高的页帧编号。使用了两个全局数组来存储这些信息:

参见mm/page_alloc.c?v=4.7, line 259)

static unsigned long __meminitdata arch_zone_lowest_possible_pfn[MAX_NR_ZONES];static unsigned long __meminitdata arch_zone_highest_possible_pfn[MAX_NR_ZONES];

通过max_zone_pfn传递给free_area_init_nodes的信息记录了各个内存域包含的最大页帧号。
free_area_init_nodes将该信息转换为一种更方便的表示形式,即以[low, high]形式描述各个内
存域的页帧区间,存储在前述的全局变量中(我省去了对这些变量填充字节0的初始化过程):

void __init free_area_init_nodes(unsigned long *max_zone_pfn)
{/*  ......  */arch_zone_lowest_possible_pfn[ZONE_MOVABLE] = 0;arch_zone_highest_possible_pfn[ZONE_MOVABLE] = 0;/* Find the PFNs that ZONE_MOVABLE begins at in each node */memset(zone_movable_pfn, 0, sizeof(zone_movable_pfn));/*  用于计算进入ZONE_MOVABLE的内存数量  */find_zone_movable_pfns_for_nodes();/*  依次遍历,确定各个内存域的边界    */for (i = 1; i < MAX_NR_ZONES; i++) {/*  由于ZONE_MOVABLE是一个虚拟内存域*  不与真正的硬件内存域关联*  该内存域的边界总是设置为0 */if (i == ZONE_MOVABLE)continue;/*  第n个内存域的最小页帧*  即前一个(第n-1个)内存域的最大页帧  */arch_zone_lowest_possible_pfn[i] =arch_zone_highest_possible_pfn[i-1];/*  不出意外,当前内存域的最大页帧*  由max_zone_pfn给出  */arch_zone_highest_possible_pfn[i] =max(max_zone_pfn[i], arch_zone_lowest_possible_pfn[i]);}/*  ......  */
}

辅助函数find_min_pfn_with_active_regions用于找到注册的最低内存域中可用的编号最小的页帧。该内存域不必一定是ZONE_DMA,例如,在计算机不需要DMA内存的情况下也可以是ZONE_NORMAL。最低内存域的最大页帧号可以从max_zone_pfn提供的信息直接获得。

3.3 构建其他内存域的页帧区间


接下来构建其他内存域的页帧区间,方法很直接:第n个内存域的最小页帧,即前一个(第n-1个)内存域的最大页帧。当前内存域的最大页帧由max_zone_pfn给出

void __init free_area_init_nodes(unsigned long *max_zone_pfn)
{/*  ......  */arch_zone_lowest_possible_pfn[ZONE_MOVABLE] = 0;arch_zone_highest_possible_pfn[ZONE_MOVABLE] = 0;/* Find the PFNs that ZONE_MOVABLE begins at in each node */memset(zone_movable_pfn, 0, sizeof(zone_movable_pfn));/*  用于计算进入ZONE_MOVABLE的内存数量  */find_zone_movable_pfns_for_nodes();/*  ......  */
}

由于ZONE_MOVABLE是一个虚拟内存域,不与真正的硬件内存域关联,该内存域的边界总是设置为0。回忆前文,可知只有在指定了内核命令行参数kernelcore或movablecore之一时,该内存域才会存在.
该内存域一般开始于各个结点的某个特定内存域的某一页帧号。相应的编号在find_zone_movable_pfns_for_nodes里计算。

现在可以向用户提供一些有关已确定的页帧区间的信息。举例来说,其中可能包括下列内容(输出取自AMD64系统,有4 GiB物理内存):

> dmesgZone PFN ranges:
DMA 0 0 -> 4096
DMA32 4096 -> 1048576
Normal 1048576 -> 1245184

3.4 建立结点数据结构


free_area_init_nodes剩余的部分遍历所有结点,分别建立其数据结构

void __init free_area_init_nodes(unsigned long *max_zone_pfn)
{/*  输出有关内存域的信息  *//*  ......  *//*  代码遍历所有的活动结点,*  并分别对各个结点调用free_area_init_node建立数据结构,*  该函数需要结点第一个可用的页帧作为一个参数,*  而find_min_pfn_for_node则从early_node_map数组提取该信息   */for_each_online_node(nid) {pg_data_t *pgdat = NODE_DATA(nid);free_area_init_node(nid, NULL,find_min_pfn_for_node(nid), NULL);/* Any memory on that node* 根据node_present_pages字段判断结点具有内存* 则在结点位图中设置N_HIGH_MEMORY标志* 该标志只表示结点上存在普通或高端内存* 因此check_for_regular_memory* 进一步检查低于ZONE_HIGHMEM的内存域中是否有内存* 并据此在结点位图中相应地设置N_NORMAL_MEMORY   */if (pgdat->node_present_pages)node_set_state(nid, N_MEMORY);check_for_memory(pgdat, nid);}/*  ......  */
}

代码遍历所有活动结点,并分别对各个结点调用free_area_init_node建立数据结构。该函数需要结点第一个可用的页帧作为一个参数,而find_min_pfn_for_node则从early_node_map数组提取该信息。

如果根据node_present_pages字段判断结点具有内存,则在结点位图中设置N_HIGH_MEMORY标志。我们知道该标志只表示结点上存在普通或高端内存,因此check_for_regular_memory进一步检查低于ZONE_HIGHMEM的内存域中是否有内存,并据此在结点位图中相应地设置N_NORMAL_MEMORY标志

4 free_area_init_node初始化UMA内存结点


free_area_init_nodes函数初始化所有结点的pg_data_t和zone、page的数据,并打印了管理区信息.

4.1 free_area_init_node函数注释


该函数定义在mm/page_alloc.c?v=4.7, line 6076

void __paginginit free_area_init_node(int nid, unsigned long *zones_size,unsigned long node_start_pfn, unsigned long *zholes_size)
{pg_data_t *pgdat = NODE_DATA(nid);unsigned long start_pfn = 0;unsigned long end_pfn = 0;/* pg_data_t should be reset to zero when it's allocated */WARN_ON(pgdat->nr_zones || pgdat->classzone_idx);reset_deferred_meminit(pgdat);pgdat->node_id = nid;pgdat->node_start_pfn = node_start_pfn;
#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAPget_pfn_range_for_nid(nid, &start_pfn, &end_pfn);pr_info("Initmem setup node %d [mem %#018Lx-%#018Lx]\n", nid,(u64)start_pfn << PAGE_SHIFT,end_pfn ? ((u64)end_pfn << PAGE_SHIFT) - 1 : 0);
#elsestart_pfn = node_start_pfn;
#endif/*  首先累计各个内存域的页数*  计算结点中页的总数*  对连续内存模型而言*  这可以通过zone_sizes_init完成*  但calculate_node_totalpages还考虑了内存空洞 */calculate_node_totalpages(pgdat, start_pfn, end_pfn,zones_size, zholes_size);/*  分配了该节点的页面描述符数组*  [pgdat->node_mem_map数组的内存分配  */alloc_node_mem_map(pgdat);
#ifdef CONFIG_FLAT_NODE_MEM_MAPprintk(KERN_DEBUG "free_area_init_node: node %d, pgdat %08lx, node_mem_map %08lx\n",nid, (unsigned long)pgdat,(unsigned long)pgdat->node_mem_map);
#endif/*  对该节点的每个区[DMA,NORMAL,HIGH]的的结构进行初始化  */free_area_init_core(pgdat);
}

4.2 流程分析


  • calculate_node_totalpages函数累计各个内存域的页数,计算结点中页的总数。对连续内存模型而言,这可以通过zone_sizes_init完成,但calculate_node_totalpages还考虑了内存空洞,该函数定义在mm/page_alloc.c, line 5789

    以下例子取自一个UMA系统, 具有512 MiB物理内存。

> dmesg
...
On node 0 totalpages: 131056
  • alloc_node_mem_map(pgdat)函数分配了该节点的页面描述符数组[pgdat->node_mem_map数组的内存分配.

  • 继续调用free_area_init_core函数,继续初始化该节点的pg_data_t结构,初始化zone以及page结构 ,##2.6 free_area_init_core函数是初始化zone的核心

4.3 alloc_node_mem_map函数


alloc_node_mem_map负责初始化一个简单但非常重要的数据结构。如上所述,系统中的各个物理内存页,都对应着一个struct page实例。该结构的初始化由alloc_node_mem_map执行

static void __init_refok alloc_node_mem_map(struct pglist_data *pgdat)
{unsigned long __maybe_unused start = 0;unsigned long __maybe_unused offset = 0;/* Skip empty nodes */if (!pgdat->node_spanned_pages)return;#ifdef CONFIG_FLAT_NODE_MEM_MAPstart = pgdat->node_start_pfn & ~(MAX_ORDER_NR_PAGES - 1);offset = pgdat->node_start_pfn - start;/* ia64 gets its own node_mem_map, before this, without bootmem */if (!pgdat->node_mem_map) {unsigned long size, end;struct page *map;/** The zone's endpoints aren't required to be MAX_ORDER* aligned but the node_mem_map endpoints must be in order* for the buddy allocator to function correctly.*/end = pgdat_end_pfn(pgdat);end = ALIGN(end, MAX_ORDER_NR_PAGES);size =  (end - start) * sizeof(struct page);map = alloc_remap(pgdat->node_id, size);if (!map)map = memblock_virt_alloc_node_nopanic(size,pgdat->node_id);pgdat->node_mem_map = map + offset;}
#ifndef CONFIG_NEED_MULTIPLE_NODES/** With no DISCONTIG, the global mem_map is just set as node 0's*/if (pgdat == NODE_DATA(0)) {mem_map = NODE_DATA(0)->node_mem_map;
#if defined(CONFIG_HAVE_MEMBLOCK_NODE_MAP) || defined(CONFIG_FLATMEM)if (page_to_pfn(mem_map) != pgdat->node_start_pfn)mem_map -= offset;
#endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */}
#endif
#endif /* CONFIG_FLAT_NODE_MEM_MAP */
}

没有页的空结点显然可以跳过。如果特定于体系结构的代码尚未建立内存映射(这是可能的,例如,在IA-64系统上),则必须分配与该结点关联的所有struct page实例所需的内存。各个体系结构可以为此提供一个特定的函数。但目前只有在IA-32系统上使用不连续内存配置时是这样。在所有其他的配置上,则使用普通的自举内存分配器进行分配。请注意,代码将内存映射对齐到伙伴系统的最大分配阶,因为要使所有的计算都工作正常,这是必需的。

指向该空间的指针不仅保存在pglist_data实例中,还保存在全局变量mem_map中,前提是当前考察的结点是系统的第0个结点(如果系统只有一个内存结点,则总是这样)。mem_map是一个全局数组,在讲解内存管理时,我们会经常遇到, 定义在mm/memory.c?v=4.7, line 85

struct page *mem_map;

然后在free_area_init_node函数的最后, 通过free_area_init_core来完成内存域zone的初始化

5 free_area_init_core初始化内存域zone


初始化内存域数据结构涉及的繁重工作由free_area_init_core执行,它会依次遍历结点的所有内存域, 该函数定义在mm/page_alloc.c?v=4.7, line 5932

5.1 free_area_init_core函数代码注释


/** Set up the zone data structures:*   - mark all pages reserved*   - mark all memory queues empty*   - clear the memory bitmaps** NOTE: pgdat should get zeroed by caller.*/
static void __paginginit free_area_init_core(struct pglist_data *pgdat)
{enum zone_type j;int nid = pgdat->node_id;int ret;/*  初始化pgdat->node_size_lock自旋锁  */pgdat_resize_init(pgdat);
#ifdef CONFIG_NUMA_BALANCINGspin_lock_init(&pgdat->numabalancing_migrate_lock);pgdat->numabalancing_migrate_nr_pages = 0;pgdat->numabalancing_migrate_next_window = jiffies;
#endif
#ifdef CONFIG_TRANSPARENT_HUGEPAGEspin_lock_init(&pgdat->split_queue_lock);INIT_LIST_HEAD(&pgdat->split_queue);pgdat->split_queue_len = 0;
#endif/*  初始化pgdat->kswapd_wait等待队列  */init_waitqueue_head(&pgdat->kswapd_wait);/*  初始化页换出守护进程创建空闲块的大小*  为2^kswapd_max_order  */init_waitqueue_head(&pgdat->pfmemalloc_wait);
#ifdef CONFIG_COMPACTIONinit_waitqueue_head(&pgdat->kcompactd_wait);
#endifpgdat_page_ext_init(pgdat);/* 遍历每个管理区 */for (j = 0; j < MAX_NR_ZONES; j++) {struct zone *zone = pgdat->node_zones + j;unsigned long size, realsize, freesize, memmap_pages;unsigned long zone_start_pfn = zone->zone_start_pfn;/*  size为该管理区中的页框数,包括洞 */size = zone->spanned_pages;/* realsize为管理区中的页框数,不包括洞  /realsize = freesize = zone->present_pages;/** Adjust freesize so that it accounts for how much memory* is used by this zone for memmap. This affects the watermark* and per-cpu initialisations* 调整realsize的大小,即减去page结构体占用的内存大小  *//*  memmap_pags为包括洞的所有页框的page结构体所占的大小  */memmap_pages = calc_memmap_size(size, realsize);if (!is_highmem_idx(j)) {if (freesize >= memmap_pages) {freesize -= memmap_pages;if (memmap_pages)printk(KERN_DEBUG"  %s zone: %lu pages used for memmap\n",zone_names[j], memmap_pages);} else  /*  内存不够存放page结构体  */pr_warn("  %s zone: %lu pages exceeds freesize %lu\n",zone_names[j], memmap_pages, freesize);}/* Account for reserved pages* 调整realsize的大小,即减去DMA保留页的大小  */if (j == 0 && freesize > dma_reserve) {freesize -= dma_reserve;printk(KERN_DEBUG "  %s zone: %lu pages reserved\n",zone_names[0], dma_reserve);}if (!is_highmem_idx(j))nr_kernel_pages += freesize;/* Charge for highmem memmap if there are enough kernel pages */else if (nr_kernel_pages > memmap_pages * 2)nr_kernel_pages -= memmap_pages;nr_all_pages += freesize;/** Set an approximate value for lowmem here, it will be adjusted* when the bootmem allocator frees pages into the buddy system.* And all highmem pages will be managed by the buddy system.*//* 设置zone->spanned_pages为包括洞的页框数  */zone->managed_pages = is_highmem_idx(j) ? realsize : freesize;
#ifdef CONFIG_NUMA/* 设置zone中的节点标识符 */zone->node = nid;/* 设置可回收页面比率 */zone->min_unmapped_pages = (freesize*sysctl_min_unmapped_ratio)/ 100;/* 设置slab回收缓存页的比率 */zone->min_slab_pages = (freesize * sysctl_min_slab_ratio) / 100;
#endif/*  设置zone的名称  */zone->name = zone_names[j];/* 初始化各种锁 */spin_lock_init(&zone->lock);spin_lock_init(&zone->lru_lock);zone_seqlock_init(zone);/* 设置管理区属于的节点对应的pg_data_t结构 */zone->zone_pgdat = pgdat;/* 初始化cpu的页面缓存 */zone_pcp_init(zone);/* For bootup, initialized properly in watermark setup */mod_zone_page_state(zone, NR_ALLOC_BATCH, zone->managed_pages);/* 初始化lru相关成员 */lruvec_init(&zone->lruvec);if (!size)continue;set_pageblock_order();/* 定义了CONFIG_SPARSEMEM该函数为空 */setup_usemap(pgdat, zone, zone_start_pfn, size);/* 设置pgdat->nr_zones和zone->zone_start_pfn成员* 初始化zone->free_area成员* 初始化zone->wait_table相关成员*/ret = init_currently_empty_zone(zone, zone_start_pfn, size);BUG_ON(ret);/* 初始化该zone对应的page结构 */memmap_init(size, nid, j, zone_start_pfn);}/*  ......  */
}

5.2 流程讲解


初始化内存域数据结构涉及的繁重工作由free_area_init_core执行,它会依次遍历结点的所有内存域

static void __paginginit free_area_init_core(struct pglist_data *pgdat)
{enum zone_type j;int nid = pgdat->node_id;int ret;/*  ......  *//* 遍历每个管理区 */for (j = 0; j < MAX_NR_ZONES; j++) {struct zone *zone = pgdat->node_zones + j;unsigned long size, realsize, freesize, memmap_pages;unsigned long zone_start_pfn = zone->zone_start_pfn;/*  size为该管理区中的页框数,包括洞 */size = zone->spanned_pages;/* realsize为管理区中的页框数,不包括洞  /realsize = freesize = zone->present_pages;/*  ......  */
}

内存域的真实长度,可通过跨越的页数减去空洞覆盖的页数而得到。这两个值是通过两个辅助函数计算的,我不会更详细地讨论了。其复杂性实质上取决于内存模型和所选定的配置选项,但所有变体最终都没有什么意外之处

static void __paginginit free_area_init_core(struct pglist_data *pgdat)
{/*  ......  */if (!is_highmem_idx(j))nr_kernel_pages += freesize;/* Charge for highmem memmap if there are enough kernel pages */else if (nr_kernel_pages > memmap_pages * 2)nr_kernel_pages -= memmap_pages;nr_all_pages += freesize;/** Set an approximate value for lowmem here, it will be adjusted* when the bootmem allocator frees pages into the buddy system.* And all highmem pages will be managed by the buddy system.*//* 设置zone->spanned_pages为包括洞的页框数  */zone->managed_pages = is_highmem_idx(j) ? realsize : freesize;
#ifdef CONFIG_NUMA/* 设置zone中的节点标识符 */zone->node = nid;/* 设置可回收页面比率 */zone->min_unmapped_pages = (freesize*sysctl_min_unmapped_ratio)/ 100;/* 设置slab回收缓存页的比率 */zone->min_slab_pages = (freesize * sysctl_min_slab_ratio) / 100;
#endif/*  设置zone的名称  */zone->name = zone_names[j];/* 初始化各种锁 */spin_lock_init(&zone->lock);spin_lock_init(&zone->lru_lock);zone_seqlock_init(zone);/* 设置管理区属于的节点对应的pg_data_t结构 */zone->zone_pgdat = pgdat;/*  ......  */
}

内核使用两个全局变量跟踪系统中的页数。nr_kernel_pages统计所有一致映射的页,而nr_all_pages还包括高端内存页在内free_area_init_core始化为0

我们比较感兴趣的是调用的两个辅助函数

  • zone_pcp_init尝试初始化该内存域的per-CPU缓存, 定义在mm/page_alloc.c?v=4.7, line 5443

  • init_currently_empty_zone初始化free_area列表,并将属于该内存域的所有page实例都设置为初始默认值。正如前文的讨论,调用了memmap_init_zone来初始化内存域的页, 定义在mm/page_alloc.c?v=4.7, line 5458

我们还可以回想前文提到的,所有页属性起初都设置MIGRATE_MOVABLE。
此外,空闲列表是在zone_init_free_lists中初始化的

static void __paginginit free_area_init_core(struct pglist_data *pgdat)
{/*  ......  */{/* 初始化cpu的页面缓存 */zone_pcp_init(zone);/* 设置pgdat->nr_zones和zone->zone_start_pfn成员* 初始化zone->free_area成员* 初始化zone->wait_table相关成员*/ret = init_currently_empty_zone(zone, zone_start_pfn, size);BUG_ON(ret);/* 初始化该zone对应的page结构 */memmap_init(size, nid, j, zone_start_pfn);}/*  ......  */
}

6 memmap_init初始化page页面


在free_area_init_core初始化内存管理区zone的过程中, 通过memmap_init函数对每个内存管理区zone的page内存进行了初始化

memmap_init函数定义在mm/page_alloc.c?v=4.7, line

#ifndef __HAVE_ARCH_MEMMAP_INIT
#define memmap_init(size, nid, zone, start_pfn) \memmap_init_zone((size), (nid), (zone), (start_pfn), MEMMAP_EARLY)
#endif

memmap_init_zone函数完成了page的初始化工作, 该函数定义在mm/page_alloc.c?v=4.7, line 5139

至此,节点和管理区的关键数据已完成初始化,内核在后面为内存管理做得一个准备工作就是将所有节点的管理区都链入到zonelist中,便于后面内存分配工作的进行

内核在start_kernel()–>build_all_zonelist()中完成zonelist的初始化

7 总结


7.1 start_kernel启动流程


start_kernel()|---->page_address_init()|     考虑支持高端内存|     业务:初始化page_address_pool链表;|          将page_address_maps数组元素按索引降序插入|          page_address_pool链表; |          初始化page_address_htable数组.| |---->setup_arch(&command_line);||---->setup_per_cpu_areas();|     为per-CPU变量分配空间||---->build_all_zonelist()|     为系统中的zone建立后备zone的列表.|     所有zone的后备列表都在|     pglist_data->node_zonelists[0]中;||     期间也对per-CPU变量boot_pageset做了初始化. ||---->page_alloc_init()|---->hotcpu_notifier(page_alloc_cpu_notifier, 0);|     不考虑热插拔CPU ||---->pidhash_init()|     详见下文.|     根据低端内存页数和散列度,分配hash空间,并赋予pid_hash||---->vfs_caches_init_early()|---->dcache_init_early()|     dentry_hashtable空间,d_hash_shift, h_hash_mask赋值;|     同pidhash_init();|     区别:|         散列度变化了(13 - PAGE_SHIFT);|         传入alloc_large_system_hash的最后参数值为0;||---->inode_init_early()|     inode_hashtable空间,i_hash_shift, i_hash_mask赋值;|     同pidhash_init();|     区别:|         散列度变化了(14 - PAGE_SHIFT);|         传入alloc_large_system_hash的最后参数值为0;|

7.2 pidhash_init配置高端内存


void pidhash_init(void)|---->pid_hash = alloc_large_system_hash("PID", sizeof(*pid_hash), |         0, 18, HASH_EARLY|HASH_SMALL, &pidhash_shift, NULL, 4096);|     根据nr_kernel_pages(低端内存的页数),分配哈希数组,以及各个哈希|     数组元素下的哈希链表的空间,原理如下:|     number = nr_kernel_pages; |     number >= (18 - PAGE_SHIFT) 根据散列度获得数组元素个数|     number = roundup_pow_of_two(number);|     pidhash_shift = max{x | 2**x <= number}|     size = number * sizeof(*pid_hash);|     使用位图分配器分配size空间,将返回值付给pid_hash;||---->pidhash_size = 1 << pidhash_shift;||---->for(i = 0; i < pidhash_size; i++)|         INIT_HLIST_HEAD(&pid_hash[i]);

7.3 build_all_zonelists初始化每个内存节点的zonelists


void build_all_zonelists(void)|---->set_zonelist_order()|---->current_zonelist_order = ZONELIST_ORDER_ZONE;||---->__build_all_zonelists(NULL);|    Memory不支持热插拔, 为每个zone建立后备的zone,|    每个zone及自己后备的zone,形成zonelist||---->pg_data_t *pgdat = NULL;|     pgdat = &contig_page_data;(单node)||---->build_zonelists(pgdat);|     为每个zone建立后备zone的列表||---->struct zonelist *zonelist = NULL;|     enum zone_type j;|     zonelist = &pgdat->node_zonelists[0];||---->j = build_zonelists_node(pddat, zonelist, 0, MAX_NR_ZONES - 1);|     为pgdat->node_zones[0]建立后备的zone,node_zones[0]后备的zone|     存储在node_zonelist[0]内,对于node_zone[0]的后备zone,其后备的zone|     链表如下(只考虑UMA体系,而且不考虑ZONE_DMA):|     node_zonelist[0]._zonerefs[0].zone = &node_zones[2];|     node_zonelist[0]._zonerefs[0].zone_idx = 2;|     node_zonelist[0]._zonerefs[1].zone = &node_zones[1];|     node_zonelist[0]._zonerefs[1].zone_idx = 1;|     node_zonelist[0]._zonerefs[2].zone = &node_zones[0];|     node_zonelist[0]._zonerefs[2].zone_idx = 0;||     zonelist->_zonerefs[3].zone = NULL;|     zonelist->_zonerefs[3].zone_idx = 0;||---->build_zonelist_cache(pgdat);|---->pdat->node_zonelists[0].zlcache_ptr = NULL;|     UMA体系结构||---->for_each_possible_cpu(cpu)|     setup_pageset(&per_cpu(boot_pageset, cpu), 0);|详见下文|---->vm_total_pages = nr_free_pagecache_pages();|    业务:获得所有zone中的present_pages总和.||---->page_group_by_mobility_disabled = 0;|     对于代码中的判断条件一般不会成立,因为页数会最够多(内存较大)
查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 转换工具续

    2006/11/15 JASON ZHU 开始整理 送给大家 点这里进入PDF大全(一) 点这里进入PDF大全(三)14.Adobe Acraobat 8.0 Professional 软件大小:260M,安装后大小:约1400M软件语言:英文授权方式:注册 破解 有序列号主要用途:阅读、创建、编辑PDF格式文件,并转化其它…...

    2024/4/13 4:10:25
  2. Windows Server 2016 Nginx 安装配置详细图文教程

    1、下载Nginx官网地址:http://nginx.org/ 下载地址:http://nginx.org/en/download.html2、下载Windows Service Wrapper官网地址:https://github.com/kohsuke/winsw/ 下载地址:http://repo.jenkins-ci.org/releases/com/sun/winsw/winsw/3、解压Nginx压缩包文件到C盘根目录…...

    2024/4/12 12:01:40
  3. 解决google在国内加载很慢或无法打开的问题

    最近一直在用google,但是很不爽的是加载网页相当的慢,还莫名其妙的打不开网页,所以呢,就去找找资料,看看为什么打不开?后面也找到了一个比较有用的方法,立竿见影的哦。一、用记事本打开 C:\WINDOWS\system32\drivers\etc\hosts二、在文件末端换行添加以下内容:203.208.4…...

    2024/5/3 15:28:25
  4. 仓库管理系统

    一、课题来源二、系统概述1、本课题的研究意义2、本论文的目的、内容及作者主要贡献3、作者的主要贡献三、管理系统概述1、管理系统现状2、管理信息系统开发方法介绍四、系统调研及可行性分析1、系统调研2、可行性分析3、技术可行性分析五、系统及需求分析1 系统需求2 可行性分…...

    2024/5/3 2:58:17
  5. nginx实战之nginx安装教程

    1、nginx下载1.1 联网下载如wget http://nginx.org/download/nginx-1.9.4.tar.gz1.2 本地上传服务器 2、依赖准备yum install -y pcre pcre-develyum install -y zlib zlib-develyum install -y openssl openssl-devel 3、进行configure配置进入nginx目录执行脚本 ./configu…...

    2024/4/5 0:41:26
  6. Linux内存管理机制简介

    在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然。这是Linux内存管理的一个优秀特性,在这方面,区别于 Windows的内存管理。主要特点是,无论物理内存有多大,Linux 都将其充份利用,将一些程序调用过的硬盘数据读入内存,利…...

    2024/4/19 12:14:11
  7. 新版Chrome访问http页面无法打开麦克风、摄像头

    原因分析:1、http协议的网站的安全原因,不能访问本地的麦克风2、允许打开麦克风的请求有http://localhost/以及https请求。解决方法:方法一:访问摄像头,麦克风等必须使用https,将网站部署成https请求方法二:官方解释:You can run chrome with the --unsafely-treat-ins…...

    2024/4/18 3:39:08
  8. 进制转换,用栈实现

    今天看数据结构中的栈解决进制转换的问题,实现了一下。 linux下gdb调程序感觉还是会有一点麻烦啊,主要是不能一下子看到很多变量的值,要一个个地去p,好郁闷 代码如下: stack.cpp//用于实现栈的操作 #include <iostream> using namespace std; //const int MAX=100;/…...

    2024/4/12 12:01:58
  9. 速Geometric.Stackup.2.1.0.15228公差分析

    速Geometric.Stackup.2.1.0.15228公差分析使用 Geometric.Stackup.2.1.0.15228 的好处有:集成的CAD平台;多格式CAD文件支持;自动尺寸标注捕捉;交互式尺寸环;连续选择功能。Geometric.Stackup.2.1.0.15228可以在关键的零部件或装配体上使用快速、简单和精准的方法来执行尺寸…...

    2024/4/24 11:52:34
  10. nginx 入门级学习教程

    首先自行下载nginx启动方式:(两种)1. 双击打开nginx.exe 2. 命令行打开,cd 至 nginx安装目录输入: start nginx 停止关闭方式:(两种)1.打开任务管理器,找到nginx的两个进程(一个是守护进程,一个是工作线程),并关闭即可。2.打开cmd命令行,cd至 nginx安装目录,输入…...

    2024/4/9 17:37:41
  11. Google凌晨访问现异常,疑与退出风波有关

    北京时间3月16日凌晨00:54—01:00,Google网站出现访问502错误,导致全国网民在这一时间段无法正常访问。基调网络(networkbench) 第一时间通过RPC报表做了数据查询,监测出当时用户访问异常的散点图。基调网络(networkbench)同时针对Google的任务做了警报机制。网页访问一旦出…...

    2024/4/24 15:17:15
  12. Linux内存管理(2):内存描述

    linux内存管理建立在基本的分页机制基础上,在linux内核中RAM的某些部分将会永久的分配给内核,并用来存放内核代码以及静态内核数据结构。RAM的其余部分称为动态内存,这不仅是进程所需的宝贵资源,也是内核本身所需的宝贵资源。实际上,整个系统的性能取决于如何有效地管理动…...

    2024/5/8 5:11:27
  13. 银行核心操作系统as400 rs6000

    核心银行系统 核心银行系统,英文名称:Core Banking System。 核心银行系统指金融行业的银行核心业务系统。 目前的核心银行系统,都是以客户为中心,进行帐务处理、满足综合柜员制、并提供24小时服务的核心银行业务系统。技术实现 对于不同技术平台的选择,决定了不同的技术实…...

    2024/5/4 21:12:16
  14. 在Centos下安装Nginx教程

    在centos中安装nginx,官网中有很多个版本中,我试了nginx-0.8.55和nginx-0.6.39但在安装的时候总是提示各种编译错误,不管是重新编译C文件,还是修改就是不可以。 后来下载了官网的nginx-1.5.6版本,就能够安装成功。 1、首先从http://nginx.org/en/download.html下载nginx-1…...

    2024/4/12 12:02:52
  15. linux内存管理之数据结构

    linux内存管理之数据结构linux内存管理之数据结构 一物理空间管理 1 页表项2 物理页面管理对象page 二内存分区 1 过去的分区 2 当下的分区情况三 虚拟空间管理 1 进程虚存区域 2 进程地址空间 3 进程地址空间和进程虚存区域的关系一、物理空间管理1.1 页表项[include /asm-i38…...

    2024/5/6 15:57:07
  16. 五大最受欢迎的BUG管理系统

    五大最受欢迎的BUG管理系统经过认真的查找和比较,选出以下五大为比较受欢迎的BUG管理系统。 以下简单介绍一下其功能优缺点和资源获取方式吧:1、 QC(Quality Center) 是原Mercury Interactive公司(现已被HP收购)生产的企业级基于WEB测试管理工具,需要安装配置IIS和数据库,系…...

    2024/4/12 20:40:38
  17. Android手机无法使用google地图的问题的解决方案

    众所周知,谷歌已经退出中国市场有一段时间,恰巧公司的项目是给国外做的,于是以前信手拈来的百度和高德地图这个时候就用不上了,于是便用上了谷歌的地图,但是遇到一个问题,就是自己的手机是华为荣耀系列,用不上谷歌的地图,一直报一个类未进行初始化,具体给忘记了。但是…...

    2024/5/6 8:29:51
  18. 伙伴系统之伙伴系统概述--Linux内存管理(十五)

    日期内核版本架构作者GitHubCSDN2016-09-02Linux-4.7X86 & armgatiemeLinuxDeviceDriversLinux内存管理1 前景回顾1.1 Linux内存管理的层次结构Linux把物理内存划分为三个层次来管理层次描述存储节点(Node)CPU被划分为多个节点(node), 内存则被分簇, 每个CPU对应一个本地…...

    2024/4/26 18:14:19
  19. 嵌入式系统及其在医疗仪器设备应用

    嵌入式系统及其在医疗仪器设备应用   嵌入式系统是计算机技术、通信技术、半导体技术、微电子技术、语音图像数据传输技术,甚至传感器等先进技术和具体应用对象相结合后的更新换代产品,反映当代最新技术的先进水平。嵌入式系统是当今非常热门的研究领域,在PC市场已趋于稳定…...

    2024/5/6 14:29:22
  20. 关于chrome浏览器不能正常访问百度的解决方法

    关于chrome浏览器不能正常访问百度的解决方法 最近一段时间,使用chrome的时候,经常会出现不能正常访问百度的问题,搞得我每次都想怒卸chrome,回归foxfire。以下是几种解决方案。 对于我来说最有用的方案 使用管理员权限打开cmd,输入 netsh winsock reset,随后重启电脑。 …...

    2024/4/29 23:59:05

最新文章

  1. 记录我的程序猿副业首笔创收

    在这个充满机遇的数字时代&#xff0c;我&#xff0c;一个普通的程序猿&#xff0c;编程爱好者&#xff0c;终于在云端源想这个平台上收获了属于我的第一桶金。这是一个关于兼职、学习与成长的故事&#xff0c;希望能激发同在编程路上的你&#xff0c;勇敢迈出那一步。 先晒晒…...

    2024/5/8 19:01:36
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/5/7 10:36:02
  3. 518. 零钱兑换 II(力扣LeetCode)

    文章目录 518. 零钱兑换 II题目描述动态规划一维数组为什么不能交换两个for循环的顺序&#xff1f; 二维数组 518. 零钱兑换 II 题目描述 给你一个整数数组 coins 表示不同面额的硬币&#xff0c;另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数…...

    2024/5/4 9:01:49
  4. 腾讯云容器与Serverless的融合:探索《2023技术实践精选集》中的创新实践

    腾讯云容器与Serverless的融合&#xff1a;探索《2023技术实践精选集》中的创新实践 文章目录 腾讯云容器与Serverless的融合&#xff1a;探索《2023技术实践精选集》中的创新实践引言《2023腾讯云容器和函数计算技术实践精选集》整体评价特色亮点分析Serverless与Kubernetes的…...

    2024/5/5 8:50:37
  5. Java插值查找知识点(含面试大厂题和源码)

    插值查找&#xff08;Interpolation Search&#xff09;是一种改进的二分查找算法&#xff0c;适用于数据分布均匀的有序数组。插值查找的基本思想是&#xff0c;根据要查找的关键字与数组的最大值和最小值之间的比例&#xff0c;预测关键字可能存在的位置&#xff0c;从而跳过…...

    2024/5/6 20:21:17
  6. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/8 6:01:22
  7. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/7 9:45:25
  8. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  9. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/7 14:25:14
  10. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  11. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  12. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  13. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/7 11:36:39
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  15. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/6 1:40:42
  16. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  17. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/7 9:26:26
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  20. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  22. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  24. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/6 21:42:42
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57