本人目前就职于烽火集成,从事云计算产品架构设计相关工作,长期专注于内核、虚拟化、分布式、云计算等方向。
技术交流请联系:xiaoding@fiberhome.com
在32位系统下,linux的地址空间划分以0xC0000000为分界线,以下为用户空间,以上为系统空间。64位下是使用0xffffffff80000000来进行划分。
每个程序运行的时候,都有自己独立的用户地址空间,内核空间则是共用的。如下图所示:
linux提供了标准的函数 copy_from_user,copy_to_user,来提供内核对用户空间的读写。
但是这种方式使用了memcpy来将数据从用户空间copy到内核空间,或者相反的操作,这样在大量数据交换时就会产生很大的性能损耗。
那么如何能够zerocopy来进行数据共享?
首先要注意的是内核是可以访问到全局每一个地址的。
所以如果进程通过syscall陷入到内核中时,内核有了进程的上下文,可以直接访问进程空间的任一逻辑地址。
验证程序如下,
程序主要实现了一个char设备,然后通过文件操作write来传递相关信息。内核直接对用户空间地址进行读写操作。
用户态程序,
内核程序
这样的访问方式有着很大的局限性,这个内核态必须要具备用户空间的信息,而且只能在返回之前使用,内核中其他进程就无法通过这个地址来访问用户空间的地址。
函数get_user_pages提供了从用户空间的地址获取其物理页的方法。
所以这样我们来实现一个更加通用的读写方式,先获取物理page列表,然后再把page用kmap进行映射重新获取新的地址,来访问用户空间的信息。
内核程序
上面只是实现了单页面跨内核线程的zerocopy,对于多页面该怎么做呢?
需要注意一点,在用户空间连续的地址空间,如果超过一页,在内核是会被取到多个page,
这样如果要读取page信息需要分别kamp页面,并通过各自的偏移来读取信息。