在应用空间采用mmap进行内存映射时,内核调用的是ring_mmap函数;例如:我们在前面讲解时,讲解了pfring_open_consumer函数,这个函数里面调用mmap如下:
ring->buffer = (char *)mmap(NULL,memSlotsLen,PROT_READ|PROT_WRITE,
MAP_SHARED, ring->fd, 0);
在内核采用ring_mmap函数进行处理;该函数源码如下:
staticint ring_mmap(struct file *file,
struct socket *sock, struct vm_area_struct*vma)
{
struct sock *sk = sock->sk;
struct pf_ring_socket *pfr = ring_sk(sk);
//采用ring_sk将sk和pf_ring_socket建立关联,即取得环形缓冲区的内存地址
int rc;
unsigned long size = (unsigned long)(vma->vm_end - vma->vm_start);
if(enable_debug)
printk("[PF_RING] ring_mmap()called\n");
if(ring_alloc_mem(sk) != 0) {
printk("[PF_RING] ring_mmap(): unableto allocate memory\n");
return(-EINVAL);
}
if(size % PAGE_SIZE) {
if(enable_debug)
printk("[PF_RING] ring_mmap()failed: len is not multiple of PAGE_SIZE\n");
//打印错误信息,因为分配内存的长度不是PAGE_SIZE的整数倍
return(-EINVAL);
}
if(enable_debug)
printk("[PF_RING] ring_mmap() called,size: %ld bytes [bucket_len=%d]\n",
size, pfr->bucket_len);
//打印调试信息,内存分配的大小及桶的大小
if((pfr->dna_device == NULL) &&(pfr->ring_memory == NULL)) {
if(enable_debug)
printk("[PF_RING] ring_mmap()failed: ""mapping area to an unbound socket\n");
return -EINVAL;
}
//dna_device==NULL表示没有采用dna技术
if(pfr->dna_device == NULL) {
/*如果用户空间试图mmap的size超过了tot_mem的大小的话,直接报错*/
if(size >pfr->slots_info->tot_mem) {
if(enable_debug)
printk("[PF_RING] ring_mmap()failed: "
"area too large [%ld > %d]\n",
size, pfr->slots_info->tot_mem);
return(-EINVAL);
}
if(enable_debug)
printk("[PF_RING] mmap[slot_len=%d]"
"[tot_slots=%d] for ring on device %s\n",
pfr->slots_info->slot_len, pfr->slots_info->min_num_slots,
pfr->ring_netdev->dev->name);
/*do_memory_mmap函数作用是进行内存映射,pfr->ring_memory为分配的环形队列空间;要mmap操作,实际上就是调用remap_pfn_range函数把pfr->ring_memory映射到用户空间;
*/
if((rc = do_memory_mmap(vma, size,pfr->ring_memory, VM_LOCKED, 0)) < 0)
return(rc);
} else {
int count = pfr->mmap_count;
/* DNA Device */
/*
do_memory_mmap函数的定义如下:
static int do_memory_mmap(structvm_area_struct *vma,
unsigned long size, char *ptr, u_int flags, intmode)
{
unsigned long start;
/* we do not want to have this area swapped out, lock it */
vma->vm_flags |= flags;
start = vma->vm_start;
if(enable_debug)
printk("[PF_RING] do_memory_mmap(mode=%d, size=%lu,ptr=%p)\n", mode, size, ptr);
while(size > 0) {
int rc;
if(mode == 0) {
#if(LINUX_VERSION_CODE >=KERNEL_VERSION(2,6,11))
rc = remap_vmalloc_range(vma, ptr, 0);
break; /* Do not iterate */
#else
rc = remap_pfn_range(vma, start,kvirt_to_pa((unsigned long)ptr), PAGE_SIZE, PAGE_SHARED); //remap_pfn_range作用是进行内存映射
#endif
} else if(mode == 1) {
rc = remap_pfn_range(vma, start,__pa(ptr) >> PAGE_SHIFT, PAGE_SIZE, PAGE_SHARED);
} else {
rc = remap_pfn_range(vma, start,((unsigned long)ptr) >> PAGE_SHIFT, PAGE_SIZE, PAGE_SHARED);
}
//根据3种模式的不同进行内存映射;
if(rc) {
if(enable_debug)
printk("[PF_RING]remap_pfn_range() failed\n");
return(-EAGAIN);
}
start += PAGE_SIZE;
ptr += PAGE_SIZE;
if(size > PAGE_SIZE) {
size -= PAGE_SIZE;
} else {
size = 0;
}
}
return(0);
}
pfr->ring_memory 即为分配的环形队列空间。要mmap操作,实际上就是调用remap_pfn_range函数把pfr->ring_memory 映射到用户空间,就ok了。
remap_pfn_range()函数的原型:
int remap_pfn_range(struct vm_area_struct *vma, unsigned long virt_addr,unsigned long pfn, unsigned long size, pgprot_t prot);
*/
printk("[PF_RING] mmapcount(%d)\n", count);
pfr->mmap_count++;
switch(count) {
case 0:
if((rc = do_memory_mmap(vma, size,
(void*)pfr->dna_device->packet_memory, VM_LOCKED, 1)) < 0)
return(rc);
break;
case 1:
if((rc = do_memory_mmap(vma, size,
(void*)pfr->dna_device->descr_packet_memory, VM_LOCKED, 1)) < 0)
return(rc);
break;
case 2:
if((rc = do_memory_mmap(vma, size,
(void *)pfr->dna_device->phys_card_memory,(VM_RESERVED | VM_IO), 2)) < 0)
return(rc);
break;
default:
return(-EAGAIN);
}
}
if(enable_debug)
printk("[PF_RING] ring_mmapsucceeded\n");
return 0;
}
/************************************** */
上面分析的源码就是应用程序采用mmap和内核ring_mmap进行内存映射的代码,可以用来共享内核和用户空间之间的内存;