这里面的pfpf值是什么意思?

pf在英雄联盟里是什么_百度知道
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。
pf在英雄联盟里是什么
我有更好的答案
皮肤??????????
为您推荐:
其他类似问题
英雄联盟的相关知识
等待您来回答电脑里的任务管理器性能下的“PF使用”PF是什么意思?指什么?_百度知道
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。
电脑里的任务管理器性能下的“PF使用”PF是什么意思?指什么?
我有更好的答案
F使用率是指Pagefile使用率. 建议..
把开机时不必要启动的东西很关掉,另外去系统进程里停掉一些进程:硬件升级是最好的方法(单独升内存会使机器的命中率降低 即PF降 CPU升)
改善的方法很多:装个小一点的系统;管理工具/计算机管理,看其需要手工关闭一些系统服务喽.,关闭随开机启动的程序,清下注册表垃圾,做一下系统盘的碎片整理,设置一下虚拟内存及环境变量,降低下分辨率及刷新率,更绝的话去控制面板&#47,正确的含义应为:正在使用的物理内存和虚拟内存之和。一般来说开机后正常是200左右。 PF使用率开机就太高有可能是你开机随系统启动的程序太多太大
采纳率:73%
为您推荐:
其他类似问题
任务管理器的相关知识
等待您来回答trackbacks-0
版权所有,转载请注明出处独孤九贱内核版本:Linux 2.6.30.9PF_RING版本:4.1.0最近看了一个PF_RING的实现,看了个大概,发上来大家讨论讨论,共同学习。一、什么是PF_RINGPF_RING是一个第三方的内核数据包捕获接口,类似于libpcap,它的官方网址是:二、为什么需要PF_RING一切为了效率,按照其官方网站上的测试数据,在Linux平台之上,其效率至少高于libpcap 50% - 60%,甚至是一倍。更好的是,PF_RING提供了一个修改版本的libpcap,使之建立在PF_RING接口之上。这样,原来使用libpcap的程序,就可以自然过渡了。三、声明1、这不是“零拷贝”,研究“零拷贝”的估计要失望,如果继续看下去的话;2、这不是包截获接口,如果需要拦截、修改内核数据包,请转向Netfilter;3、本文只分析了PF_RING最基础的部份。关于DNA、TNAPI,BPF等内容不包含在内。四、源码的获取svn co&&最近好像全流行svn了。五、编译和使用接口分为两部份,一个是内核模块,一个是用户态的库cd my_pf_ring_goes_here&cd kernel&make&sudo insmod ./pf_ring.ko&cd ../userland&make&在源码目录中,关于用户态的库有使用的现成的例子,很容易依葫芦画瓢。后文也会提到用户态库的实现的简单分析,可以两相比照,很容易上手。而且源码目录中有一个PDF文档,有详细的API介绍,建议使用前阅读。六、实现分析初步1、核心思路A、在内核队列层注册Hook,获取数据帧。B、在内核创建一个环形队列(这也是叫RING的原因),用于存储数据,并使用mmap映射到用户空间。这样,避免用户态的系统调用,也是提高性能的关键所在。C、创建了一个新的套接字类型PF_RING,用户态通过它与内核通信。2、模块初始化模块源码只有一个文件,在目录树kernel/pf_ring.c,嗯,还有一个头文件,在kernel/linux下static int __init ring_init(void){&&int i,&&printk("[PF_RING] Welcome to PF_RING %s ($Revision: 4012 $)\n"& & & &&&"(C) 2004-09 L.Deri &[email]deri@ntop.org[/email]&\n", RING_VERSION);& & & &&& & & & //注册名为PF_RING的新协议&&if((rc = proto_register(&ring_proto, 0)) != 0)& & return(rc);复制代码ring_proto的定义为#if(LINUX_VERSION_CODE & KERNEL_VERSION(2,6,11))static struct proto ring_proto = {&&.name = "PF_RING",&&.owner = THIS_MODULE,&&.obj_size = sizeof(struct ring_sock),};#endif复制代码初始化四个链表,它们的作用,后文会分析到:& & & & //初始化四个链表&&INIT_LIST_HEAD(&ring_table);& & & & & & & & & & & & & & & & & & & & /* List of all ring sockets. */&&INIT_LIST_HEAD(&ring_cluster_list);& & & & & & & & & & & & /* List of all clusters */&&INIT_LIST_HEAD(&ring_aware_device_list);& & & & & & & & /* List of all devices on which PF_RING has been registered */&&INIT_LIST_HEAD(&ring_dna_devices_list);& & & & & & & & /* List of all dna (direct nic access) devices */复制代码device_ring_list是一个指针数组,它的每一个元素对应一个网络设备,后文也会分析它的使用:/*&&For each device, pf_ring keeps a list of the number of&&available ring socket slots. So that a caller knows in advance whether&&there are slots available (for rings bound to such device)&&that can potentially host the packet*/&&for (i = 0; i & MAX_NUM_DEVICES; i++)& & INIT_LIST_HEAD(&device_ring_list[i]);复制代码& & & & //为新协议注册sock&&sock_register(&ring_family_ops);复制代码ring_family_ops定义为static struct net_proto_family ring_family_ops = {&&.family = PF_RING,&&.create = ring_create,&&.owner = THIS_MODULE,};复制代码这样,当用户空间创建PF_RING时,例如,fd = socket(PF_RING, SOCK_RAW, htons(ETH_P_ALL));复制代码ring_create将会被调用&&//注册通知链表&&&&register_netdevice_notifier(&ring_netdev_notifier);&&/* 工作模式语法检查 */&&if(transparent_mode & driver2pf_ring_non_transparent)& & transparent_mode = standard_linux_复制代码PF_RING一共有三种工作模式:typedef enum {&&standard_linux_path = 0, /* Business as usual */&&driver2pf_ring_transparent = 1, /* Packets are still delivered to the kernel */&&driver2pf_ring_non_transparent = 2 /* Packets not delivered to the kernel */} direct2pf_复制代码第一种最简单,这里仅分析第一种& & & & //输出工作信息参数&&printk("[PF_RING] Ring slots& && & %d\n", num_slots);&&printk("[PF_RING] Slot version& &&&%d\n",& & & &&&RING_FLOWSLOT_VERSION);&&printk("[PF_RING] Capture TX& && & %s\n",& & & &&&enable_tx_capture ? "Yes [RX+TX]" : "No [RX only]");&&printk("[PF_RING] Transparent Mode %d\n",& & & &&&transparent_mode);&&printk("[PF_RING] IP Defragment& & %s\n",& & & &&&enable_ip_defrag ? "Yes" : "No");&&printk("[PF_RING] Initialized correctly\n");复制代码num_slots为槽位总数,系统采用数组来实现双向环形队列,它也就代表数组的最大元素。版本信息:不用多说了。不过我的版本在2.6.18及以下都没有编译成功,后来使用2.6.30.9搞定之。enable_tx_capture:是否启用发送时的数据捕获,对于大多数应用而言,都是在接收时处理。enable_ip_defrag:为用户提供一个接口,是否在捕获最重组IP分片。& & & & //创建/proc目录&&ring_proc_init();&&&&//注册设备句柄&&register_device_handler();&&pfring_enabled = 1;& & & & & & & & //工作标志&&return 0;}复制代码register_device_handler注册了一个协议,用于数据包的获取:/* Protocol hook */static struct packet_type prot_void register_device_handler(void) {& & & & //只有在第一种模式下,才用这种方式接收数据&&if(transparent_mode != standard_linux_path)&&prot_hook.func = packet_&&prot_hook.type = htons(ETH_P_ALL);&&dev_add_pack(&prot_hook);}void register_device_handler(void) {&&if(transparent_mode != standard_linux_path)&&prot_hook.func = packet_&&prot_hook.type = htons(ETH_P_ALL);&&dev_add_pack(&prot_hook);}复制代码2、创建套接字Linux的套按字的内核接口,使用了两个重要的数据结构:struct socket和struct sock,这本来并没有什么,不过令人常常迷惑的是,前者常常被缩写为sock,即:struct socket *这样,“sock”就容易造成混淆了。还好,后者常常被缩写为sk……我这里写sock指前者,sk指后者,如果不小心写混了,请参考上下文区分&。关于这两个结构的含义,使用等等,可以参考相关资料以获取详细信息,如《Linux情景分析》。我的个人网站上也分析了Linux socket的实现。可以参考。这里关于socket的进一步信息,就不详细分析了。这里的创建套接字,内核已经在系统调用过程中,准备好了sock,主要就是分析sk,并为sk指定一系列的操作函数,如bind、mmap、poll等等。如前所述,套接字的创建,是通过调用ring_create函数来完成的:static int ring_create(#if(LINUX_VERSION_CODE &= KERNEL_VERSION(2,6,24))& & & & & & & && && &&&struct net *net,#endif& & & & & & & && && &&&struct socket *sock, int protocol){&&struct sock *&&struct ring_opt *&&#if defined(RING_DEBUG)&&printk("[PF_RING] ring_create()\n");#endif&&/* 权限验证 ? */&&if(!capable(CAP_NET_ADMIN))& & return -EPERM;&&//协议簇验证&&if(sock-&type != SOCK_RAW)& & return -ESOCKTNOSUPPORT;&&//协议验证&&if(protocol != htons(ETH_P_ALL))& & return -EPROTONOSUPPORT;&&err = -ENOMEM;&&// 分配sk&&// options are.#if(LINUX_VERSION_CODE &= KERNEL_VERSION(2,6,11))&&sk = sk_alloc(PF_RING, GFP_KERNEL, 1, NULL);#else#if(LINUX_VERSION_CODE & KERNEL_VERSION(2,6,24))& & // BD: API changed in 2.6.12, ref:& & // [url]http://svn.clkao.org/svnweb/linux/revision/?rev=28201[/url]& & sk = sk_alloc(PF_RING, GFP_ATOMIC, &ring_proto, 1);#else& & sk = sk_alloc(net, PF_INET, GFP_KERNEL, &ring_proto);#endif#endif&&//分配失败&&if(sk == NULL)& &&&&&//这里很重要,设定sock的ops,即对应用户态的bind、connect诸如此类操作的动作&&sock-&ops = &ring_&&//初始化sock结构(即sk)各成员,并设定与套接字socket(即sock)的关联&&sock_init_data(sock, sk);#if(LINUX_VERSION_CODE &= KERNEL_VERSION(2,6,11))&&sk_set_owner(sk, THIS_MODULE);#endif&&err = -ENOMEM;#define ring_sk_datatype(__sk) ((struct ring_opt *)__sk)#define ring_sk(__sk) ((__sk)-&sk_protinfo)//作者喜欢用小写的宏//这里分配一个struct ring_opt结构,这个结构比较重要,其记录了ring的选项信息。在sk中,使用sk_protinfo成员指向之,这样就建立了sock-&sk-&ring_opt的关联。可以通过套接字很容易获取Ring的信息。&&ring_sk(sk) = ring_sk_datatype(kmalloc(sizeof(*pfr), GFP_KERNEL));&&//分配失败&&if(!(pfr = ring_sk(sk))) {& & sk_free(sk);& &&&}&&//初始化各成员&&memset(pfr, 0, sizeof(*pfr));//激活标志&&pfr-&ring_active = 0;& & & & /* We activate as soon as somebody waits for packets */&&//通道ID&&pfr-&channel_id = RING_ANY_CHANNEL;&&//RING的每个槽位的桶的大小,其用来存储捕获的数据帧,这个值,用户态也可以使用setsocketopt来调整&&pfr-&bucket_len = DEFAULT_BUCKET_LEN;//过滤器hash桶&&pfr-&handle_hash_rule = handle_filtering_hash_&&//初始化等待队列&&init_waitqueue_head(&pfr-&ring_slots_waitqueue);&&//初始化RING的锁&&rwlock_init(&pfr-&ring_index_lock);&&rwlock_init(&pfr-&ring_rules_lock);&&//初始化使用计数器&&atomic_set(&pfr-&num_ring_users, 0);&&INIT_LIST_HEAD(&pfr-&rules);&&//设定协议簇&&sk-&sk_family = PF_RING;//设定sk的destuct函数&&sk-&sk_destruct = ring_sock_&&//sk入队&&ring_insert(sk);#if defined(RING_DEBUG)&&printk("[PF_RING] ring_create() - created\n");#endif&&return(0);out:&&复制代码在模块初始化中,初始化过四个链表。其中一个是ring_table,ring_insert将刚刚创建的套接字插入其中。其封装引进了一个struct ring_element 结构:/** ring_insert()** store the sk in a new element and add it* to the head of the list.*/static inline void ring_insert(struct sock *sk){&&struct ring_element *&&struct ring_opt *#if defined(RING_DEBUG)&&printk("[PF_RING] ring_insert()\n");#endif//分配element&&next = kmalloc(sizeof(struct ring_element), GFP_ATOMIC);&&if(next != NULL) {& & //记录sk& & next-&sk =& & write_lock_bh(&ring_mgmt_lock);& & //入队& & list_add(&next-&list, &ring_table);& & write_unlock_bh(&ring_mgmt_lock);&&} else {& & if(net_ratelimit())& && &printk("[PF_RING] net_ratelimit() failure\n");&&}&&//累计使用计数器&&ring_table_size++;&&//ring_proc_add(ring_sk(sk));&&//记录进程PID&&pfr = (struct ring_opt *)ring_sk(sk);&&pfr-&ring_pid = current-&}复制代码3 、分配队列空间用户态在创建了套接字后,接下来就调用bind函数,绑定套接字,而PF_RING实际做的就是为RING分配相应的空间。也就是说,一个套接字,都有一个与之对应的RING。这样,有多个进程同时使用PF_RING,也没有问题:& & & & & & & & sa.sa_family& &= PF_RING;& & & & & & & & snprintf(sa.sa_data, sizeof(sa.sa_data), "%s", device_name);& & & & & & & & rc = bind(ring-&fd, (struct sockaddr *)&sa, sizeof(sa));复制代码因为前一步创建套接字时,为sk指定了其ops:static struct proto_ops ring_ops = {&&.family = PF_RING,&&.owner = THIS_MODULE,&&/* Operations that make no sense on ring sockets. */&&.connect = sock_no_connect,&&.socketpair = sock_no_socketpair,&&.accept = sock_no_accept,&&.getname = sock_no_getname,&&.listen = sock_no_listen,&&.shutdown = sock_no_shutdown,&&.sendpage = sock_no_sendpage,&&.sendmsg = sock_no_sendmsg,&&/* Now the operations that really occur. */&&.release = ring_release,&&.bind = ring_bind,&&.mmap = ring_mmap,&&.poll = ring_poll,&&.setsockopt = ring_setsockopt,&&.getsockopt = ring_getsockopt,&&.ioctl = ring_ioctl,&&.recvmsg = ring_recvmsg,};复制代码这样,当bing系统调用触发时,ring_bind函数将被调用:* Bind to a device */static int ring_bind(struct socket *sock, struct sockaddr *sa, int addr_len){&&struct sock *sk = sock-&&&struct net_device *dev = NULL;#if defined(RING_DEBUG)&&printk("[PF_RING] ring_bind() called\n");#endif&&/*& &*& && &Check legality& &*/&&if(addr_len != sizeof(struct sockaddr))& & return -EINVAL;&&if(sa-&sa_family != PF_RING)& & return -EINVAL;&&if(sa-&sa_data == NULL)& & return -EINVAL;&&/* Safety check: add trailing zero if missing */&&sa-&sa_data[sizeof(sa-&sa_data) - 1] = '\0';#if defined(RING_DEBUG)&&printk("[PF_RING] searching device %s\n", sa-&sa_data);#endif&&if((dev = __dev_get_by_name(#if(LINUX_VERSION_CODE &= KERNEL_VERSION(2,6,24))& & & & & & & & & & & && && &&&&init_net,#endif& & & & & & & & & & & && && &&&sa-&sa_data)) == NULL) {#if defined(RING_DEBUG)& & printk("[PF_RING] search failed\n");#endif& & return(-EINVAL);&&} else& & return(packet_ring_bind(sk, dev));}复制代码在做了一些必要的语法检查后,函数转向packet_ring_bind:/** We create a ring for this socket and bind it to the specified device*/static int packet_ring_bind(struct sock *sk, struct net_device *dev){&&u_int the_slot_&&u_int32_t tot_&&struct ring_opt *pfr = ring_sk(sk);&&// struct page *page, *page_&&if(!dev)& & return(-1);#if defined(RING_DEBUG)&&printk("[PF_RING] packet_ring_bind(%s) called\n", dev-&name);#endif&&/* **********************************************& &* *************************************& &* *& && && && && && && && && && && &&&*& &* *& && &&&FlowSlotInfo& && && && && &*& &* *& && && && && && && && && && && &&&*& &* ************************************* &-+& &* *& && &&&FlowSlot& && && && && && & *& &|& &* *************************************& &|& &* *& && &&&FlowSlot& && && && && && & *& &|& &* *************************************& &+- num_slots& &* *& && &&&FlowSlot& && && && && && & *& &|& &* *************************************& &|& &* *& && &&&FlowSlot& && && && && && & *& &|& &* ************************************* &-+& &*& &* ********************************************** */& & & & //计算每一个槽位所需的内存空间&&the_slot_len = sizeof(u_char)& & & & /* flowSlot.slot_state */#ifdef RING_MAGIC& & + sizeof(u_char)#endif& & + sizeof(struct pfring_pkthdr)& & + pfr-&bucket_len /* flowSlot.bucket */ ;/*& &&& & & & 对于槽位空间的计算,有意思的是typedef struct flowSlot {#ifdef RING_MAGIC&&u_char& &&&& && &/* It must alwasy be zero */#endif&&u_char& &&&slot_ /* 0=empty, 1=full& &*/&&u_char& &&&& &&&/* bucket[bucketLen] */} FlowS对照结构定义和上面的计算公式:1、作者好像把magic和slog_state的顺序给搞反了,不过还好,它们都是u_char,对结果不影响2、bucket,桶的大小,这个桶就是拿来装要捕获的数据包了,虽然它在结构中定义是一个成员,事实上,它由两个部份组成,一个是包的首部信息,这个结构的定义同libpcap很接近。另一个才是包的空间。*/& & & & //总共的环形队列内存所需空间,包含一个队列控制信息FlowSlotInfo和若干个(由变量num_slots决定)槽位空间&&tot_mem = sizeof(FlowSlotInfo) + num_slots * the_slot_&&&&//确保按整数页分配,mmap也要求这样&&if(tot_mem % PAGE_SIZE)& & tot_mem += PAGE_SIZE - (tot_mem % PAGE_SIZE);& & & & //分配内存空间&&pfr-&ring_memory = rvmalloc(tot_mem);&&if(pfr-&ring_memory != NULL) {& & printk("[PF_RING] successfully allocated %lu bytes at 0x%08lx\n",& & & && & (unsigned long)tot_mem, (unsigned long)pfr-&ring_memory);&&} else {& & printk("[PF_RING] ERROR: not enough memory for ring\n");& & return(-1);&&}&&// memset(pfr-&ring_memory, 0, tot_mem); // rvmalloc does the memset already&&//初始化各成员& & & & //内存指定,因为分配的内存开始部份是sizeof(FlowSlotInfo),所以可以做这样的强制转换,很容易互相取值&&pfr-&slots_info = (FlowSlotInfo *) pfr-&ring_&&//跳过控制信息,指向槽位指针.事实上,它就是一个一维数组了,可以计算出合适的索引值,取到数组(RING)中的任意槽位值&&pfr-&ring_slots = (char *)(pfr-&ring_memory + sizeof(FlowSlotInfo));&&//版本信息&&pfr-&slots_info-&version = RING_FLOWSLOT_VERSION;&&//登记单个槽的大小&&pfr-&slots_info-&slot_len = the_slot_&&//登记bucket大小,从前面特别注释的bucket的分配看,bucket_len这个大小并不代表bucket成员的实际大小——它不包含struct pfring_pkthdr&&pfr-&slots_info-&data_len = pfr-&bucket_&&//登记实际分配到的槽位数量,这里不用num_slots,难道是怕rvmalloc偷吃?&&pfr-&slots_info-&tot_slots =& & (tot_mem - sizeof(FlowSlotInfo)) / the_slot_&&//登记实际分配的内存总数&&&&pfr-&slots_info-&tot_mem = tot_&&//采样速率??&&pfr-&slots_info-&sample_rate = 1;&&printk("[PF_RING] allocated %d slots [slot_len=%d][tot_mem=%u]\n",& & & &&&pfr-&slots_info-&tot_slots, pfr-&slots_info-&slot_len,& & & &&&pfr-&slots_info-&tot_mem);#ifdef RING_MAGIC&&{& && & for (i = 0; i & pfr-&slots_info-&tot_ i++) {& && &unsigned long idx = i * pfr-&slots_info-&slot_& && &FlowSlot *slot = (FlowSlot *) & pfr-&ring_slots[idx];& && &slot-&magic = RING_MAGIC_VALUE;& && &slot-&slot_state = 0;& & }&&}#endif&&//这些控制变量可以在环的入队操作中看到它们的作用&&pfr-&sample_rate = 1;& & & & /* No sampling */&&pfr-&insert_page_id = 1, pfr-&insert_slot_id = 0;&&pfr-&rules_default_accept_policy = 1, pfr-&num_filtering_rules = 0;&&ring_proc_add(ring_sk(sk), dev);&&//记录与之相应的设备信息,例如,如果在eth0上打开了5 个PF_RING, bind&&//被调用5次,分配了5个环形队列空间。eth0上随之分配5个elem,它们指向与&&//之对应的ring,然后根据设备索引号民全部加入至了device_ring_list&&//当有数据报文从指定接口进入时,可以很容易地在device_ring_list中找到相应的设备&&//然后再遍历链表,再找到与之相应的ring&&if(dev-&ifindex & MAX_NUM_DEVICES) {& & device_ring_list_element *& & /* printk("[PF_RING] Adding ring to device index %d\n", dev-&ifindex); */& & elem = kmalloc(sizeof(device_ring_list_element), GFP_ATOMIC);& & if(elem != NULL) {& && &elem-&the_ring =& && &INIT_LIST_HEAD(&elem-&list);& && &write_lock(&ring_list_lock);& && &list_add(&elem-&list, &device_ring_list[dev-&ifindex]);& && &write_unlock(&ring_list_lock);& && &/* printk("[PF_RING] Added ring to device index %d\n", dev-&ifindex); */& & }&&}&&/*& & IMPORTANT& & Leave this statement here as last one. In fact when& & the ring_netdev != NULL the socket is ready to be used.&&*/&&pfr-&ring_netdev =&&return(0);}复制代码这个函数中,最重要的三点:1、整个空间的详细构成,作者画了一个简单的草图,清晰明了。2、如果取得某个槽位。3、device_ring_list链表的使用。一些作者有详细注释的地方,我就不再重重了。这一步进行完了后,就有一块内存了(系统将其看成一个数组),用来存储捕获的数据帧。接下来要做的事情。就是把它映射到用户态。4、mmap操作用户态的接下来调用:& & & & & & & & & & & & ring-&buffer = (char *)mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE,& & & & & & & & & & & & & & & & MAP_SHARED, ring-&fd, 0);复制代码进行内存映射。同样地,内核调用相应的ring_mmap进行处理。Ring选项结构通过ring_sk宏与sk 建立关联struct ring_opt *pfr = ring_sk(sk);复制代码pfr-&ring_memory 即为分配的环形队列空间。所以,要mmap操作,实际上就是调用remap_pfn_range函数把pfr-&ring_memory 映射到用户空间即可。这个函数的原型为:/*** remap_pfn_range - remap kernel memory to userspace* @vma: user vma to map to* @addr: target user address to start at* @pfn: physical address of kernel memory* @size: size of map area* @prot: page protection flags for this mapping**&&Note: this is only safe if the mm semaphore is held when called.*/int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,& & & & & & & && &&&unsigned long pfn, unsigned long size, pgprot_t prot){复制代码关于remap_pfn_range函数的进一步说明,可以参考LDD3,上面有详细说明和现成的例子。static int ring_mmap(struct file *file,& & & & & & & && && &struct socket *sock, struct vm_area_struct *vma){&&struct sock *sk = sock-&&&struct ring_opt *pfr = ring_sk(sk);& & & & & & & & //取得pfr指针,也就是相应取得环形队列的内存空间地址指针&&&&unsigned long size = (unsigned long)(vma-&vm_end - vma-&vm_start);&&if(size % PAGE_SIZE) {#if defined(RING_DEBUG)& & printk("[PF_RING] ring_mmap() failed: "& & & && & "len is not multiple of PAGE_SIZE\n");#endif& & return(-EINVAL);&&}#if defined(RING_DEBUG)&&printk("[PF_RING] ring_mmap() called, size: %ld bytes\n", size);#endif&&if((pfr-&dna_device == NULL) && (pfr-&ring_memory == NULL)) {#if defined(RING_DEBUG)& & printk("[PF_RING] ring_mmap() failed: "& & & && & "mapping area to an unbound socket\n");#endif& & return -EINVAL;&&}&&//dns设备为空,即没有使用dns技术&&if(pfr-&dna_device == NULL) {& & /* if userspace tries to mmap beyond end of our buffer, fail */& & //映射空间超限& & if(size & pfr-&slots_info-&tot_mem) {#if defined(RING_DEBUG)& && &printk("[PF_RING] ring_mmap() failed: "& & & && && &"area too large [%ld & %d]\n",& & & && && &size, pfr-&slots_info-&tot_mem);#endif& && &return(-EINVAL);& & }#if defined(RING_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-&tot_slots,& & & && & pfr-&ring_netdev-&name);#endif& & & & //进行内存映射& & if((rc =& & & &&&do_memory_mmap(vma, size, pfr-&ring_memory, VM_LOCKED,& & & & & & & & & & & & 0)) & 0)& && &return(rc);&&} else {& & /* DNA Device */& & if(pfr-&dna_device == NULL)& && &return(-EAGAIN);& & switch (pfr-&mmap_count) {& & case 0:& && &if((rc = do_memory_mmap(vma, size,& & & & & & & & & & & && && &&&(void *)pfr-&dna_device-&& & & & & & & & & & & && && &&&packet_memory, VM_LOCKED,& & & & & & & & & & & && && &&&1)) & 0)& & & & return(rc);& && && & case 1:& && &if((rc = do_memory_mmap(vma, size,& & & & & & & & & & & && && &&&(void *)pfr-&dna_device-&& & & & & & & & & & & && && &&&descr_packet_memory, VM_LOCKED,& & & & & & & & & & & && && &&&1)) & 0)& & & & return(rc);& && && & case 2:& && &if((rc = do_memory_mmap(vma, size,& & & & & & & & & & & && && &&&(void *)pfr-&dna_device-&& & & & & & & & & & & && && &&&phys_card_memory,& & & & & & & & & & & && && &&&(VM_RESERVED | VM_IO), 2)) & 0)& & & & return(rc);& && && & default:& && &return(-EAGAIN);& & }& & pfr-&mmap_count++;&&}#if defined(RING_DEBUG)&&printk("[PF_RING] ring_mmap succeeded\n");#endif&&return 0;}复制代码实际上的内存映射工作,是由do_memory_mmap来完成的,这个函数实际上基本就是remap_pfn_range的包裹函数。不过因为系统支持dna等技术,相应的mode参数有些变化,这里只分析了最基本的方法:mode == 0static int do_memory_mmap(struct vm_area_struct *vma,& & & & & & & & & & & && &unsigned long size, char *ptr, u_int flags, int mode){&&&&&&/* we do not want to have this area swapped out, lock it */&&vma-&vm_flags |=&&start = vma-&vm_&&while (size & 0) {& && & if(mode == 0) {#if(LINUX_VERSION_CODE &= KERNEL_VERSION(2,6,11))& & & && &//根据地址,计算要映射的页帧& && &page = vmalloc_to_pfn(ptr);& && &//进行内存映射& && &rc = remap_pfn_range(vma, start, page, PAGE_SIZE,& & & & & & & & & & & && & PAGE_SHARED);#else& && &page = vmalloc_to_page(ptr);& && &page = kvirt_to_pa(ptr);& && &rc = remap_page_range(vma, start, page, PAGE_SIZE,& & & & & & & & & & & && &&&PAGE_SHARED);#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);& & }& & if(rc) {#if defined(RING_DEBUG)& && &printk("[PF_RING] remap_pfn_range() failed\n");#endif& && &return(-EAGAIN);& & }& & start += PAGE_SIZE;& & ptr += PAGE_SIZE;& & if(size & PAGE_SIZE) {& && &size -= PAGE_SIZE;& & } else {& && &size = 0;& & }&&}&&return(0);}复制代码嗯,跳过了太多的细节,不过其mmap最核心的东东已经呈现出来。如果要共享内核与用户空间内存,这倒是个现成的可借鉴的例子。5、数据包的入队操作做到这一步,准备工作基本上就完成了。因为PF_RING在初始化中,注册了prot_hook。其func指针指向packet_rcv函数:当数据报文进入Linux网络协议栈队列时,netif_receive_skb会遍历这些注册的Hook:int netif_receive_skb(struct sk_buff *skb){& & & & list_for_each_entry_rcu(ptype, &ptype_all, list) {& & & & & & & & if (ptype-&dev == null_or_orig || ptype-&dev == skb-&dev ||& & & & & & & && &&&ptype-&dev == orig_dev) {& & & & & & & & & & & & if (pt_prev)& & & & & & & & & & & & & & & & ret = deliver_skb(skb, pt_prev, orig_dev);& & & & & & & & & & & & pt_prev =& & & & & & & & }& & & & }}复制代码相应的Hook函数得到调用:static inline int deliver_skb(struct sk_buff *skb,& & & & & & & & & & & && && & struct packet_type *pt_prev,& & & & & & & & & & & && && & struct net_device *orig_dev){& & & & atomic_inc(&skb-&users);& & & & //注意,这里引用计数器被增加了& & & & return pt_prev-&func(skb, skb-&dev, pt_prev, orig_dev);}复制代码packet_rcv随之执行环形队列的入队操作:static int packet_rcv(struct sk_buff *skb, struct net_device *dev,& & & & & & & && && & struct packet_type *pt, struct net_device *orig_dev){&&&&//忽略本地环回报文&&if(skb-&pkt_type != PACKET_LOOPBACK) {&&& & & & //进一步转向,最后一个参数直接使用-1,从上下文来看,写为RING_ANY_CHANNEL(其实也是-1)似乎可读性更强,&&& & & & //这里表示,如果从packet_rcv进入队列,由通道ID是“未指定的”,由skb_ring_handler来处理& & rc = skb_ring_handler(skb,& & & & & & & & & & & && &(skb-&pkt_type == PACKET_OUTGOING) ? 0 : 1,& & & & & & & & & & & && &1, -1 /* unknown channel */);&&} else& & rc = 0;&&kfree_skb(skb);& & & & & & & & & & & & & & & & //所以,这里要做相应的减少&&return(rc);}复制代码static int skb_ring_handler(struct sk_buff *skb,& & & & & & & & & & & & & & & & //要捕获的数据包& & & & & & & & & & & && &&&u_char recv_packet,& & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & //数据流方向,&0表示是进入(接收)方向& & & & & & & & & & & && &&&u_char real_skb /* 1=real skb, 0=faked skb */ ,& & & & & & & & & & & && &&&short channel_id)& & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & //通道ID{&&struct sock *skE&&int rc = 0, is_ip_&&struct list_head *&&struct pfring_&&&&struct sk_buff *skk = NULL;&&struct sk_buff *orig_skb =#ifdef PROFILING&&uint64_t rdt = _rdtsc(), rdt1, rdt2;#endif&&//skb合法检查,包括数据流的方向&&if((!skb)& & & & & & & & /* Invalid skb */& && &||((!enable_tx_capture) && (!recv_packet))) {& & /*& && &An outgoing packet is about to be sent out& && &but we decided not to handle transmitted& && &packets.& & */& & return(0);&&}#if defined(RING_DEBUG)&&if(1) {& && & skb_get_timestamp(skb, &tv);& & printk& && &("[PF_RING] skb_ring_handler() [skb=%p][%u.%u][len=%d][dev=%s][csum=%u]\n",& && & skb, (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec,& && & skb-&len,& && & skb-&dev-&name == NULL ? "&NULL&" : skb-&dev-&name,& && & skb-&csum);&&}#endif& & & & //如果通道ID未指定,根据进入的报文设备索引,设定之#if(LINUX_VERSION_CODE &= KERNEL_VERSION(2,6,21))&&if(channel_id == RING_ANY_CHANNEL /* Unknown channel */ )& & channel_id = skb-&& & & & /* Might have been set by the driver */#endif#if defined (RING_DEBUG)&&/* printk("[PF_RING] channel_id=%d\n", channel_id); */#endif#ifdef PROFILING&&rdt1 = _rdtsc();#endif&&if(recv_packet) {& & /* Hack for identifying a packet received by the e1000 */& & if(real_skb)& && &displ = SKB_DISPLACEMENT;& & else& && &displ = 0;& & & & /* Received by the e1000 wrapper */&&} else& & displ = 0;& & & &&&&//解析数据报文,并判断是否为IP报文&&is_ip_pkt = parse_pkt(skb, displ, &hdr);&&//分片处理,是一个可选的功能项,事实上,对大多数包捕获工具而言,它们好像都不使用底层库来完成这一功能&&/* (de)Fragmentation && */&&if(enable_ip_defrag& && &&& real_skb && is_ip_pkt && recv_packet && (ring_table_size & 0)) {& & } else {#if defined (RING_DEBUG)& & & & printk("[PF_RING] Do not seems to be a fragmented ip_pkt[iphdr=%p]\n",& & & && && &&&iphdr);#endif& && &}& & }&&}&&//按惯例,在报文的捕获首部信息中记录捕获的时间戳&&/* BD - API changed for time keeping */#if(LINUX_VERSION_CODE & KERNEL_VERSION(2,6,14))&&if(skb-&stamp.tv_sec == 0)& & do_gettimeofday(&skb-&stamp);&&hdr.ts.tv_sec = skb-&stamp.tv_sec, hdr.ts.tv_usec = skb-&stamp.tv_#elif(LINUX_VERSION_CODE & KERNEL_VERSION(2,6,22))&&if(skb-&tstamp.off_sec == 0)& & __net_timestamp(skb);&&hdr.ts.tv_sec = skb-&tstamp.off_sec, hdr.ts.tv_usec =& & skb-&tstamp.off_#else /* 2.6.22 and above */&&if(skb-&tstamp.tv64 == 0)& & __net_timestamp(skb);&&hdr.ts = ktime_to_timeval(skb-&tstamp);#endif&&//除了时间,还有长度,熟悉libpcap的话,这些操作应该很眼熟&&hdr.len = hdr.caplen = skb-&len +&&/* Avoid the ring to be manipulated while playing with it */&&read_lock_bh(&ring_mgmt_lock);&&/* 前面在创建sk时,已经看过ring_insert的入队操作了,现在要检查它的成员&&* 它们的关系是,通过ring_table的成员,获取到element,它里面封装了sk,&&*通过ring_sk宏,就可以得到ring_opt指针& &*/&&list_for_each(ptr, &ring_table) {& & struct ring_opt *& & struct ring_element *& & entry = list_entry(ptr, struct ring_element, list);& & skElement = entry-&& & pfr = ring_sk(skElement);& & & &&& & & & //看来要加入社团,条件还是满多的,pfr不能为空,未指定集群cluster_id,槽位不能为空,方向要正确,绑定的网络设备& & & & //得对上号& & & & //另一种可能就是对bonding的支持,如果设备是从属设备,则应校验其主设备& & if((pfr != NULL)& & & & && (pfr-&cluster_id == 0 /* No cluster */ )& & & & && (pfr-&ring_slots != NULL)& & & & && is_valid_skb_direction(pfr-&direction, recv_packet)& & & & && ((pfr-&ring_netdev == skb-&dev)& & & && &&&|| ((skb-&dev-&flags & IFF_SLAVE)& & & & & & & & && (pfr-&ring_netdev == skb-&dev-&master)))) {& && &/* We've found the ring where the packet can be stored */& && &/* 从新计算捕获帧长度,是因为可能因为巨型帧的出现——超过了桶能容纳的长度 */& && &int old_caplen = hdr.& & & & /* Keep old lenght */& && &hdr.caplen = min(hdr.caplen, pfr-&bucket_len);& && &/* 入队操作 */& && &add_skb_to_ring(skb, pfr, &hdr, is_ip_pkt, displ, channel_id);& && &hdr.caplen = old_& && &rc = 1;& & & & /* Ring found: we've done our job */& & }&&}&&/* [2] Check socket clusters */&&list_for_each(ptr, &ring_cluster_list) {& & ring_cluster_element *cluster_& & struct ring_opt *& & cluster_ptr = list_entry(ptr, ring_cluster_element, list);& & if(cluster_ptr-&cluster.num_cluster_elements & 0) {& && &u_int skb_hash = hash_pkt_cluster(cluster_ptr, &hdr);& && &skElement = cluster_ptr-&cluster.sk[skb_hash];& && &if(skElement != NULL) {& & & & pfr = ring_sk(skElement);& & & & if((pfr != NULL)& & & && &&&&& (pfr-&ring_slots != NULL)& & & && &&&&& ((pfr-&ring_netdev == skb-&dev)& & & & & & & & || ((skb-&dev-&flags & IFF_SLAVE)& & & & & & & && &&&&& (pfr-&ring_netdev ==& & & & & & & & & & & & skb-&dev-&master)))& & & && &&&&& is_valid_skb_direction(pfr-&direction, recv_packet)& & & && &&&) {& & & && &/* We've found the ring where the packet can be stored */& & & && &add_skb_to_ring(skb, pfr, &hdr,& & & & & & & & & & & && &is_ip_pkt, displ,& & & & & & & & & & & && &channel_id);& & & && &rc = 1;& & & & /* Ring found: we've done our job */& & & & }& && &}& & }&&}&&read_unlock_bh(&ring_mgmt_lock);#ifdef PROFILING&&rdt1 = _rdtsc() - rdt1;#endif#ifdef PROFILING&&rdt2 = _rdtsc();#endif&&/* Fragment handling */&&if(skk != NULL)& & kfree_skb(skk);&&if(rc == 1) {& & if(transparent_mode != driver2pf_ring_non_transparent) {& && &rc = 0;& & } else {& && &if(recv_packet && real_skb) {#if defined(RING_DEBUG)& & & & printk("[PF_RING] kfree_skb()\n");#endif& & & & kfree_skb(orig_skb);& && &}& & }&&}#ifdef PROFILING&&rdt2 = _rdtsc() - rdt2;&&rdt = _rdtsc() -#if defined(RING_DEBUG)&&printk& & ("[PF_RING] # cycles: %d [lock costed %d %d%%][free costed %d %d%%]\n",& &&&(int)rdt, rdt - rdt1,& &&&(int)((float)((rdt - rdt1) * 100) / (float)rdt), rdt2,& &&&(int)((float)(rdt2 * 100) / (float)rdt));#endif#endif&&//printk("[PF_RING] Returned %d\n", rc);&&return(rc);& & & & & & & & /*&&0 = packet not handled */}上面跳过了对cluster(集群)的分析,PF_RING允许同时对多个接口捕获报文,而并不是一个。这就是集群。看一下它用户态的注释就一目了然了:& & & & & & & & & & & & /* Syntax& & & & & & & & & & & & ethX@1,5& && & channel 1 and 5& & & & & & & & & & & & ethX@1-5& && & channel 1,2...5& & & & & & & & & & & & ethX@1-3,5-7& &channel 1,2,3,5,6,7& & & & & & & & & & & & */复制代码进一步的入队操作,是通过add_skb_to_ring来完成的:static int add_skb_to_ring(struct sk_buff *skb,& & & & & & & & & & & && & struct ring_opt *pfr,& & & & & & & & & & & && & struct pfring_pkthdr *hdr,& & & & & & & & & & & && & int is_ip_pkt, int displ, short channel_id){& && &//add_skb_to_ring函数比较复杂,因为它要处理过滤器方面的问题。& && &//关于PF_RING的过滤器,可以参考[url]http://luca.ntop.org/Blooms.pdf[/url]& && &//获取更多内容。这里不做详细讨论了。或者留到下回分解吧。& && && && &//最终入队操作,是通过调用dd_pkt_to_ring来实现的。& && &add_pkt_to_ring(skb, pfr, hdr, displ, channel_id,& & & & & & & && && & offset, mem);& & & &&}复制代码static void add_pkt_to_ring(struct sk_buff *skb,& & & & & & & & & & & && &&&struct ring_opt *pfr,& & & & & & & & & & & && &&&struct pfring_pkthdr *hdr,& & & & & & & & & & & && &&&int displ, short channel_id,& & & & & & & & & & & && &&&int offset, void *plugin_mem){&&char *ring_&&&&FlowSlot *theS&&int32_t the_bit = 1 && channel_#if defined(RING_DEBUG)&&printk("[PF_RING] --& add_pkt_to_ring(len=%d) [pfr-&channel_id=%d][channel_id=%d]\n",& & & &&&hdr-&len, pfr-&channel_id, channel_id);#endif&&//检查激活标志&&if(!pfr-&ring_active)& &&&if((pfr-&channel_id != RING_ANY_CHANNEL)& && &&& (channel_id != RING_ANY_CHANNEL)& && &&& ((pfr-&channel_id & the_bit) != the_bit))& & /* Wrong channel */&&//写锁&&write_lock_bh(&pfr-&ring_index_lock);&&//获取前一次插入的位置索引&&idx = pfr-&slots_info-&insert_&&//调用get_insert_slot获取当前要捕获数据报文的合适的槽位&&//这里idx++后,指向了下一次插入的位置索引&&idx++, theSlot = get_insert_slot(pfr);&&//累计计数器&&pfr-&slots_info-&tot_pkts++;&&//没位子了,累计丢包计数器,返回之&&if((theSlot == NULL) || (theSlot-&slot_state != 0)) {& & /* No room left */& & pfr-&slots_info-&tot_lost++;& & write_unlock_bh(&pfr-&ring_index_lock);& &&&}&&//获取当前槽位的桶&&ring_bucket = &theSlot-&&&//支持插件??在最开始处记录插件信息??&&if((plugin_mem != NULL) && (offset & 0))& & memcpy(&ring_bucket[sizeof(struct pfring_pkthdr)], plugin_mem, offset);&&&&if(skb != NULL) {&&& & & & //重新计算捕获帧长度& & hdr-&caplen = min(pfr-&bucket_len - offset, hdr-&caplen);& & if(hdr-&caplen & 0) {#if defined(RING_DEBUG)& && &printk("[PF_RING] --& [caplen=%d][len=%d][displ=%d][parsed_header_len=%d][bucket_len=%d][sizeof=%d]\n",& & & &&&hdr-&caplen, hdr-&len, displ,& & & && && &hdr-&parsed_header_len, pfr-&bucket_len,& & & && && &sizeof(struct pfring_pkthdr));#endif& && &//拷贝捕获的数据报文,前面空了两个栏位:一个是pkthdr首部,一个是插件offset长度& && &//这里经过了一次数据拷贝,对于完美主义者,这并不是一个好的方法。但是PF_RING定位于一个& && &//通用的接口库,似乎只有这么做了。否则,追求“零拷贝”,为了避免这一次拷贝,只有逐个修改网卡驱动了。& && &skb_copy_bits(skb, -displ,& & & & & & & && &&&&ring_bucket[sizeof(struct pfring_pkthdr) + offset], hdr-&caplen);& & } else {& && &if(hdr-&parsed_header_len &= pfr-&bucket_len) {& & & & static u_char print_once = 0;& & & & if(!print_once) {& & & && &printk("[PF_RING] WARNING: the bucket len is [%d] shorter than the plugin parsed header [%d]\n",& & & && && &pfr-&bucket_len, hdr-&parsed_header_len);& & & && &print_once = 1;& & & & }& && &}& & }&&}&&//记录首部&&memcpy(ring_bucket, hdr, sizeof(struct pfring_pkthdr)); /* Copy extended packet header */&&//前面idx已经自加过了,判断是否队列已满,若满,归零,否则更新插入索引&&if(idx == pfr-&slots_info-&tot_slots)& & pfr-&slots_info-&insert_idx = 0;&&else& & pfr-&slots_info-&insert_idx =#if defined(RING_DEBUG)&&printk("[PF_RING] ==& insert_idx=%d\n", pfr-&slots_info-&insert_idx);#endif&&//累计插入计数器&&pfr-&slots_info-&tot_insert++;&&//槽位就绪标记,用户空间可以来取了&&theSlot-&slot_state = 1;&&write_unlock_bh(&pfr-&ring_index_lock);&&//有的时候会出现,用户空间取不到的情况,如队列为空。这样,用户空间调用poll等待数据。这里做相应的唤醒处理&&/* wakeup in case of poll() */&&if(waitqueue_active(&pfr-&ring_slots_waitqueue))& & wake_up_interruptible(&pfr-&ring_slots_waitqueue);}复制代码槽位的计算:在ring_bind函数中,分配空间后,使用ring_slots做为槽位指针。事实上,这里要计算槽位,就是通过索引号 * 槽位长度来得到:static inline FlowSlot *get_insert_slot(struct ring_opt *pfr){&&if(pfr-&ring_slots != NULL) {& & FlowSlot *slot =& && &(FlowSlot *) & (pfr-&& & & & & & & && && & ring_slots[pfr-&slots_info-&insert_idx *& & & & & & & & & & & & & & & &&&pfr-&slots_info-&slot_len]);#if defined(RING_DEBUG)& & printk& && &("[PF_RING] get_insert_slot(%d): returned slot [slot_state=%d]\n",& && & pfr-&slots_info-&insert_idx, slot-&slot_state);#endif& & return(slot);&&} else {#if defined(RING_DEBUG)& & printk("[PF_RING] get_insert_slot(%d): NULL slot\n",& & & && & pfr-&slots_info-&insert_idx);#endif& & return(NULL);&&}}复制代码整理中,未完,待续。。。。。。
阅读(8663)
阅读排行榜
评论排行榜}

我要回帖

更多关于 pf鞋子是什么品牌 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信