create_mapping如何创建内存映射表

本文详细解析了ARM920T处理器MMU的工作原理及Linux内核中的内存映射实现。介绍了MMU地址转换流程,包括TLB、一级表项与二级表项的计算方法,并展示了create_mapping函数如何为物理地址创建静态映射。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 ARM920T的MMU工作原理

下图显示了MMU地址转化关系

 

一级表项的地址(pmd) = ( (TLB) & (0xFFFFC000) ) + ( (Table Index)<<2 );

注1:TLB(Translate table base),即转换表基地址

注2:由于每个表项占32位(4Bytes),因此(Table Index)<<2

一级表项的内容(*pmd) = ( (pte) & (0xFFFFFC00) ) + prot_l1;

-------------------------------------------------------------------------------------

二级表项的地址(pte) =  ( (pte) & (0xFFFFFC00) )  + ( (L2 table index) <<2 );

二级表项的内容(*pte) = ( (PA) & (0xFFFFF000) ) + prot_pte;

 

 

2 create_mapping分析

//利用该函数可以为物理地址创建内存的静态映射

static void __init create_mapping(struct map_desc *md)
{
     ...  

     pgd = pgd_offset_k(addr);

     //计算pgd的地址(64bits) = ((mm)->pgd+pgd_index(addr))

     //                              = ((mm)->pgd+((addr) >> 21) )

     //pmd地址(32bits)        = ((mm)->pgd+((addr) >> 20) )

     end = addr + length;

     //PGDIR_SIZE=(1UL << PGDIR_SHIFT)=2M

     //一次处理2个连续的一级表项:2*1M = 2M
     do {
            unsigned long next = pgd_addr_end(addr, end);

            alloc_init_section(pgd, addr, next, phys, type);

            phys += next - addr;
            addr = next;
      } while (pgd++, addr != end);
}

 

//尝试使用段映射

static void __init alloc_init_section(pgd_t *pgd, unsigned long addr,
          unsigned long end, unsigned long phys,
          const struct mem_type *type)
{
     pmd_t *pmd = pmd_offset(pgd, addr); //一级表指针

     //pmd地址(32bits)        = ((mm)->pgd+((addr) >> 20) )

     

     /*  当大小和地址1MB对齐时,使用段映射 */
     if (((addr | end | phys) & ~SECTION_MASK) == 0) {
         pmd_t *p = pmd;

         if (addr & SECTION_SIZE)
              pmd++;

         do {
             *pmd = __pmd(phys | type->prot_sect);
             phys += SECTION_SIZE;
         } while (pmd++, addr += SECTION_SIZE, addr != end);

         //2MB,循环2次

         flush_pmd_entry(p);
      } else {
          /*  使用small页映射,必须为二级表分配空间4Kb(1page) */
          alloc_init_pte(pmd, addr, end, __phys_to_pfn(phys), type);
       }
}

 

//small页映射

static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,
      unsigned long end, unsigned long pfn,
      const struct mem_type *type)
{
     pte_t *pte;  //二级表指针

     

     //当一级表项内容为空时,为二级表项分配空间2*512*4 = 4Kb(1Page)

     if (pmd_none(*pmd)) {
         pte = alloc_bootmem_low_pages(2 * PTRS_PER_PTE * sizeof(pte_t));
          __pmd_populate(pmd, __pa(pte) | type->prot_l1);

         //给一级表项赋值,pmd为指针,指向需赋值的地址

         //pmdp[0] = __pmd(pmdval);
         //pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t));

 

      }

      pte = pte_offset_kernel(pmd, addr);
      do {
          set_pte_ext(pte, pfn_pte(pfn, __pgprot(type->prot_pte)), 0);
          pfn++;
      } while (pte++, addr += PAGE_SIZE, addr != end);

      //2MB,2Mb/4Kb = 512次(最多)
}

 

 

 

.macro armv3_set_pte_ext wc_disable=1
 str r1, [r0], #-2048  @ linux version  MMU看到的h/w pte0/1

 eor r3, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY

 bic r2, r1, #PTE_SMALL_AP_MASK @ keep C, B bits
 bic r2, r2, #PTE_TYPE_MASK
 orr r2, r2, #PTE_TYPE_SMALL

 tst r3, #L_PTE_USER   @ user?
 orrne r2, r2, #PTE_SMALL_AP_URO_SRW  @设置SVC模式为读写,USR模式为READONLY

 tst r3, #L_PTE_WRITE | L_PTE_DIRTY @ write and dirty?
 orreq r2, r2, #PTE_SMALL_AP_UNO_SRW 

