copy_to_user的实现以及get_user_pages/kmap_atomic/kmalloc

本文详细阐述了Linux内核中copy_to_user和__copy_user函数的实现原理,对比了两者在用户空间数据拷贝过程中的不同策略,包括使用__copy_to_user和__copy_user的具体场景,以及它们与get_user_pages函数的交互过程。重点解释了kmap_atomic的使用及其在低端内存与高端内存映射的区别。
部署运行你感兴趣的模型镜像

转自 http://blog.youkuaiyun.com/eroswang/article/details/4130991

copy_to_user的实现:
copy_to_user
__copy_to_user
__copy_to_user_inatomic
     __put_user_size
     __put_user_asm/__copy_to_user_ll
__copy_to_user_ll or __copy_user
一般很少使用__copy_to_user, 而是使用__copy_user()...__copy_user():是直接使用用户空间的指针来完成拷贝工作的!!!只有没有在用户上下文是才使用下面的方法:
get_user_pages并没有直接使用用户态地址,它是先根据用户空间的地址得到对应的page(内存物理地址),又把这个page kmap_atomic了一下了
然后再进行memcpy
请看
Arch/i386/lib/usercopy.c
__copy_to_user_ll()
            retval = get_user_pages(current, current->mm,
                    (unsigned long)to, 1, 1, 0, &pg, NULL);
             ......
            maddr = kmap_atomic(pg, KM_USER0);
            memcpy(maddr + offset, from, len);
            kunmap_atomic(maddr, KM_USER0);

而kmap_atomic的实现分两种情况:
1. 当用户的page在低端内存时,内核已经对其进行了一一映射,直接可以使用,即,直接:return page->virtual;
2. 当用户的page在高端内存时,由于内核并没有对其进行一一映射,这时就需要建立临时映射了...
-------------------
kmalloc是基于slab实现的,当slab中没有空闲的object可以分配时,它会向buddy system请求新的物理页cache_grow -> kmem_getpages -> alloc_page_node, 得到物理页后,就将这个物理页所对应的kernel virtual address返回给slab.因为slab allocator只使用low memory,所以这个virtual addr就是phy_addr + PAGE_OFFSET。这里并没有对page table的操作,kernel就直接使用这个virtual address来访问物理内存,因为映射在初始化的时候早就做好了。

kmalloc()返回的指针就是这个virtual address + 偏移量(指向slab中不同的object).

如果在kernel中访问high memory的话,就需要通过kmap_atomic把这个物理页map到kernel的虚拟空间后,才能访问。

 

 

get_user_pages是用来将用户空间的页(pages)映射(锁)入内存,并得到它们的页结构(struct page)指针。
    page_cache_release(
            pdx->DmaInfo[channel].PageList[i]
            );
用到get_user_pages函数还需page_cache_release来解锁

您可能感兴趣的与本文相关的镜像

Wan2.2-T2V-A5B

Wan2.2-T2V-A5B

文生视频
Wan2.2

Wan2.2是由通义万相开源高效文本到视频生成模型,是有​50亿参数的轻量级视频生成模型,专为快速内容创作优化。支持480P视频生成,具备优秀的时序连贯性和运动推理能力

`kmap_local_page` 是 Linux 内核中用于将 **高端内存页(highmem)或普通页** 映射到内核虚拟地址空间的一个高效接口,特别适用于 **本地 CPU 上的临时映射**。它是 `kmap` 系列函数的现代替代方案之一,旨在提高性能并支持非抢占式上下文。 --- ### ✅ 函数定义与使用 ```c #include <linux/highmem.h> static inline void *kmap_local_page(struct page *page); ``` #### 功能: 将给定的 `struct page` 临时映射到当前 CPU 的 **本地固定映射区域(fix-mapped region)**,返回其内核虚拟地址。 > 适用于所有类型的页:常规页、高端内存页(HighMem)、ZSMALLOC 分配的页等。 --- ### 📌 示例代码 ```c #include <linux/mm.h> #include <linux/highmem.h> #include <linux/slab.h> void example_usage(struct page *page) { void *vaddr; /* 1. 建立本地映射 */ vaddr = kmap_local_page(page); /* 2. 操作页面内容(例如清零) */ memset(vaddr, 0, PAGE_SIZE); /* 3. 取消映射 */ kunmap_local(vaddr); // 注意:传入的是地址,不是 page! } ``` --- ### 🔍 工作原理详解 | 特性 | 说明 | |------|------| | ✅ **无需全局锁** | 使用 per-CPU 的映射槽位,避免了传统 `kmap()` 中需要自旋锁保护的问题。 | | ✅ **支持抢占** | 在 PREEMPT=1 的系统上也能安全使用(只要不被中断中途调用)。 | | ✅ **高性能** | 直接使用 `FIX_KMAP_BEGIN + current_cpu` 区域进行映射,开销极小。 | | ✅ **统一接口** | 不区分 `highmem` 和普通页,简化代码逻辑。 | #### 内部机制简述: - 利用 `fix_to_virt(FIX_KMAP_BEGIN + idx)` 来获取一个每 CPU 固定虚拟地址。 - 每个 CPU 有多个 slot(通常为 `KM_TYPE_NR` 个),允许嵌套映射不同用途的页。 - 实际实现依赖于 `__kmap_local_page()` 或汇编辅助函数,在 `arch/` 目录下完成页表更新。 --- ### 🆚 对比其他 kmap 接口 | 函数 | 是否推荐 | 适用场景 | 锁? | 支持抢占? | |------|----------|-----------|-------|-------------| | `kmap()` / `kunmap()` | ❌ 过时 | 高端内存页 | 需要 `kmap_lock` | 否(禁抢占) | | `kmap_atomic()` / `kunmap_atomic()` | ⚠️ 替代中 | 快速原子映射 | 无锁但禁抢占 | 仅可用于原子上下文 | | `kmap_local_page()` / `kunmap_local()` | ✅ 推荐 | 所有临时映射 | 无锁 | ✔️ 支持抢占 | > 💡 自 Linux 5.7 起,`kmap_local_*` 成为主流推荐方式,尤其在新驱动和核心子系统中广泛采用。 --- ### 🛠️ 实现细节(以 x86_64 为例) 虽然 `kmap_local_page()` 多数是 inline 函数,但底层由架构相关代码支撑: ```c // arch/x86/mm/kmap.c void *kmap_local_page(struct page *page) { enum fixed_addresses idx = FIX_KMAP_BEGIN; struct page *ppte = virt_to_page(__fix_to_virt(idx)); pte_t *pte = pte_offset_kernel(pmd_off_k(__fix_to_virt(idx)), __fix_to_virt(idx)); pte_val(*pte) = mk_pte(page, __pgprot(_PAGE_KERNEL)) | _PAGE_ENC; __flush_tlb_single((unsigned long)__fix_to_virt(idx)); return __fix_to_virt(idx); } ``` > 注:实际实现更复杂,涉及 PTE 更新、TLB 刷新、加密内存(SEV)处理等。 --- ### ⚠️ 使用注意事项 1. **不能跨进程/中断持久使用**:映射只在当前任务上下文中有效。 2. **不能睡眠?** - `kmap_local_page()` 允许在可睡眠上下文中使用(不同于 `kmap_atomic`)。 - 但在某些架构上仍建议尽快 `kunmap_local`。 3. **参数传递注意**: ```c kunmap_local(vaddr); // 正确:传指针 // kunmap_local(page); // 错误! ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值