Skip to content

Latest commit

 

History

History
76 lines (67 loc) · 6.66 KB

内核模块内存共享.md

File metadata and controls

76 lines (67 loc) · 6.66 KB

之前说过内核态用户态的事情,一个系统中所有的一切都依托于资源,而对于资源访问的控制衍生出特权的概念,而用户态内核态的区别就在于:

  1. 用户态的进程可以访问的资源有限制
  2. 内核态对于资源的访问没有限制

然而一个程序的运行并非就单纯的限制在某个特权等级下,因此就必然涉及到特权切换的过程。 在设计上,用户空间和内核空间的内存是不能够直接互访的,因为linux使用的虚拟内存机制,用户空间的数据可能会被换出,当内核空间使用用户空间指针时,数据可能已经不在内存了。

但其实并非没有办法,熟悉驱动开发的自然会知道copy_to_usercopy_from_user这两个函数。这两个函数可以完成从一个空间到另一个空间的复制,但是呢消耗也是明显的,看一下源码:

copy_to_user(void __user *to, const void *from, unsigned long n)
{
 if (likely(check_copy_size(from, n, true)))
  n = _copy_to_user(to, from, n);
 return n;
}

这是4.15的内核源码,很明显的在函数中有一个check_copy_size的检查项,然而更久远之前的内核源码中,实际上是有一个access_ok的指针检查的:

static inline long copy_to_user(void __user *to,
  const void *from, unsigned long n)
{
 might_fault();
 if (access_ok(VERIFY_WRITE, to, n))
  return __copy_to_user(to, from, n);
 else
  return n;
}

这儿直接套用别人的解答:

copy_to_user在每次拷贝时需要检测指针的合法性,也就是用户空间的指针所指向的地址的确是一段该进程本身的地址,而不是指向了不属于它的地方,而且每次都会拷贝一次数据,频繁访问内存,由于虚拟地址连续,物理地址不一定会连续,从而造成CPU的CACHE频繁失效,从而使速度降低。   

简单来说,速度的降低实际在于每次拷贝都会做的合法性检查。 那么为了提高速度,还有一个方法就是使用mmap函数。

mmap仅在第一次使用时为进程建立页表,也就是将一段物理地址映射到一段虚拟地址上,以后操作时不再检测其地址的合法性(合法性交由CPU页保护异常来做),另一方面是内核下直接操作mmap地址,可以不用频繁拷贝,也就是说在内核下直接可用指针向该地址操作,而不再在内核中专门开一个缓冲区,然后将缓冲区中的数据拷贝一次进来,mmap一般是将一段连续的物理地址映射成一段虚拟地址,当然,也可以将每段连续,但各段不连续的物理地址映射成一段连续的虚拟地址,无论如何,其物理地址在每段之中是连续的,这样一来,就不会造成CPU的CACHE频繁失效,从而大大节约时间

简单来说就是共享内存机制,允许多个进程共享相同的存储区,这样数据就不需要来回复制,全靠指针的方式读写这一段内存,因此内核态对内存的读写也将直接反映到用户态下。 这样就在看一个函数系列shm*mmap实现共享内存是通过映射普通文件机制实现的,而shmat则是通过系统共享内存机制实现。

1、mmap保存到实际硬盘,实际存储并没有反映到主存上。优点:储存量可以很大(多于主存);缺点:进程间读取和写入速度要比主存的要慢。
2、shm保存到物理存储器(主存),实际的储存量直接反映到主存上。优点,进程间访问速度(读写)比磁盘要快;缺点,储存量不能非常大(多于主存)

使用上看:如果分配的存储量不大,那么使用shm;如果存储量大,那么使用mmap。

简单来说:mmap是文件操作,shm*是内存操作。

实操

那需求也很简单,就像先前的netlink的操作一样,快速把一个新的进程信息传输到用户态下。 本来找到一个轮子:Linux 进程通信之:内存共享(Shared Memory),这里面的代码挺好的实现了进程间共享内存,但是问题在于是通过shm来实现的,这个方法没法在内核和用户态之间使用,因此还是回到mmap()上才行。这是因为在linux中,用户内存内核内存是独立的,在各自的地址空间。

看一下对于mmap的解释:

map操作提供了一种机制,让用户程序直接访问设备内存,这种机制,相比较在用户空间和内核空间互相拷贝数据,效率更高。在要求高性能的应用中比较常用。mmap映射内存必须是页面大小的整数倍,面向流的设备不能进行mmap,mmap的实现和硬件有关。

也就是说文件被映射进内存,映射后进程访问文件就可以像访问内存一样,无需read()或者write()等操作,而共享是因为此文件被同时映射而已。

而核心实现还是把一个虚拟内存区映射到指定page开始的连续物理页上。

6979cf7a-885f-4938-a96f-2125b3f3b9ed.jpg

代码上通过一个轮子改一改,但是要思考的是,内核模块把信息写入一个buff中,用户程序读取buff,这是异步的,就是说二者互不干扰,只不过能同时对一个buff作操作而已,因此如何让信息同步就是一个比较费脑筋的问题,但这是设计上的问题,而非代码。

参考资料