设置SVC模式为读写,USR模式为不能读,PTE_SMALL_AP_URO_SRW | PTE_SMALL_AP_UNO_SRW为SVC & USR可读写

#define PAGE_SHARED __pgprot(pgprot_val(pgprot_user) | _L_PTE_READ | \
     L_PTE_WRITE)

#define _L_PTE_READ L_PTE_USER | L_PTE_EXEC

PAGE_SHARED 使用PAGE_SHARE映射的空间USR模式也能进行读写

关于读写的权限,参考ARM V5 reference.pdf  协处理器的C3寄存器和PTE的APx(3~0),PMD的domain参数

 tst r3, #L_PTE_PRESENT | L_PTE_YOUNG @ present and young?
 movne r2, #0

 .if /wc_disable
#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
 tst r2, #PTE_CACHEABLE
 bicne r2, r2, #PTE_BUFFERABLE
#endif
 .endif
 str r2, [r0]   @ hardware version
 .endm

 

3 Linux MMU

#define PTRS_PER_PTE512 { (h/w + s/w )  *  4byte = 4096 }
#define PTRS_PER_PMD 1
#define PTRS_PER_PGD 2048

4 9260EK 内存映射情况

Author: WoodPecker <Pecker.hu@gmail.com>

 

import pymysql import pandas as pd import os import csv from sqlalchemy import create_engine, text from sqlalchemy.exc import SQLAlchemyError def import_csv_to_mysql(config): """ 将CSV文件数据导入MySQL数据库 参数: config (dict): 包含数据库连接和文件信息的配置字典 返回: bool: 导入成功返回True,失败返回False """ try: # 检查CSV文件是否存在 if not os.path.exists(config['csv_path']): raise FileNotFoundError(f"CSV文件不存在: {config['csv_path']}") # 创建数据库连接 connection_str = f"mysql+pymysql://{config['user']}:{config['password']}@{config['host']}/{config['database']}" engine = create_engine(connection_str, echo=False) # 获取目标表结构 with engine.connect() as conn: result = conn.execute(text(f"DESCRIBE `{config['table']}`")) table_columns = [row[0] for row in result] print(f"目标表 '{config['table']}' 的列: {table_columns}") # 读取CSV文件 with open(config['csv_path'], 'r', encoding=config.get('encoding', 'utf-8')) as f: csv_reader = csv.reader(f, delimiter=config.get('delimiter', ',')) headers = next(csv_reader) # 列映射检查 missing_columns = set(table_columns) - set(headers) if missing_columns: print(f"⚠️ 警告: CSV中缺少表列: {missing_columns}") column_mapping = {header: header for header in headers if header in table_columns} print(f"列映射: {column_mapping}") data_rows = [] for row in csv_reader: # 跳过空行 if not any(row): continue # 创建映射行 mapped_row = {} for csv_col, value in zip(headers, row): if csv_col in column_mapping: db_col = column_mapping[csv_col] mapped_row[db_col] = value data_rows.append(mapped_row) # 检查是否有有效数据 if not data_rows: print("❌ CSV文件中未找到有效数据") return False # 分批导入参数 chunk_size = 1000 total_rows = len(data_rows) success_count = 0 # 创建临时表 temp_table = f"temp_{config['table'].lower()}_{os.getpid()}" with engine.begin() as conn: # 创建临时表 conn.execute(text(f"CREATE TEMPORARY TABLE `{temp_table}` LIKE `{config['table']}`")) # 分批处理数据 for i in range(0, total_rows, chunk_size): # 获取当前批次数据 chunk = data_rows[i:i + chunk_size] # 转换为DataFrame并处理空值 df_chunk = pd.DataFrame(chunk) df_chunk = df_chunk.where(pd.notnull(df_chunk), None) try: # 导入到临时表 df_chunk.to_sql( name=temp_table, con=conn, if_exists='append', index=False ) success_count += len(chunk) print(f"✅ 已插入 {success_count}/{total_rows} 条记录") except Exception as e: print(f"❌ 批量插入失败: {e}") print(f"问题数据示例: {chunk[0] if chunk else '无数据'}") return False # 从临时表导入目标表 try: cols = ', '.join([f'`{col}`' for col in column_mapping.values()]) insert_sql = f""" INSERT INTO `{config['table']}` ({cols}) SELECT {cols} FROM `{temp_table}` """ conn.execute(text(insert_sql)) print(f"🎉 成功导入 {success_count} 条记录到表 '{config['table']}'") # 清理临时表 conn.execute(text(f"DROP TEMPORARY TABLE IF EXISTS `{temp_table}`")) return True except SQLAlchemyError as e: print(f"❌ 最终导入失败: {e}") return False except Exception as e: print(f"❌ 导入过程出错: {e}") return False # 使用示例 if __name__ == "__main__": # 配置参数 IMPORT_CONFIG = { 'host': 'localhost', # 数据库主机 'user': 'root', # 数据库用户名 'password': '123456', # 数据库密码 'database': 'bdpd_rmv', # 数据库名称 'table': 'RMV_KDSQ_MODEL_MODEL_INFO', # 目标表名 'csv_path': r'D:\历史项目留存2\诺褀2025\python加工浦发模型模拟\底层表模拟\RMV_KDSQ_MODEL_MODEL_INFO.csv', # CSV文件路径 'delimiter': ',', # CSV分隔符 'encoding': 'gbk' # 文件编码 } print("=== CSV数据导入MySQL ===") print(f"源文件: {IMPORT_CONFIG['csv_path']}") print(f"目标表: {IMPORT_CONFIG['table']}") # 执行导入 result = import_csv_to_mysql(IMPORT_CONFIG) # 输出结果 if result: print("✅ 数据导入完成") else: print("❌ 数据导入失败") 逐步解析这边的代码我是小白,第一次接触
最新发布
07-23
<think>我们逐步解析这段代码,帮助小白理解其功能和工作流程。 1. **整体功能**:这段代码是一个Python函数,用于将CSV文件的数据导入到MySQL数据库的指定表中。它使用临时表作为中间步骤,分批导入数据,最后将数据从临时表插入到目标表。 2. **导入的库**: - `pymysql`:用于连接MySQL数据库。 - `pandas as pd`:用于数据处理,这里主要用来将数据块转换为DataFrame并插入临时表。 - `os`:用于操作文件路径。 - `csv`:用于读取CSV文件。 - `sqlalchemy`:用于创建数据库引擎和执行SQL语句。 3. **函数定义**:`import_csv_to_mysql(config)` - 参数:`config`是一个字典,包含数据库连接信息和CSV文件信息。 - 返回值:成功返回True,失败返回False。 4. **主要步骤**: a. **检查CSV文件是否存在**:如果不存在则抛出异常。 b. **创建数据库连接**:使用`sqlalchemy`的`create_engine`创建引擎。 c. **获取目标表结构**:通过执行`DESCRIBE`语句获取目标表的列名。 d. **读取CSV文件**: - 使用`csv.reader`读取文件,获取表头(第一行)。 - 将表头与目标表的列进行对比,找出CSV中缺少的列(警告)。 - 建立列映射:只保留目标表中存在的列。 - 逐行读取数据,跳过空行,并根据列映射构建每行数据的字典。 e. **检查有效数据**:如果没有有效数据则返回False。 f. **分批导入**: - 定义每批的大小(chunk_size=1000)。 - 创建一个临时表(结构同目标表)。 - 将数据分批转换为DataFrame,并将空值转换为None(对应数据库的NULL)。 - 将每批数据插入临时表(使用`to_sql`方法)。 g. **从临时表导入目标表**: - 使用`INSERT INTO ... SELECT ...`语句将临时表的数据插入目标表。 - 删除临时表。 h. **异常处理**:在整个过程中捕获异常并打印错误信息。 5. **使用示例**: - 在`__main__`中,定义了一个配置字典`IMPORT_CONFIG`,然后调用`import_csv_to_mysql`函数。 6. **关键点**: - 使用临时表:可以避免在导入过程中出错时污染目标表,同时提高导入效率(特别是大文件)。 - 分批处理:避免一次性加载整个大文件导致内存溢出。 - 列映射:只导入目标表存在的列,忽略CSV中多余的列,并警告缺失列。 7. **注意事项**: - 临时表是会话级别的,连接关闭后会自动删除,但代码中显式删除是良好的习惯。 - 使用`to_sql`方法时,设置`if_exists='append'`表示追加数据。 - 空字符串和空值的处理:使用`pd.notnull`将空字符串等转换为None,以便数据库正确识别为NULL。 8. **可能的问题**: - 如果CSV文件很大,但内存有限,可以考虑逐批读取CSV而不是一次性读入。 - 列名大小写敏感问题:MySQL在Linux下默认区分大小写,在Windows下不区分。代码中使用了与CSV头相同的列名,需要确保目标表列名与CSV头匹配(包括大小写)。 9. **改进建议**: - 对于非常大的文件,可以边读取边分批导入,而不是一次性读取整个文件到内存。 - 增加更多的错误处理,例如数据类型转换错误。 现在,我们将按照代码的执行顺序逐步解析:</think>### 逐步解析代码(针对小白) #### 1. **导入库** ```python import pymysql # MySQL数据库连接 import pandas as pd # 数据处理库 import os # 文件路径操作 import csv # CSV文件读写 from sqlalchemy import create_engine, text # 数据库引擎和SQL执行 from sqlalchemy.exc import SQLAlchemyError # 异常处理 ``` #### 2. **核心函数 `import_csv_to_mysql`** - **功能**:将CSV数据导入MySQL表 - **参数**:`config`字典(包含数据库和文件配置) - **流程**: ##### (1) 检查文件是否存在 ```python if not os.path.exists(config['csv_path']): raise FileNotFoundError(...) # 文件不存在时报错 ``` ##### (2) 创建数据库连接 ```python engine = create_engine(f"mysql+pymysql://{user}:{password}@{host}/{database}") ``` ##### (3) 获取目标表结构 ```python with engine.connect() as conn: result = conn.execute(text(f"DESCRIBE `{table}`")) table_columns = [row[0] for row in result] # 提取列名 ``` ##### (4) 读取CSV并映射列 ```python with open(csv_path) as f: csv_reader = csv.reader(f) headers = next(csv_reader) # 读取表头 # 检查缺失列(警告) missing_columns = set(table_columns) - set(headers) # 创建列映射:{CSV列名: 数据库列名} column_mapping = {header: header for header in headers if header in table_columns} # 逐行处理数据 for row in csv_reader: mapped_row = {} for csv_col, value in zip(headers, row): if csv_col in column_mapping: db_col = column_mapping[csv_col] mapped_row[db_col] = value # 只保留有效列 data_rows.append(mapped_row) ``` ##### (5) 分批导入临时表 ```python # 创建临时表(结构克隆目标表) conn.execute(text(f"CREATE TEMPORARY TABLE `{temp_table}` LIKE `{table}`")) # 分块处理(每1000行) for i in range(0, total_rows, chunk_size): chunk = data_rows[i:i+1000] df_chunk = pd.DataFrame(chunk) df_chunk = df_chunk.where(pd.notnull(df_chunk), None) # 空值转None # 导入临时表 df_chunk.to_sql( name=temp_table, con=conn, if_exists='append', # 追加模式 index=False ) ``` ##### (6) 最终导入目标表 ```python # 从临时表复制数据到目标表 cols = ', '.join([f'`{col}`' for col in column_mapping.values()]) insert_sql = f""" INSERT INTO `{table}` ({cols}) SELECT {cols} FROM `{temp_table}` """ conn.execute(text(insert_sql)) # 删除临时表 conn.execute(text(f"DROP TEMPORARY TABLE `{temp_table}`")) ``` #### 3. **主程序逻辑** ```python if __name__ == "__main__": IMPORT_CONFIG = { # 配置示例 'host': 'localhost', 'user': 'root', 'password': '123456', 'database': 'bdpd_rmv', 'table': 'RMV_KDSQ_MODEL_MODEL_INFO', 'csv_path': r'D:\...\RMV_KDSQ_MODEL_MODEL_INFO.csv', 'delimiter': ',', 'encoding': 'gbk' } result = import_csv_to_mysql(IMPORT_CONFIG) # 执行导入 ``` #### 关键设计亮点: 1. **临时表机制**:避免导入失败污染原表 2. **分批处理**:防止内存溢出(大文件场景) 3. **列映射验证**:自动过滤无效列 4. **空值转换**:`pd.notnull()`处理空字符串 5. **事务管理**:`with engine.begin()`保证原子性
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值