内核态用户态的数据拷贝,有点不明白

2020-07-04 15:13:39 +08:00
 yangyuhan12138

我们读一个文件的时候是需要系统调用的,也就是从用户态切换到内核态,然后内核态读取数据,再将数据拷贝到用户态,这中间的拷贝到底是个什么过程,内存里我们读取的这个文件的数据到底有几份,我觉得应该是一份,内核态拷贝到用户态应该只是把这份文件的地址给了用户态,不知道我理解的有没有问题...

7011 次点击
所在节点    程序员
47 条回复
louettagfh
2020-07-05 10:54:47 +08:00
@vk42 去看看源码 mmap_region() --> call_mmap(file, vma)

vma 指向的是哪里 你说 page cache, 那 page cache 在哪里? 块设备接口读的内容存在哪里的?
vk42
2020-07-05 11:07:54 +08:00
@louettagfh
无语了,你先把两个基本概念内核地址空间和物理地址搞清楚好吧。你不妨做个实验,看看 page cache 的地址和用户 mmap 得到的地址是一个地址吗?
(怎么感觉又回到以前给学生出题的时候了……)
louettagfh
2020-07-05 11:12:35 +08:00
@vk42 听不懂我说什么就算了
vk42
2020-07-05 11:16:00 +08:00
@louettagfh
你高兴就好哈,我也是无聊到不行在这种低级错误上这么较真……
louettagfh
2020-07-05 11:25:56 +08:00
@vk42 你前面提了 Page Cache, 所以我问你 Page Cache 在哪里?

不懂就看源码 更别给学生出题 误人子弟
lyi4ng
2020-07-05 11:29:44 +08:00
是真的在内存里拷贝一遍啊,就是把物理内存 A 段上的内容拷贝到 B 段,而 B 段有个虚拟内存映射,只不过再校验下 flag 决定是不是 COW,不然会是一个 zero page,涉及到的概念有虚拟内存和物理内存之间的分页机制还有 COW
vk42
2020-07-05 11:33:00 +08:00
@louettagfh
摆脱你自己基本概念都搞不懂就先别扯别的了好吧。源码我写文件系统的时候已经看够多了,而且你这个问题根本不需要拿源码说事。那我不误人子弟你不妨指教一下 page cache 在哪里?
aheadlead
2020-07-05 11:33:07 +08:00
@louettagfh #25 。。。。映射后的也是用户地址空间啊
louettagfh
2020-07-05 12:05:16 +08:00
@aheadlead 我当然知道 mmap 返回的是用户地址空间

读写文件: 用户调用 mmap --> 申请 VMa --> VMa->file 指向对应的 file --> address_space --> page cache
真正读写的时候触发缺页中断,kernel 读 page 至 page cache, 用户通过 VMa 可以直接访问 page, 难道用户进程还能直接读文件?
aheadlead
2020-07-05 12:24:59 +08:00
@louettagfh #29 不知道你俩到底争这个有啥意思。。。。。这不都挺懂的吗

vk42
2020-07-05 13:29:40 +08:00
@aheadlead 我也是挺纳闷啊,就是随口纠正一个简单的概念错误怎么就来来扯这么多……
no1xsyzy
2020-07-05 14:31:22 +08:00
@louettagfh #10 请区分:地址空间 vs 内存
你术语用错了…… 访问“属于内核的内存”并不一定通过“内核地址空间”,直接访问内核地址空间那还叫 “映射”( map ) 干嘛……
何况,地址空间映射目标不一定在内存上,甚至不一定是电子元件。
no1xsyzy
2020-07-05 14:34:49 +08:00
@Jooooooooo #8 我觉得说的 “地址” 是指 “指针” 而不是 “目录” 或者……
直白地讲,指针是 C 叫法,地址是 ASM 叫法。
Jooooooooo
2020-07-05 14:36:20 +08:00
@no1xsyzy 不用扣这个细节. 简单说就是你拿着这个东西能不能知道这个东西内容是什么, 如果能就是内容, 如果不能, 就是索引 /指针 /地址.
no1xsyzy
2020-07-05 14:44:21 +08:00
@Jooooooooo #34 我是说楼主并不是认为 read 之后还得再去磁盘读,而是 read 完之后返回一个指针,这个指针内已经存储了需要的内容。
何况 C 里面超过 4 字节(或者 64-bit 后 8 字节)就没有内容,只有指针。
liuxu
2020-07-05 15:34:04 +08:00
@louettagfh #10 https://man7.org/linux/man-pages/man2/mmap.2.html

