.net 零拷贝_模拟总线型以太网数据帧发送过程

.net 零拷贝_模拟总线型以太网数据帧发送过程mmap/munmap接口是用户空间的最常用的一个系统调用接口,无论是在用户程序中分配内存、读写大文件,链接动态库文件,还是多进程间共享内存,都可以看到mmap/munmap的身影。

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

mmap/munmap接口是用户空间的最常用的一个系统调用接口,无论是在用户程序中分配内存、读写大文件,链接动态库文件,还是多进程间共享内存,都可以看到mmap/munmap的身影。

mmap说明

mmap系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而Posix或系统V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一。

mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。

mmap/munmap函数声明如下:

#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags,
		   int fd, off_t offset);
int munmap(void *addr, size_t length);

addr

addr用于指定映射到进程空间的起始地址,为了应用程序的可移植性,一般设置为NULL,让内核来选择一个合适的地址。

mmap函数就是为了寻找一个可用的地址空间,将文件描述符fd对应的文件从偏移量offset开始,copy文件的length个字符进入用户地址空间。将起始地址返回。为什么第一个参数还要传用户地址空间的指针呢?这不是骑驴找驴吗?

这个addr参数可以NULL,这种情况我们比较容易理解。当addr不为NULL的时候,这其实是个建议查找地址,用来指导内存区定位的线索,是个用户指定的经验值。

  • 如果flags带上了MAP_FIXED标志,表示用户王八吃秤砣,铁了心要addr这个地址作为映射起始地址,直接把addr返回。
  • 如果没带上MAP_FIXED标志,则首先在建议地址addr附近寻找合适的区域。
  • addr为NULL,让内核自己选择。

length、fd、offset

将fd对应的文件,从offset位置开始,长为len的内容映射到内存地址空间。

prot

prot参数通常表示映射页面的的读写权限,可以有如下参数组合:

  • PROT_EXEC:表示映射的页面是可以执行的。
  • PROT_READ:表示映射的页面是可以读取的。
  • PROT_WRITE:表示映射的页面是可以写入的。
  • PROT_NONE:表示映射的页面是不可访问的。

flags

flags参数也是一个重要的参数,有如下常见的参数:

  • MAP_SHARED:创建一个共享映射的区域。多个进程可以通过共享映射方式来映射一个文件,这样其他进程也可以看到映射内容的改变,修改后的内容会同步到磁盘文件中。
  • MAP_PRIVATE:创建一个私有的写时复制的映射。多个进程可以通过私有映射的方式来映射一个文件,这样其他进程不会看到映射内容的改变,修改后的内容也不会同步到磁盘文件中。
  • MAP_ANONYMOUS:创建一个匿名映射,即没有关联到文件的映射。
  • MAP_FIXED:使用参数addr创建映射,如果内核无法映射指定地址addr,那么mmap会返回失败,参数addr要求按页对齐。如果addr和length指定的进程地址空间和已有的VMA区域重叠,那么内核会调用do_munmap()函数把这段重叠区域销毁,然后重新映射新的内容。
  • MAP_POPULATE:对于文件映射来说,会提前预读文件内容到映射区域,该特性只支持私用映射。

mmap映射类型

参数fd可以看出mmap映射是否和文件相关联,因此Linux内核中映射可以分为匿名映射和文件映射。

  • 匿名映射:没有映射对应的相关文件,这种映射的内存区域的内容会被初始化为0。
  • 文件映射:映射和实际文件相关联,通常是把文件的内容映射到进程地址空间,这样应用程序就可以像操作进程地址空间一样读写文件。

最后根据文件关联性和映射区域是否共享等属性,又可以分为如下4种:

私有映射 共享映射
匿名映射 私有匿名映射-通常用于内存分配 共享匿名映射-通常用于进程间共享内存
文件映射 私有文件映射-通常用于加载动态库 共享文件映射-通常用于内存映射IO,进程间通信

私有匿名映射

当使用参数fd=-1且flags=MAP_ANONYMOUS | MAP_PRIVATE时,创建的mmap映射是私有匿名映射。私有匿名映射最常见的用途是在glibc分配大块内存中,当需要的分配的内存大于MMAP_THREASHOLD(128KB)时,glibc会默认使用mmap代替brk来分配内存。

共享匿名映射

当使用参数fd=-1且flags=MAP_ANONYMOUS | MAP_SHARED。在这种情况下,创建共享匿名映射。共享匿名映射让相关进程共享一块内存区域,通常用于父子进程的之间通信。

创建共享匿名映射有如下两种方式:

  1. fd=-1且flags= MAP_ANONYMOUS|MAP_SHARED。在这种情况下,do_mmap_pgoff()->mmap()函数最终调用shmem_zero_setup()来打开一个”/dev/zero”特殊的设备文件。
  2. 另外一个是直接打开”/dev/zero”设备文件,然后使用这个文件句柄来创建mmap。

私有文件映射

私有文件映射时flags的标志位被设置为MAP_PRIVATE,那么就会创建私有文件映射。

私有文件映射的最常用的场景是加载动态共享库。

共享文件映射

