mpcap 是利用mmap 文件映射的方式把抓包文件映射进程的虚拟地址空间。从而减少了write函数一次内存拷贝到页缓存的过程,从而提高效率。
在简书上 《深入剖析mmap原理 - 从三个关键问题说起》
(https://www.jianshu.com/p/eece39beee20)及《深入浅出Linux】关于mmap的解析》(https://www.jianshu.com/p/755338d11865)有关于mmap的相关的数据分析。详细的可以去了解一下。
下面内容来源于简书,详细介绍了write系统调用写文件流程
- Step1:进程(用户态)调用write系统调用,并告诉内核需要写入数据的开始地址与长度(告诉内核写入的数据在哪)。
- Step2:内核write方法,将校验用户态的数据,然后复制到kernel buffer(这里是Page Cache)。 [ ps: 特意查了ext4 write的内核实现,write是直接将user buffer copy到page中 ]
- Step3: 由操作系统调用,将脏页回写到磁盘(通常这是异步的)
再来简单讲讲使用mmap时,写入文件流程:
- Step1:进程(用户态)将需要写入的数据直接copy到对应的mmap地址(内存copy)
- Step2: 2.1) 若mmap地址未对应物理内存,则产生缺页异常,由内核处理 2.2) 若已对应,则直接copy到对应的物理内存
- Step3:由操作系统调用,将脏页回写到磁盘(通常这是异步的)
可以看到mmap在100byte写入时已经基本达到最大写入性能,而write调用需要在4096(也就是一个page size)时,才能达到最大写入性能。 从测试结果可以看出,在写小数据时,mmap会比write调用快,但在写大数据时,反而没那么快。------本人也不清楚数据的可靠性,还要自己实际对比测试。
每次写入大小 | mmap 耗时 | write 耗时 |
---|---|---|
100 bytes | 2.84s | 22.86s |
512 bytes | 2.51s | 5.43s |
1024 bytes | 2.48s | 3.48s |
2048 bytes | 2.47s | 2.34s |
4096 bytes | 2.48s | 1.74s |
8192 bytes | 2.45s | 1.67s |
10240 bytes | 2.49s | 1.65s |
好了,回到正文,来介绍mpcap的结构及使用,mpcap所有代码就这四个文件。也体现了vpp的架构分层。
mpcap基础库mpcap_close、mpcap_init、mpcap_map
src/vppinfra/mpcap.h
src/vppinfra/mpcap.c
基于vlib_buffer_t结构的写文件接口函数mpcap_add_buffer
src/vnet/mpcap.h
mpcap 单元测试例子
src/plugins/unittest/mpcap_node.c :。
- mpcap_main数据结构
结构体定义在src/vppinfra/mpcap.h文件中。
代码语言:javascript复制typedef struct
{
/** File name of mpcap output. */
char *file_name;
/** spinlock, initialized if flagged MPCAP_FLAG_THREAD_SAFE */
clib_spinlock_t lock;
/** Number of packets to capture. */
u32 n_packets_to_capture;
/** Packet type */
mpcap_packet_type_t packet_type;
/** Maximum file size */
u64 max_file_size;
/** Base address */
u8 *file_baseva;
/** current memory address */
u8 *current_va;
/** Number of packets currently captured. */
u32 n_packets_captured;
/** Pointer to file header in svm, for ease of updating */
mpcap_file_header_t *file_header;
/** flags */
u32 flags;
#define MPCAP_FLAG_INIT_DONE (1 << 0)
#define MPCAP_FLAG_THREAD_SAFE (1 << 1)
#define MPCAP_FLAG_WRITE_ENABLE (1 << 2)
/** Bytes written */
u32 n_mpcap_data_written;
/** Vector of mpcap data. */
u8 *mpcap_data;
/** Packets in mapped mpcap file. */
u64 packets_read;
/** Min/Max Packet bytes */
u32 min_packet_bytes, max_packet_bytes;
} mpcap_main_t;
结构体字段还是比较容易理解的。主要说下最后三个字段是mpcap_map使用。是另外一个进程统计当前已经读了多少报文。及最大最小报文长度。
packets_read :统计当前读了多少报文。
min_packet_bytes:最小报文字节数。
max_packet_bytes:最大报文字节数。
- mpcap 的使用
vpp提供了详细使用例子,可以在文件中src/plugins/unittest/mpcap_node.c看到。
1、定义自己的test_mpcap_main
这里有一点需要说明,默认flag标识是不开启多线程锁的。需要多线程使用的话,需要flag标识置位MPCAP_FLAG_THREAD_SAFE。
代码语言:javascript复制static mpcap_main_t test_mpcap_main = {
.file_name = "/tmp/mpcap_unittest.pcap",
.n_packets_to_capture = 15,
.packet_type = MPCAP_PACKET_TYPE_ethernet,
};
2、初始化test_mpcap_main
代码语言:javascript复制mpcap_init (&test_mpcap_main);
3、将报文写入文件中
写报文函数在src/vnet/mpcap.h文件中。
代码语言:javascript复制 mpcap_add_buffer (&test_mpcap_main, vm, now_time, b[0],pkt_lens)
- 总结 mpcap相关函数的使用还是比较简单的。这里只是简单介绍了使用方法。可以根据mpcap的代码来实现自己的基于mmap的写文件接口。