long vmsplice(int fd, const struct iovec *iov, unsigned long nr_segs, unsigned int flags); 这个系统调用将用户空间的内存映射到内核空间,从而避免了实际的内存写操作,提高了系统效率。
socket使用mmap
如果你的内核提供了CONFIG_PACKET_MMAP选项,那么恭喜你,对网络包你可以采用mmap了,用了mmap,你就节省了内核从内核内存区拷贝到用户内存区的这一步,效率提高很多。
下面我们说说怎么用这么强的功能。
int fd;
fd= socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))
socket函数的protocal参数采用了ETH_P_ALL,表示抓取所有以太帧。SOCK_RAW表示抓取到的包的数据是IP包。
setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, sizeof(req)) 其中req参数是个结构,如下所示:
struct tpacket_req {
unsigned int tp_block_size; /* Minimal size of contiguous block */ unsigned int tp_block_nr; /* Number of blocks */ unsigned int tp_frame_size; /* Size of frame */
unsigned int tp_frame_nr; /* Total number of frames */ };
该结构是用来在内核中创建循环缓冲区,这个缓冲区随后会被映射到调用进程的用户空间。
这块内存在内核中用block来组织,每个块是连续的物理内存区,每 个块的大小是tp_block_size个字节,每个块最多可以装tp_block_size/tp_frame_size个帧。 这几个参数是由用户程序设置的,但有以下一些 tp_block_size 必须是 PAGE_SIZE 的倍数
tp_frame_size 必须大于 TPACKET_HDRLEN ,每个帧都有帧头结构用来描述meta信息,如时间戳
tp_frame_size 必须是 TPACKET_ALIGNMENT的倍数,TPACKET_ALIGNMENT的值是15。 tp_frame_nr 必须是frames_per_block*tp_block_nr 每个帧由以下的部件组成: - struct tpacket_hdr 帧头
- pad 填充物,起对齐到16字节边界的作用 - struct sockaddr_ll
- Gap 填充物,起对齐到16字节边界的作用 - Start+tp_mac: 可选mac地址
- Start+tp_net: 包数据,16字节对齐 - Pad to align to TPACKET_ALIGNMENT=16 如果用以下参数:
tp_block_size= 4096 tp_frame_size= 2048 tp_block_nr = 4 tp_frame_nr = 8
我们就得到了,如下的数据结构: 代码:
block #1 block #2 block #3 block #4
+---------+---------+ +---------+---------+ +---------+---------+ +---------+---------+
| frame 1 | frame 2 | | frame 3 | frame 4 | | frame 5 | frame 6 | | frame 7 | frame 8 |
+---------+---------+ +---------+---------+ +---------+---------+ +---------+---------+
接着用户程序使用
mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 映射后得到的是一个block数组,每个block中包含有若干帧。 程序直接就可以读这些内容了。
linux下零拷贝技术的实现(zero-copy)
2009-11-19 09:55
作者:jk.li (jk.li@foxmail.com)
零拷贝(zero-copy)是只使用内存映射技术实现在内核与应用层之间的数据传递,由于内核与应用层使用的是同一块内存,取消了内核向用户空间的拷贝过程,会很大程度上提高系统效率。
实际应用,有一数据采集设备A,产生大量数据流,应用层读取通过该设备驱动获得数据,处理后,在通过网卡分发出去。在实际应用的过程中,数据要由A驱动copy到应用层,再由应用层处理后再次走网卡驱动,cpu除了要进行大量的应用处理(计算)还要copy,效率很低,接收端会出现数据卡的现象。采用zero-copy技术就可以解决这个问题。 具体实现: 内核端
#define SH_MEM_SIZE (1024*1024*2) int order;
unsigned long shmem_virt_addr; unsigned long shmem_phy_addr; order = get_order(SH_MEM_SIZE);
shmem_virt_addr = __get_free_pages(GFP_KERNEL, order); SetPageReserved(virt_to_page(shmem_virt_addr)); shmem_phy_addr=virt_to_phys(shmem_virt_addr); printk(\"[shmem_phy_addr= %x]\\n\copy_to_user(arg,&shmem_phy_addr,sizeof(long)); //shmem_virt_addr 内核使用,可以直接写入数据 //shmem_phy_addr 需要提供给应用层
应用
shmem_phy_addr = get_phy_addr(); //获得内核传过来的物理地址
printf(\"[shmem_phy_addr= %x]\\n\fd=open(\"/dev/mem\if(fd == -1){
perror(\"open mem\"); }
buf=mmap(0,SH_MEM_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,fd,shmem_phy_addr);
if(buf == MAP_FAILED){
perror(\"mmbuf error!\\n\"); }
//buf存放共享后的内存指针,应用层可以直接读取buf中的内容 代码:
原理介绍:
http://www.ibm.com/developerworks/cn/linux/l-cn-zerocopy1/index.html http://www.chineselinuxuniversity.net/articles/42547.shtml http://www.ibm.com/developerworks/cn/java/j-zerocopy/ http://www.linuxjournal.com/article/6345?page=0,0
实现:
http://blog.csdn.net/drizztzou/article/details/18293
http://hi.baidu.com/ljk0209/blog/item/95b025cbfb060cf7536f37.html