创建文件映射时flags的标志位被设置为MAP_SHARED,那么就会创建共享文件映射。如果prot参数指定了PROT_WRITE,那么打开文件需要制定O_RDWR标志位。共享文件映射通常有如下场景:

  • 读写文件:把文件内容映射到进程地址空间,同时对映射的内容做了修改,内核的回写机制(writeback)最终会把修改的内容同步到磁盘中。
  • 进程间通信:进程之间的进程地址空间相互隔离,一个进程不能访问到另外一个进程的地址空间。如果多个进程都同时映射到一个相同的文件,就实现了多进程间的共享内存的通信。如果一个进程对映射内容做了修改,那么另外的进程是可以看到的。

mmap的使用

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/syscall.h>

int main() { 
   

        int fd = open("/root/demo/aa", O_CREAT | O_RDWR, 777);

        size_t length = 1024;
        ftruncate(fd, length); // 一定要这句,否则会报Bus error
        char *addr = mmap(NULL, length, PROT_READ | PROT_WRITE,
                       MAP_SHARED, fd, 0);

        char* data = "hello";
        memcpy(addr, data, 5);

        char result[5];
        memcpy(result, addr, 5);

        printf("%s\n", result);

        sleep(100);

        munmap(addr, length);
        close(fd);

        return 0;
}

对一个空的新文件进行mmap前,需要往里面写入一点内容,否则会报错Bus error。发生错误的原因是因为mmap不能去扩展一个内容为空的新文件,因为大小为0,所有本没有与之对应的合法的物理页,不能扩展。

解决方案有2个:

  • 一个就是上面的链接里的方案: 只需要在新创建的空文件中先写入一些数据即可;
  • 另外一个是通过ftruncate对新建立的文件进行扩展后再映射修改。

查询进程所有的mmap映射:

# cat /proc/1078/maps
5636aadb5000-5636aadb6000 r--p 00000000 08:10 41881                      /root/demo/a.out
5636aadb6000-5636aadb7000 r-xp 00001000 08:10 41881                      /root/demo/a.out
5636aadb7000-5636aadb8000 r--p 00002000 08:10 41881                      /root/demo/a.out
5636aadb8000-5636aadb9000 r--p 00002000 08:10 41881                      /root/demo/a.out
5636aadb9000-5636aadba000 rw-p 00003000 08:10 41881                      /root/demo/a.out
5636abae0000-5636abb01000 rw-p 00000000 00:00 0                          [heap]
7f9f4e83f000-7f9f4e861000 r--p 00000000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f9f4e861000-7f9f4e9d9000 r-xp 00022000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f9f4e9d9000-7f9f4ea27000 r--p 0019a000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f9f4ea27000-7f9f4ea2b000 r--p 001e7000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f9f4ea2b000-7f9f4ea2d000 rw-p 001eb000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f9f4ea2d000-7f9f4ea33000 rw-p 00000000 00:00 0
7f9f4ea3b000-7f9f4ea3c000 r--p 00000000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f9f4ea3c000-7f9f4ea5f000 r-xp 00001000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f9f4ea5f000-7f9f4ea67000 r--p 00024000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f9f4ea67000-7f9f4ea68000 rw-s 00000000 08:10 768                        /root/demo/aa
7f9f4ea68000-7f9f4ea69000 r--p 0002c000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f9f4ea69000-7f9f4ea6a000 rw-p 0002d000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f9f4ea6a000-7f9f4ea6b000 rw-p 00000000 00:00 0
7ffe68bf2000-7ffe68c13000 rw-p 00000000 00:00 0                          [stack]
7ffe68dd5000-7ffe68dd8000 r--p 00000000 00:00 0                          [vvar]
7ffe68dd8000-7ffe68dd9000 r-xp 00000000 00:00 0                          [vdso]

查看内存映射的区域地址范围:

# ll /proc/1078/map_files/
total 0
dr-x------ 2 root root  0 Jul 12 11:02 ./
dr-xr-xr-x 9 root root  0 Jul 12 11:02 ../
lr-------- 1 root root 64 Jul 12 11:02 5636aadb5000-5636aadb6000 -> /root/demo/a.out*
lr-------- 1 root root 64 Jul 12 11:02 5636aadb6000-5636aadb7000 -> /root/demo/a.out*
lr-------- 1 root root 64 Jul 12 11:02 5636aadb7000-5636aadb8000 -> /root/demo/a.out*
lr-------- 1 root root 64 Jul 12 11:02 5636aadb8000-5636aadb9000 -> /root/demo/a.out*
lr-------- 1 root root 64 Jul 12 11:02 5636aadb9000-5636aadba000 -> /root/demo/a.out*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4e83f000-7f9f4e861000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4e861000-7f9f4e9d9000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4e9d9000-7f9f4ea27000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea27000-7f9f4ea2b000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea2b000-7f9f4ea2d000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea3b000-7f9f4ea3c000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea3c000-7f9f4ea5f000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea5f000-7f9f4ea67000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*
lrw------- 1 root root 64 Jul 12 11:02 7f9f4ea67000-7f9f4ea68000 -> /root/demo/aa*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea68000-7f9f4ea69000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea69000-7f9f4ea6a000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*

查看内存映射地址范围的内容:

# cat /proc/1078/map_files/7f9f4ea67000-7f9f4ea68000
hello

我们看到/root/demo/aa文件已经映射到内存中取了,占用空间0x7f9f4ea67000-0x7f9f4ea68000=4K,虽然申请的是1K,但是映射的分配是以页面为单位分配的,即最小分配4K。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/192741.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号