mmap() creates a new mapping in the virtual address space of the calling process. The starting address for the new mapping is specified in addr. The length argument specifies the length of the mapping (which must be greater than 0). If addr is NULL, then the kernel chooses the (page-aligned) address at which to create the mapping; this is the most portable method of creating a new mapping. If addr is not NULL, then the kernel takes it as a hint about where to place the mapping; on Linux, the kernel will pick a nearby page boundary (but always above or equal to the value specified by /proc/sys/vm/mmap_min_addr) and attempt to create the mapping there. If another mapping already exists there, the kernel picks a new address that may or may not depend on the hint. The address of the new mapping is returned as the result of the call. The contents of a file mapping (as opposed to an anonymous mapping; see MAP_ANONYMOUS below), are initialized using length bytes starting at offset offset in the file (or other object) referred to by the file descriptor fd. offset must be a multiple of the page size as returned by sysconf(_SC_PAGE_SIZE). After the mmap() call has returned, the file descriptor, fd, can be closed immediately without invalidating the mapping.
Jooooooooo
2020-07-05 15:36:10 +08:00
@no1xsyzy 啥叫指针存储了需要的内容, 一个指针好几兆大小?
yangyuhan12138
2020-07-05 17:17:49 +08:00
@louettagfh
@zhgg0
@vk42
@lyi4ng
@no1xsyzy
@Jooooooooo
谢谢大家的热心解答,不过感觉大家说的都太专业了,我是做 Java 开发的,确实对底层的系统层面的知识不是很了解,主要是我最近看了 fork,还有零拷贝这些知识,对有些概念还是很模糊,有个大概认识,比如 fork 就是将虚拟内存考了一份,然后写时复制,然后就是读文件在内核态和用户态进行切换的问题了(我在试图把这些内容串起来理解)
我再把我的想法描述清楚点,我认为内核态和用户态可以粗略的理解为内核进程(权限高,想干嘛就干嘛)和用户进程(权限低很多事干不了)吧?(我不知道对不对),然后是因为用户进程没办法和磁盘进行交互读写,所以需要调用内核进程来完成相应的功能,于是我们调用读文件的时候,其实系统是切换到了内核进程执行读文件的代码,然后将文件内容读到了内存里,看大家完大家的说法,感觉这个应该是读到了内核进程的专属一块内存里,用户进程依然没法访问,所以才又将内容拷贝了一份?现在内存里有了两份文件的内容?
但是我还是有点不明白,如果是按照进程来理解的话我们操作的都应该是虚拟地址才对,为啥内核不直接把读进来的内容在内存上的物理地址告诉用户进程(比如是哪几页),然后用户进程维护个虚拟地址就好了,为啥还要在物理内存上考一份,这个文件的内容在物理内存里到底是一份还是两份?
我觉得我现在的问题可能是不太明白啥是用户态和内核态
yangyuhan12138
2020-07-05 17:21:05 +08:00
@louettagfh
@zhgg0
@vk42
@lyi4ng
@no1xsyzy
@Jooooooooo
是否内存在一开始就已经被分为了两部分 一部分是内核可访问的,一部分是用户可访问的,但是读出来的数据被放在了内核可访问的内存区域,所以要将他拷贝到用户可访问的内存区域
FutherAll
2020-07-05 17:55:19 +08:00
一般是用 PTE (页表项)的权限位去控制的,至于为什么要二次拷贝,我理解也是为了权限控制,进程用户态的内存空间是当前进程可访问的;内核态的 Page Cache 可以共享给多个进程,由内核控制
用 mmap 可以避免二次拷贝。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/687113

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX