ptype_base和ptype_all理解netid_receive_skb()函数注解

合集下载

netif_receive_skb 例子

netif_receive_skb 例子

netif_receive_skb 例子摘要:if_receive_skb 函数概述if_receive_skb 函数的输入参数if_receive_skb 函数的工作原理if_receive_skb 函数的应用示例5.总结正文:一、netif_receive_skb 函数概述etif_receive_skb 是Linux 内核中的一个函数,主要用于接收网络数据包。

该函数是netif_start_queue 函数的回调函数,当有新的数据包到达时,会被调用以处理接收到的数据包。

二、netif_receive_skb 函数的输入参数etif_receive_skb 函数接收的数据包由skb(sockbuf)结构体表示,该结构体包含了数据包的所有信息,如源地址、目标地址、数据长度等。

此外,netif_receive_skb 函数还需要接收一个netdev(网络设备)结构体作为参数,以确定数据包的接收设备。

三、netif_receive_skb 函数的工作原理etif_receive_skb 函数首先会检查接收到的数据包是否为错误包,如果是,则将其丢弃。

接下来,函数会检查数据包是否为广播包或组播包,如果是,则将其发送给所有与该网络设备关联的CPU。

如果数据包既不是错误包,也不是广播包或组播包,那么函数会将其放入设备的接收队列中,并通知协议栈进行进一步处理。

四、netif_receive_skb 函数的应用示例以下是一个netif_receive_skb 函数的应用示例:```c#include <linux/netdevice.h>#include <linux/skbuff.h>static int my_netif_receive_skb(struct sk_buff *skb){// 处理数据包printk(KERN_INFO "Received packet: %s", skb->dev->name);dev_kfree_skb(skb);return NETDEV_OK;}static struct net_device *my_netdev;static int __init my_init(void){my_netdev = alloc_etherdev(0);if (!my_netdev) {printk(KERN_ALERT "Failed to allocate network device ");return -ENOMEM;}my_netdev->ndo = &my_netdev_priv;INIT_LIST_HEAD(&my_netdev->list);my_netdev->name = "my_network_device";my_netdev->start_queue = my_netif_receive_skb;my_netdev->stop_queue = my_netif_receive_skb;if (register_netdev(my_netdev) < 0) {printk(KERN_ALERT "Failed to register network device ");free_etherdev(my_netdev);return -EIO;}printk(KERN_INFO "Network device registered");return 0;}static void __exit my_exit(void){unregister_netdev(my_netdev);free_etherdev(my_netdev);printk(KERN_INFO "Network device unregistered");}module_init(my_init);module_exit(my_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Your Name");MODULE_DESCRIPTION("Example of netif_receive_skb function");```五、总结etif_receive_skb 函数是Linux 内核中处理网络数据包的重要函数,通过该函数,我们可以实现自定义的数据包接收处理逻辑。

plink参数范文

plink参数范文

plink参数范文以下是一些常用的plink参数和它们的详细描述:1. -v: 启用详细的输出模式。

使用此参数,plink将提供更多的调试信息,以帮助诊断连接问题。

2. -ssh: 使用SSH协议进行远程连接。

这是plink的默认协议,它提供了安全的通信通道,并加密了所有传输的数据。

3. -telnet: 使用Telnet协议进行远程连接。

尽管Telnet不像SSH 那样安全,但有时它可能是唯一可用的协议。

使用此参数时,连接将以明文形式进行。

4. -P <port>: 指定连接的远程端口号。

默认端口号是22(SSH)或23(Telnet)。

使用此参数可以连接到非标准端口的服务器。

5. -l <username>: 指定要连接的远程主机的用户名。

plink将使用提供的用户名进行身份验证。

6. -pw <password>: 指定要连接的远程主机的登录密码。

使用此参数时,plink将自动将密码发送给远程主机进行身份验证。

7. -i <private_key>: 指定要使用的私钥文件。

如果服务器使用公钥身份验证,plink将使用此参数指定的私钥进行身份验证。

8. -batch: 在批处理模式下运行plink。

使用此参数,plink将不会提示用户输入任何信息,而是使用默认设置运行。

9. -m <script_file>: 指定一个脚本文件以执行一系列命令。

在文件中列出的命令将按顺序在远程主机上执行。

10.-C:启用压缩功能,以减少在网络上传输的数据量。

这对于慢速网络连接特别有用。

11. -X <server_list>: 指定代理服务器,将连接传递给其中一个服务器。

这在需要通过多个中转服务器进行连接时很有用。

12. -L <local_port>:<destination_host>:<destination_port>: 创建本地端口转发,将远程主机上的数据转发到本地机器上。

DA14580学习总结

DA14580学习总结

DA14580学习汇总简介芯片名称:DA14580内核:Cortex-M0 32-bit系统时钟:16MHZ 睡眠时钟:32K (所以要外挂两个晶振)协议栈:不开源,采用Riviera Waves授权协议栈IPRam:42 kB System SRAM(存放运行数据)8 kB RetentionSRAM(低漏电存储器,暂存休眠状态下的运行数据)存储方式:32k的OTP(一次性烧录),要实现反复烧录则需要外挂一个flash或者EEPROM 84 kBROM(存放协议栈)最小系统只需7个元件支持仿真烧录方式:串口烧录(JTAG也可以烧录,烧录到外挂的芯片中)封装:34 pins,40pins, 48 pins功耗:首款突破4mA无线收发电流极限的蓝牙智能解决方案(小米手环可满足30天续航)术语Profile:配置文件(在GATT的基础上进行数据的本地处理)GATT:Generic Attribute Profile 通用的配置文件(负责基础的数据通信)DISS:设备信息服务(显示设备的制造商信息)UUID:全球唯一识别码,如0x2A45位设备序列号的UUID(任意蓝牙都可以通过他获取到设备序列号)。

AES:Advanced Encryption Standard 是DA14580中内置的128 位加密处理器development_guide:开发手册GAP:Generic Access Profile 通用接口配置。

跟蓝牙的advertising相关GTL: Generic Transport Layer 通用传输层。

当工作于外部主控模式时,用来传输主控到DA14580的数据NVDS: Non-Volatile Data Storage 非易失性数据存储器OTP: One Time Programmable (memory) 单次可编程存储器PHY:physical layer物理层LL:Link Layer链路层外挂EEPROMDA14580的芯片是没有flash空间的(其实有个32kb的OTP,但只能烧写一次),也可以使用烧录到内存,但是掉电过后就没有程序了。

ptype_base和ptype_all理解netid_receive_skb()函数注解

ptype_base和ptype_all理解netid_receive_skb()函数注解

ptype_base和ptype_all理解netid_receive_skb()函数注解ptype_base和ptype_all理解,netid_receive_skb()函数注解在数据包接收过程的那篇笔记中可以知道,在数据包的处理函数netif_receive_skb中,会先看ptype_all中是否有注册的协议,如果有,则调用相应的处理函数,然后再到ptype_base中,找到合适的协议,将skb发送到相关协议的处理函数.比如ip协议(ip_rcv)或者arp(arp_rcv)等等.此篇笔记讲的是有关ptype_all和ptype_base的相关知识点.ptype_base和ptype_all在内核中存储的情况如下图:可以看到,ptype_base为一个hash表,而ptype_all为一个双向链表.每一个里面注册的协议都用一个struct packet_type表示.struct packet_type {unsigned short type; /*协议类型*/struct net_device *dev;int (*func) (struct sk_buff *, struct net_device *,struct packet_type *);void *data; /* Private to the packet type */struct packet_type *next;};其中需要注意的是dev参数,此参数表明了协议只处理来自dev指向device的数据,当dev=NULL时,表示该协议处理来自所有device的数据.这样,当注册自己的协议时,就可以指定自己想要监听或者接收的device.其中注册和注销协议的函数为:dev_add_pack(...)和dev_remove_pack(...)这两个函数很简单,分别如下:void dev_add_pack(struct packet_type *pt){int hash;br_write_lock_bh(BR_NETPROTO_LOCK);#ifdef CONFIG_NET_FASTROUTE/* Hack to detect packet socket */if ((pt->data) && ((int)(pt->data)!=1)) {netdev_fastroute_obstacles++;dev_clear_fastroute(pt->dev);}#endifif (pt->type == htons(ETH_P_ALL)) {netdev_nit++;pt->next=ptype_all;ptype_all=pt;} else {hash=ntohs(pt->type)&15;pt->next = ptype_base[hash];ptype_base[hash] = pt;}br_write_unlock_bh(BR_NETPROTO_LOCK);}此函数判断协议类型,然后加到ptype_base或者ptype_all 中. void dev_remove_pack(struct packet_type *pt){struct packet_type **pt1;br_write_lock_bh(BR_NETPROTO_LOCK);if (pt->type == htons(ETH_P_ALL)) {netdev_nit--;pt1=&ptype_all;} else {pt1=&ptype_base[ntohs(pt->type)&15];for (; (*pt1) != NULL; pt1 = &((*pt1)->next)) {if (pt == (*pt1)) {*pt1 = pt->next;#ifdef CONFIG_NET_FASTROUTEif (pt->data)netdev_fastroute_obstacles--;#endifbr_write_unlock_bh(BR_NETPROTO_LOCK);return;}}br_write_unlock_bh(BR_NETPROTO_LOCK);printk(KERN_WARNING "dev_remove_pack: %p not found.\n", pt);}此函数也很简单,只是把协议从相关的链表中移除.下面以ip协议为例子来看看相关的实现:ip协议结构体的定义如下:static struct packet_type ip_packet_type ={__constant_htons(ETH_P_IP),NULL, /* All devices */ip_rcv,(void*)1,NULL,};当ipv4协议栈初始化时,会调用ip_init.之后,所有协议类型为ETH_P_IP的包都会交由ip_rcv处理.代码如下:void __init ip_init(void)dev_add_pack(&ip_packet_type);ip_rt_init();inet_initpeers();#ifdef CONFIG_IP_MULTICASTproc_net_create("igmp", 0, ip_mc_procinfo);#endif}这样在系统启动之后,ip协议便被注册到ptype_base链表中,相应的处理函数为ip_rcv.arp协议和其他类型的协议(在ptype_base或者ptype_all中的)的执行过程同理.本人初学网络,水平很菜,如有错误,希望看到的朋友们及时指出,不胜感激.ps1:记得刚来实验室的时候,做的截包模块的第一种方法是用的netfilter,第二种方法主要就是用到的这块知识.现在总结起来,觉得还算简单,当初却用了很长时间,想想,真是难者不会,会者不难啊.今天看书的时候,好像又发现了另外一种方法可以实现我的要求,记录在ps2上.ps2:在数据链路层截包的另一种方法:用PF_PACKETsocket type.linux可以用此类型套节字直接从链路层截获或者注入数据.发送数据时,直接发送到dev_queue_xmit.而接收函数时,可以在数据包通过路由之前截获到.如tcpdump和Ethereal都是用到了此套接字.那么总结起来可以看出,截获数据包至少可以有三种方法实现,第一种的netfilter是在协议栈中截获数据包,而利用ptype_all或者ptype_base和后面这种套节字的方法是在链路层截获数据包.//当网络设备收到网络数据包时,最终会在软件中断环境里调用此函数//当网络设备收到网络数据包时,最终会在软件中断环境里调用此函数[cpp] view plaincopyprint?int netif_receive_skb(struct sk_buff *skb){//ptype_all 用于sniffer这样的程序// 发送一份拷贝给这些注册的sniffer程序list_for_each_entry_rcu(ptype, &ptype_all, list) {if (!ptype->dev || ptype->dev == skb->dev) {if (pt_prev)ret = deliver_skb(skb, pt_prev, orig_dev);pt_prev = ptype;}}// 内核编译开Bridge_config,则将该数据包让网桥函数来处理,否则handle_bridge定义为空操作,// 返回skb,让协议栈来处理上层协议。

gponedat函数 -回复

gponedat函数 -回复

gponedat函数-回复什么是gponedat函数?gponedat函数是基于GPON(Gigabit Passive Optical Network,千兆无源光纤网络)的数据发送函数。

GPON是一种光纤接入技术,采用光纤作为信号传输介质,能够提供高速、大带宽的网络连接。

在GPON系统中,gponedat函数被用来发送和接收数据。

该函数的使用方法如下:1.引入gponedat函数库在你的代码中,首先需要引入gponedat函数库,这样才能使用该函数。

通常,这可以通过在代码开头使用include指令来实现。

例如,在C语言中,你可以使用以下代码来引入gponedat函数库:#include <gponedat.h>2.创建GPON数据结构接下来,你需要创建一个GPON数据结构,这个数据结构将包含发送和接收数据所需的所有信息。

在GPON系统中,数据是通过光纤传输的,因此这个数据结构将包含光纤的相关信息,如光纤的起点和终点等。

你还可以在这个数据结构中添加其他信息,如数据的类型、长度等。

3.设置数据参数在创建GPON数据结构后,你需要设置数据参数。

这些参数包括数据长度、数据类型等。

你可以使用gponedat函数提供的参数设置函数来设置这些参数。

例如,你可以使用以下代码来设置数据长度:gponedat_set_length(gpon_data, data_length);4.发送数据一旦设置了数据参数,你就可以使用gponedat函数来发送数据。

要发送数据,你需要指定数据的起点、终点和数据内容。

你可以使用gponedat 函数提供的发送函数来发送数据。

这些函数可以根据不同的数据类型和数据长度来发送数据。

例如:gponedat_send_data(gpon_data, source, destination, data);5.接收数据在发送数据之后,接收方可以使用gponedat函数来接收数据。

netif_receive_skb 例子

netif_receive_skb 例子

netif_receive_skb 例子摘要:1. netif_receive_skb 函数简介2.函数实现原理3.应用实例4.总结与建议正文:【1】netif_receive_skb 函数简介etif_receive_skb 函数是Linux 内核中网络协议栈的一个关键组成部分,主要用于处理网络设备接收到的数据包。

该函数接收到的数据包会被添加到内核的数据结构sk_buff 中,然后传递给后续的处理流程,如IP 协议栈或其他网络协议栈。

本文将详细介绍netif_receive_skb 函数的实现原理、应用实例以及如何正确使用此函数。

【2】函数实现原理etif_receive_skb 函数的核心功能是将接收到的数据包添加到sk_buff 结构中并触发设备中断。

当网络设备接收到数据包时,设备驱动程序会触发一个中断,中断处理程序会调用netif_receive_skb 函数。

此函数首先检查数据包是否符合预期的格式和校验和,然后将数据包添加到sk_buff 结构中。

接下来,netif_receive_skb 函数会根据数据包的协议类型将数据包传递给相应的网络协议栈进行处理。

【3】应用实例以下是一个netif_receive_skb 函数的应用实例:```cstatic int my_network_device_接收数据包(struct sk_buff *skb){// 检查数据包是否符合预期格式和校验和if (netif_receive_skb(skb) == 0) {// 数据包校验和正确,将其添加到网络协议栈进行处理ip_forward(skb);} else {// 数据包校验和错误,释放资源并丢弃数据包kfree_skb(skb);}return 0;}```在这个实例中,函数首先检查数据包的校验和,如果校验和正确,则将数据包添加到IP 协议栈进行处理;如果校验和错误,则释放sk_buff 结构占用的资源,并丢弃数据包。

ObjType和参数解释

ObjType和参数解释

TRAPOOL TRAPOOL TRA的名称TRAPOOL CHRATE TRA的速率TRAPOOL SPV语音编码TRAPOOL RNOTRA定义的需要的TRA数量TRAPOOL POOLACT实际有TRA设备的总数量TRAPOOL POOLIDLE处于空间状态的TRA设备的数量TRAPOOL POOLTRAF使用中的TRA设备的数量TRAPOOL SUBACT实际有TRA子设备的总数量TRAPOOL SUBIDLE处于空间状态的TRA子设备的数量TRAPOOL SUBTRAF使用中的TRA子设备的数量TRAPOOL SUBPOOL TRA子设备的编号CELL LOCATING DATA AW允许指配到较差小区CELL LOCATING DATA SCHO允许SDCCH切换CELL LOCATING DATA MISSNM允许连续丢失的最大测量报告次数CELL LOCATING DATA BSPWR BCCH控制信道的有效发射功率CELL LOCATING DATA BSTXPWR话务信道的有效发射功率CELL LOCATING DATA MSRXMIN手机测到最小信号强度,即下行最小信号强度门限,信号高于此值CELL LOCATING DATA BSRXMIN基站测到最小信号强度,即上行最小信号强度门限,信号高于此值CELL LOCATING DATA MSRXSUFF下行足够电平阙值CELL LOCATING DATA BSRXSUFF上行足够电平阙值CELL LOCATING DATA EXTPEN支持切换惩罚,用于表示MSC和目标BSC是否支持切换惩罚CELL LOCATING DATA HYSTSEP信号强度离析器,用于指示高信号和低信号小区CELL LOCATING DATA ISHOLEV内部切换水平系统,当空闲话务低于该值时,启动切换评价系统CELL LOCATING DATA FBOFFSP正的频带群偏移量,用于设置高频段和低频段的区别CELL LOCATING DATA FBOFFSN负的频带群偏移量,用于设置高频段和低频段的区别CELL LOCATING FILTER DASSEVALSI选择在信令连接阶段选择一个信号强度滤波器SSEVALSD选择在话音/数据连接阶段选择信号强度滤波器CELL LOCATING FILTER DATCELL LOCATING FILTER DATSSLENSI用于指定SSEVALSI滤波器的长度SSLENSD用于指定SSEVALSD滤波器的长度CELL LOCATING FILTER DATSSRAMPSI激活SSEVALSI斜波时决定SACCH周期的数目CELL LOCATING FILTER DATSSRAMPSD激活SSEVALSD斜波时决定SACCH周期的数目CELL LOCATING FILTER DATQEVALSI选择在信令连接阶段选择一个质量滤波器CELL LOCATING FILTER DATQEVALSD选择在话音/数据连接阶段选择一个质量滤波器CELL LOCATING FILTER DATQLENSI用于指定QEVALSI滤波器的长度CELL LOCATING FILTER DATQLENSD用于指定QEVALSD滤波器的长度CELL LOCATING FILTER DATBSC LOCATING DATA TINIT两次切换最小的时间间隔(切换成功之后的再次尝试间隔),为S BSC LOCATING DATA THO切换的时间间隔BSC LOCATING DATA NHO在THO内切换允许的次数BSC LOCATING DATA TAAVELEN时间提前(TA)的滤波器的长度BSC LOCATING DATA EVALTYPE设备选择何种定位算法。

Linux内核二层数据包接收流程

Linux内核二层数据包接收流程

Linux内核⼆层数据包接收流程本⽂主要讲解了Linux内核⼆层数据包接收流程,使⽤的内核的版本是2.6.32.27为了⽅便理解,本⽂采⽤整体流程图加伪代码的⽅式从内核⾼层⾯上梳理了⼆层数据包接收的流程,希望可以对⼤家有所帮助。

阅读本⽂章假设⼤家对C语⾔有了⼀定的了解整体流程如下:数据报⽂接收流程伪代码分析如下/*在基于中断收发报⽂的⽹卡设备驱动中,* 当有数据报⽂进来的时候,使⽤net_interrupt()进⾏中断触发*如 isa-skeleton设备驱动中*/static int __init netcard_probe1(struct net_device *dev, int ioaddr){/*注册net_interrupt为中断处理历程*/int irqval = request_irq(dev->irq, &net_interrupt, 0, cardname, dev);if (irqval) {printk("%s: unable to get IRQ %d (irqval=%d).\n",dev->name, dev->irq, irqval);goto out;}//......return err;}static irqreturn_t net_interrupt(int irq, void *dev_id){//......if (status & RX_INTR) {/* Got a packet(s). *//*使⽤NET_RX实现进⾏发送数据报⽂*/net_rx(dev);}#if TX_RINGif (status & TX_INTR) {/* Transmit complete. */net_tx(dev);np->stats.tx_packets++;netif_wake_queue(dev);}#endifreturn IRQ_RETVAL(handled);}/* We have a good packet(s), get it/them out of the buffers. */static voidnet_rx(struct net_device *dev){/*使⽤dev_alloc_skb来分配skb,并把数据报⽂复制到skb中*/skb = dev_alloc_skb(pkt_len);if (skb == NULL) {//......}skb->dev = dev;/* 'skb->data' points to the start of sk_buff data area. */memcpy(skb_put(skb,pkt_len), (void*)dev->rmem_start, pkt_len);/* or */insw(ioaddr, skb->data, (pkt_len + 1) >> 1);/*调⽤netif_rx将数据报⽂交给上层处理*/netif_rx(skb);return;}DEFINE_PER_CPU(struct netif_rx_stats, netdev_rx_stat) = { 0, };/*完成中断处理过程*/int netif_rx(struct sk_buff *skb){struct softnet_data *queue;unsigned long flags;/*取得当前时间存储在skb->tstamp中*/if (!skb->64)net_timestamp(skb);/** The code is rearranged so that the path is the most* short when CPU is congested, but is still operating.*/local_irq_save(flags);/*取得当前CPU的softnet_data,*/queue = &__get_cpu_var(softnet_data);if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {if (queue->input_pkt_queue.qlen) {enqueue:/*将SKB放⼊到softnet_data[CPU].input_pkt_queue中*⼀旦数据包出于该对列,中断就处理完成了*/__skb_queue_tail(&queue->input_pkt_queue, skb);local_irq_restore(flags);return NET_RX_SUCCESS;}/*如果queue->input_pkt_queue.qlen中已经有上次的数据包,*发起NET_RX_SOFTIRQ软中断,由软中断的处理函数net_rx_action进⾏发送*/ napi_schedule(&queue->backlog);{__napi_schedule(n){list_add_tail(&n->poll_list, &__get_cpu_var(softnet_data).poll_list);__raise_softirq_irqoff(NET_RX_SOFTIRQ);}}goto enqueue;}__get_cpu_var(netdev_rx_stat).dropped++;local_irq_restore(flags);kfree_skb(skb);return NET_RX_DROP;}/*注册软中断NET_RX_SOFTIRQ的处理函数为net_rx_action*/static int __init net_dev_init(void){open_softirq(NET_RX_SOFTIRQ, net_rx_action);}/*必须要有NAPI的POLL么?没有NAPI的POLL回调怎么送往协议栈*/static void net_rx_action(struct softirq_action *h){struct list_head *list = &__get_cpu_var(softnet_data).poll_list;while (!list_empty(list)) {struct napi_struct *n;n = list_entry(list->next, struct napi_struct, poll_list);/*调⽤每款驱动对NAPI注册的POLL函数,如pcnet32_poll*在POLL函数的RX部分⾥⾯,会调⽤netif_receive_skb将*数据包交给协议栈处理*/work = n->poll(n, weight);WARN_ON_ONCE(work > weight);budget -= work;local_irq_disable();/* Drivers must not modify the NAPI state if they* consume the entire weight. In such cases this code* still "owns" the NAPI instance and therefore can* move the instance around on the list at-will.*/if (unlikely(work == weight)) {if (unlikely(napi_disable_pending(n))) {local_irq_enable();napi_complete(n);local_irq_disable();} elselist_move_tail(&n->poll_list, list);}netpoll_poll_unlock(have);}out:local_irq_enable();#ifdef CONFIG_NET_DMA/** There may not be any more sk_buffs coming right now, so push* any pending DMA copies to hardware*/dma_issue_pending_all();#endifreturn;softnet_break:__get_cpu_var(netdev_rx_stat).time_squeeze++;__raise_softirq_irqoff(NET_RX_SOFTIRQ);goto out;}/*在RX部分⾥,会调⽤*/static int pcnet32_poll(struct napi_struct *napi, int budget){/*RX部分*/work_done = pcnet32_rx(dev, budget);{pcnet32_rx_entry(){netif_receive_skb(skb);}}/*TX部分*/pcnet32_tx(dev);return work_done;}int netif_receive_skb(struct sk_buff *skb){struct packet_type *ptype, *pt_prev;struct net_device *orig_dev;pt_prev = NULL;/*看看ptype_all中有没有相应的协议进⾏相应的协议处理,⼀般这⾥没有注册的协议,但是可以加⼊我们的分析钩⼦函数*/ 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 = ptype;}}/*处理⽹桥配置的数据报⽂*/skb = handle_bridge(skb, &pt_prev, &ret, orig_dev);if (!skb)goto out;skb = handle_macvlan(skb, &pt_prev, &ret, orig_dev);if (!skb)goto out;/*对ptype_base表中的协议进⾏遍历,如果找到对应的协议,送往对应的协议栈进⾏处理*/type = skb->protocol;list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {if (ptype->type == type && (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 = ptype;}}if (pt_prev) {ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);}else{kfree_skb(skb);ret = NET_RX_DROP;}out:rcu_read_unlock();return ret;}/*调⽤相应协议的func进⾏处理*/static inline int deliver_skb(struct sk_buff *skb, struct packet_type *pt_prev, struct net_device *orig_dev){return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);}/*在af_inet.c⽂件中对IPV4的处理注册为ip_rcv,所以IPV4对应的FUNC为ip_rcv*/static struct packet_type ip_packet_type __read_mostly = {.type = cpu_to_be16(ETH_P_IP),.func = ip_rcv,};/** Main IP Receive routine.*/int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev){//......}从分析的伪代码可以看出,数据包接受的时候,可以基于2中⽅式触发:1 收发中断 2 NAPI的轮询机制这⾥没有分析驱动代码对硬件的操作,这部分代码在设备驱动程序中,本⽂举例了2款⽹卡代码 pcnet32 和 isa-skeleton,当硬件接受完毕之后就进⼊dev层⾯进⾏内核的总体调度,这也是上⾯伪代码分析的重点。

底层通讯

底层通讯

如题所示此篇学习笔记记录的是数据报的发送过程关于接收的相关过程另一篇笔记有总结.以下总结了从网络协议到网卡数据报传输的具体过程以下研究的代码来自于2.4.无论是ip还是arp协议当有数据要发送的时候都会调用dev_queue_xmit函数也就是说dev_queue_xmit是驱动程序对上面的接口./**功能:发送一个skb*主要执行步骤:*1检查skb是否为碎片检查device是否能处理skb的碎片操作.*2确定检验和已经被正确计算.*3发送skb.此时有两种情况一种情况是有Qos层调用qdisc_run(执行过程中真正的调用函数是qdisc_restart)另一种情况是直接调用hard_queue_xmit(如虚设备)* 3.1:根据出队规则选择一个skb发送出去注意此时的skb不一定就是传递进来的skb因为要考虑到出队规则.(第一种情况)* 3.2:直接调用hard_queue_xmit将数据报发送出去.(如虚拟设备)*注意:此时发送一个skb所需要的东西都已经知道了比如说skb->dev为outgoing device即选择传输的网络接口.*/int dev_queue_xmit(struct sk_buff *skb){struct net_device*dev =skb->dev;struct Qdisc *q;/*首先检查skb_shinfo(skb)->frag_list是否有值如果有但是网络设备不支持skb的碎片列表*则通过skb_linearize把这些碎片重组到一个完整的skb中.*/if(skb_shinfo(skb)->frag_list &&!(dev->features&NETIF_F_FRAGLIST)&&skb_linearize(skb GFP_ATOMIC)!=0){kfree_skb(skb);return-ENOMEM;}if(skb_shinfo(skb)->nr_frags &&(!(dev->features&NETIF_F_SG)||illegal_highdma(dev skb))&&skb_linearize(skb GFP_ATOMIC)!=0){kfree_skb(skb);return-ENOMEM;}/* 如果数据包未做检验和并且设备对该协议不支持检验和计算则在* 此处计算检验和.*/if(skb->ip_summed ==CHECKSUM_HW &&(!(dev->features&(NETIF_F_HW_CSUM|NETIF_F_NO_CSUM))&&(!(dev->features&NETIF_F_IP_CSUM)||skb->protocol !=htons(ETH_P_IP)))){if((skb =skb_checksum_help(skb))==NULL)return-ENOMEM;}/* Grab device queue */spin_lock_bh(&dev->queue_lock);/*net_device的成员qdisk是一个发送队列缓冲等待网络设备进行发送的skb*/q =dev->qdisc;//如果队列存在入队一般情况下为实设备有自己的发送队列如果是虚设备则一般没有自己的发送队列.if(q->enqueue){int ret =q->enqueue(skb q);/*根据出队规则挑选一个skb发送出去*/qdisc_run(dev);spin_unlock_bh(&dev->queue_lock);/*返回状态信息*/return ret ==NET_XMIT_BYPASS ?NET_XMIT_SUCCE SS :ret;}/*如执行到此步则表明网络接口没有自己的发送队列一般为虚设备的情况如loopback ip隧道等*/if(dev->flags&IFF_UP){int cpu =smp_processor_id();if(dev->xmit_lock_owner !=cpu){spin_unlock(&dev->queue_lock);spin_lock(&dev->xmit_lock);dev->xmit_lock_owner =cpu;if(!netif_queue_stopped(dev)){//如果ptype_all中有成员则先发给其中注册的处理函数if(netdev_nit)dev_queue_xmit_nit(skb dev) ;//dev->hard_start_xmit对应实际设备驱动程序的发送函数if(dev->hard_start_xmit(skb dev )==0){dev->xmit_lock_owner =-1 ;spin_unlock_bh(&dev->xmit_l ock);return0;}}/*如果device的发送队列被禁止*/dev->xmit_lock_owner =-1;spin_unlock_bh(&dev->xmit_lock);if(net_ratelimit())printk(KERN_CRIT "Virtual device %s asks to queue packet!\n"dev->name);kfree_skb(skb);return-ENETDOWN;}else{/* Recursion is detected! ispossible unfortunately */if(net_ratelimit())printk(KERN_CRIT "Dead loop on virtual device%s!\n"dev->name);}}spin_unlock_bh(&dev->queue_lock);/*如果设备没有被打开*/kfree_skb(skb);return-ENETDOWN;}由上面的注释可以看到dev_queue_xmit会遇到两种情况一种情况是有traffic control层(QoS层)一种是直接调用设备驱动程序的发送函数hard_start_xmit(比如一般的虚拟设备都是如此)前种情况的路线是:dev_queue_xmit-->qdisc_run-->qdisc_restart(dev)后者的路线是:dev_queue_xmit-->hard_start_xmit.qdisc的源代码为:可以看出,qdisc_run只是qdisc_restart的包裹函数,此函数过滤了发送队列被禁止的dev,然后调用qdisc_restart.有)---->dev_hard_xmit.**返回值:* 0 : 发送队列为空时.* 1 : 队列非空但由于某种原因没能发送数据比如不能获得dev的发送队列的锁dev->xmit_lock.* -1: 发送成功时.*/int qdisc_restart(struct net_device*dev){/*dev->qdisc为dev的发送队列*/struct Qdisc *q =dev->qdisc;struct sk_buff *skb;/* 根据一定的规则挑选一个skb发送出去此处为体现有traffic control时的区别*/if((skb =q->dequeue(q))!=NULL){if(spin_trylock(&dev->xmit_lock)){/* Remember that the driver is grabbed by us. */dev->xmit_lock_owner =smp_processor_id() ;/* And release queue */spin_unlock(&dev->queue_lock);if(!netif_queue_stopped(dev)){/*如果有注册的sniffer则发送个各个sniffer当sniffer注册时netdev_nit会相应的加1所以netdev_nit代表了sniffer的数量*/if(netdev_nit)dev_queue_xmit_nit(skb de v);if(dev->hard_start_xmit(skb dev )==0){dev->xmit_lock_owner =-1 ;spin_unlock(&dev->xmit_lock );spin_lock(&dev->queue_lock) ;return-1;}}/* Release the driver */dev->xmit_lock_owner =-1;spin_unlock(&dev->xmit_lock);spin_lock(&dev->queue_lock);q =dev->qdisc;}/*如果没得到device的发送函数的锁则说明此device 已经被别的cpu调用在发送数据*/else{/* So someone grabbed the driver. *//* may be transient configuration errorwhen hard_start_xmit() recurses. We detectby checking xmit owner and drop thepacket when deadloop is detected.*//*如果device的发送函数锁为本cpu所有但却还忙则free掉sk_buff*/if(dev->xmit_lock_owner ==smp_processo r_id()){kfree_skb(skb);if(net_ratelimit())printk(KERN_DEBUG "Dead l oop on netdevice %s!\n"dev->name);return-1;}/*更新冲突状态信息*/netdev_rx_stat[smp_processor_id()].cpu_coll ision++;}/* Device kicked us out :(This is possible in three cases:0. driver is locked1. fastroute is enabled2. device cannot determine busy statebefore start of transmission (f.e. dialout)3. device is buggy (ppp)*//*走到此应该是没发送成功则把skb重新放回到队列中然后调用net0f_schedule(dev)再次试图将其通过dev发送出去*/q->ops->requeue(skb q);netif_schedule(dev);return1;}/*程序走到这时说明队列为空q.qlen应该是0*/return q->q.qlen;}可以看到调用了qdisk_restart函数的一般都是device有自己的发送队列的情况此时在出队列函数dequeue处体现了traffic control的作用.由代码可知在发送失败时会将skb重新放入队列中然后调用netif_schedule(dev)将其重新发送.下面来看看netif_schedule的源代码.可以看到,netif_schedule是__netif_schedule(dev)的包裹函数.如注释所示,此函数功能很简单,把参数传进来的dev放到softnet_data[cpu].output_queue的链表首,优先等待调度,然后触发发送软中断,调用相应的发送处理函数,即net_tx_action.读到此,产生了两个问题,就是说,发送成功后,直接返回-1,由于某种原因不能发送skb时才会有这些操作,也就是说,只有在因为某种原因不能发送skb时,需要将数据报重新放回队列,然后把dev放在output_queue的链表首,此时才触发发送软中断,不是每次发送数据时都触发发送软中断?还有,我记得当数据报发送数据包成功后会把skb加入到completion_queue中,但是怎么没看到相关代码?这两个问题先放下,看看读完net_tx_action的代码后能不能找到答案./*因为net_tx_action不是在中断的环境中运行的所以驱动程序可以在任意时候不顾net_tx_action的执行而向completion_queue中添加数据所以此处要禁止中断因为禁止中断时间越短越好所以先用本地变量clist指向softnet_data[cpu].completion_queue然后将softnet_data[cpu].completion_queue设置为NULL开中断由于本地变量是不能被外界访问的所以之后再慢慢的处理skb*/local_irq_disable();clist =softnet_data[cpu].completion_queue;softnet_data[cpu].completion_queue =NULL;local_irq_enable();while(clist !=NULL){struct sk_buff *skb =clist;clist =clist->next;BUG_TRAP(atomic_read(&skb->users)==0);__kfree_skb(skb);}}if(softnet_data[cpu].output_queue){struct net_device*head;/*禁止中断的原因同上*/local_irq_disable();head =softnet_data[cpu].output_queue;softnet_data[cpu].output_queue =NULL;local_irq_enable();while(head !=NULL){struct net_device*dev =head;head =head->next_sched;smp_mb__before_clear_bit();/*调用output_queue中的device进行发送数据*/clear_bit(__LINK_STATE_SCHED&dev->state) ;/*在调用一个device发送数据时应先获得此设备的发送队列的锁*/if(spin_trylock(&dev->queue_lock)){qdisc_run(dev);spin_unlock(&dev->queue_lock);}/*如果得不到此设备的发送队列的锁此时可能有另外的cpu在用到此设备发送数据则重新调度此设备发送数据*/else{netif_schedule(dev);}}}}此函数完成两件事如上面代码注释可知: 1释放softnet_data[cpu].completion_queue中发送成功的sk_buff. 2调度softnet_data[cpu].output_queue中的device发送其发送队列中的数据.突然想起在以前读ldd2的时候看见过如下一句话:网络接口在两种可能的事件下中断处理器:新数据包的到达或者外发数据包的传输已经完成.好像想起了点什么觉得应该是这样:当数据报成功传送完之后把其加入到completion_queue中的代码应该是在网卡驱动中具体实现的传输成功后会调用net_tx_action来处理completion_queue中发送成功的数据报然后在调度output_queue中的device 传送其发送队列中的数据包.读到这时最少两种情况会调用net_tx_action第一种情况是在由于某种原因不能发送skb时(比如不能获得dev的发送队列的锁dev->xmit_lock)或者在网卡驱动程序成功发送完数据包后会产生中断然后执行发送软中断处理函数.这样理解可以理解通不知道实事上是不是这样如果我想错了知道正确答案的朋友希望能给予及时指正不胜感激.这里要知道跟接收数据包时的原理一样一般不会将所有事情都在中断环境中解决所以当一个网卡发送完数据包后并不是直接的在中断环境下释放skb资源而是将其放入completion_queue中这块也只是指针的指向而不是数据包的拷贝然后调用发送软中断来处理数据包.当驱动程序传输完数据包后会调用dev_kfree_skb_irq来释放sk_buff.此处可以看出来,此函数只是把skb放入completion_queue的链表头,真正的释放工作在发送软中断net_tx_action中完成.ok,分析到这,基本上算是通了.下面是自己在研究以上代码时随手记的一些零散知识点,有看到这篇笔记的朋友们可以直接pass了,自己不舍得扔掉,于是贴在此.数据包的发送过程和接收过程大体上类似:1,NET_TX_SOFTIRQ:发送软中断<--------> NET_RX_SOFTIRQ:接收软中断 ; net_tx_action:发送软中断处理函数<--------> net_rx_action:接收软中断处理函数 ; dev_queue_xmit <--------> netif_rx ; output_queue:NAPI和non-NAPI都用<-------->poll_list:只有non_NAPI用.2,当一个device被调用以用来接收数据时,其device的__LINK_STATE_RX_SCHED被标记.当一个device被调用以用来发送数据时,其device的__LINK_STATR_SCHED被标记.3,当一个device正在被调用时,为什么要使能或禁止队列的传输功能呢?一个原因是,一个device可能暂时用光其存储空间,所以会导致传输失败.解决办法:当驱动程序获知devicedevice没有足够的space来存储一个数据包时,调用netif_stop_queue禁止传输.相反,当驱动程序获知device有足够的space 来存储一个skb时,则调用netif_start_queue使能传输功能.4netif_wake_queue = netif_start_queue + netif_schedule5当传输一个数据时.当以下条件发生时需要把数据包重新入队.1)发送队列被禁止(netif_queue_stopped(dev) is true)2)另一个cpu正持有此device driver的lock.3)传输失败.6dev_kfree_skb:释放skb ; dev_kfree_skb_irq:仅仅把skb放入到softnet_data的completion_queue中然后调用发送软中断让net_tx_action完成真正的释放工作.由于本人刚开始研究网络的底层实现不长时间所以难免有理解错误的地方如果本篇笔记存在错误希望看到的达人们给予指正不胜感激.。

以太网IP核说明书

以太网IP核说明书

以太网IP核说明书目录1 (3)INTRODUCTION (3)2 (4)IO PORTS (4)2.1 ETHERNET CORE IO PORTS (4)2.1.1 Host Interface Ports (4)2.1.2 PHY Interface ports (6)3 (8)REGISTERS (8)3.1 MODER (MODE REGISTER) (9)3.2 INT_SOURCE (INTERRUPT SOURCE REGISTER) (11)3.3 INT_MASK (INTERRUPT MASK REGISTER) (12)3.4 IPGT (BACK TO BACK INTER PACKET GAP REGISTER) (13)3.5 IPGR1 (NON BACK TO BACK INTER PACKET GAP REGISTER 1) (13)3.6 IPGR2 (NON BACK TO BACK INTER PACKET GAP REGISTER 2).......................................... 错误!未定义书签。

3.7 PACKETLEN (PACKET LENGTH REGISTER) .... 错误!未定义书签。

3.8 COLLCONF (COLLISION AND RETRY CONFIGURATION REGISTER).......................................... 错误!未定义书签。

3.9 TX_BD_NUM (TRANSMIT BD NUMBER REG.) ... 错误!未定义书签。

3.10 CTRLMODER (CONTROL MODULE MODE REGISTER)错误!未定义书签。

3.11 MIIMODER (MII MODE REGISTER) ......... 错误!未定义书签。

关于ptype_all和pypte_base中的pt_prev的说明[转]

关于ptype_all和pypte_base中的pt_prev的说明[转]

关于ptype_all和pypte_base中的pt_prev的说明[转]不知道原帖,我是从看到了,解决了迷惑我很久的疑问,抄过来。

看见noble_shi兄弟"关于net_rx_action函数的若⼲问题"贴中关于pt_prev的问题,本来想在论坛上找到⼀个相关的帖⼦的链接告诉他。

但是发现咱们论坛上关于pt_prev的讨论要么没有说明,要么理解的偏差,甚⾄是错误。

⽽且关于pt_prev的提问很多。

故写了以下内容。

不过本⼈⽔平有限,难免说错。

请执教结论:pt_prev使⽤的原因是为了减少⼀次kfree_skb的调⽤,提⾼效率。

如果有异议的请往下看。

如果你对skb⾮常了解,那么请直接看<三>,否则请⼀步⼀步往下看。

<⼀>相关知识在讲pt_prev的作⽤之前,咱们先说明以下的东西。

(1)alloc_skb中初始化skb->users计数为1。

struct sk_buff( ){....atomic_set(&skb->users,1);...}(2)kfree_skb中如果计数skb->users不为1则不会释放skbuff 。

static inline void kfree_skb(struct sk_buff *skb){if (atomic_read(&skb->users) == 1 || atomic_dec_and_test(&skb->users))__kfree_skb(skb);}当引⽤数为1或者引⽤数减1等于零时,回收包缓冲。

(3)linux内核⽹络协议栈中到本机的skb包是在上层协议中释放的。

<⼆>实现ptype_base和ptype_all链讲了上⾯的东西后咱们来看ptype_base及ptype_all链相关的东西。

这两个链的作⽤在这⾥就不讲了。

因为有了上⾯的东西,所以涉及到⼀个skbuff共享的问题,如果都⽤skb_clone或者skb_copy,那么性能将是很低的。

POSIX标准理解

POSIX标准理解

POSIX标准理解POSIX标准总体分析? POSIX,全称为可移植性操作系统接口,是一种关于信息技术的IEEE标准。

它包括了系统应用程序接口(简称API),以及实时扩展[C语言]。

该标准的目的是定义了标准的基于UNIX操作系统的系统接口和环境来支持源代码级的可移植性。

现在,标准主要提供了依赖C语言的一系列标准服务,再将来的版本中,标准将致力于提供基于不同语言的规范。

该标准对核心需求部分定义了一系列任何编程语言都通用的服务,这一部分服务主要从其功能需求方面阐述,而非定义依赖于编程语言的接口。

语言规范主要有两部分组成。

一部分包括了访问核心服务的编程语言的标准接口,这些核心服务为标准中基于编程语言的核心需求部分所定义;另一部分包含了一个特殊语言服务的标准接口。

基于任何语言,与该标准一致的执行都必须遵循语言规范的任何章节。

该标准一共被分为四个部分:(1)陈述的范围和一系列标准参考(第一章);(2)定义和总概念;(第二章)(3)各种接口设备;(第三章到第九章,第十一章到第十五章)(4)数据交换格式;(第十章)该标准的主要目的有:(1)面向应用(2)定义接口,而不是它的具体实现;(3)涉及资源和可移植性,而非对象;(4)基于c语言;(5)无超级用户,无系统管理;(6)最小限度的接口,最小限度的定义;(7)应用领域广泛;(8)对以前的实现进行最小限度改变;(9)对原有程序代码做最小的修改;(10)实时扩展;以下就对各个章节做简要分析。

第一章概述1.1范围定义范围的关键要素有:(1)定义足够的一套功能适用于实时应用程序领域的重要部分;(2)定义足够的实现规范和性能相关的函数,以便允许实时应用程序完成系统的确定性的响应;1.2?一致性系统须支持标准中定义的接口,系统能够提供标准中没有要求到的函数和工具。

在遵循于该标准的实现中,一种一致性文档是需要用到的,它必须具有与该标准相同的结构,包含有全名,数字,和标准所指示的日期,以及头文件<limits.h>和<unistd.h>中的界限值等等。

uefi中pcdgetbool用法

uefi中pcdgetbool用法

uefi中pcdgetbool用法UEFI中PCDGETBOOL用法什么是UEFI?•UEFI(Unified Extensible Firmware Interface)是一种新一代的固件接口标准,它取代了传统的BIOS(Basic Input/Output System)接口。

•UEFI提供了更加灵活和功能丰富的系统启动和管理能力,支持大容量硬盘、64位系统、安全启动等特性。

PCDGETBOOL概述•PCDGETBOOL是UEFI中用于获取布尔类型配置数据的函数。

•PCD代表的意思是Platform Configuration Database,也就是平台配置数据库,用于存储系统的配置信息。

•PCDGETBOOL函数可以从配置数据库中获取布尔类型的配置数据。

PCDGETBOOL用法示例1.首先,我们需要在代码中包含头文件#include <>。

2.调用PCDGETBOOL函数来获取布尔类型的配置数据。

函数的参数包括配置数据库项的GUID和配置项名称。

BOOLEAN MyBoolValue = PCDGETBOOL(MyGuid, M yBoolConfig);–MyGuid是配置数据库项的GUID,用于唯一标识一项配置数据。

–MyBoolConfig是配置项的名称,用于标识要获取的布尔类型的配置数据。

3.接下来,我们可以使用获取到的布尔值进行相应的操作,比如条件判断等。

if (MyBoolValue) {// 布尔值为真的情况下执行的代码} else {// 布尔值为假的情况下执行的代码}总结通过使用UEFI中的PCDGETBOOL函数,我们可以方便地获取布尔类型的配置数据,并根据获取到的值进行相应的操作。

这样可以使我们的UEFI代码更加灵活和可配置,适应不同的系统需求。

注意:文章中仅提供了PCDGETBOOL函数的基本用法示例,具体的使用方法和参数可能因不同的UEFI版本和具体的功能需求而有所不同。

sv modport用法 -回复

sv modport用法 -回复

sv modport用法-回复sv modport是一种系统级仿真工具,用于连接和通信硬件模块之间的各个接口。

它可以简化系统级设计的建模和仿真过程,并帮助验证和调试复杂的硬件系统。

本文将逐步介绍sv modport的用法,包括定义、使用和实例演示,希望能帮助读者更好地理解和应用这一功能。

首先,我们来了解sv modport的基本概念和定义。

sv modport是SystemVerilog语言的一部分,用于定义一个模块的接口特性。

它可以指定端口的方向和通信方式,包括输入、输出和双向端口,以及数据传输的方式,如阻塞和非阻塞。

sv modport的定义通常在模块的接口部分进行,使用以下语法:modport modport_name (port_list);其中,modport_name是自定义的模块接口名称,port_list是一个包含所有模块端口的列表。

每个端口都指定了其方向和通信方式。

接下来,我们将详细介绍如何使用sv modport。

在使用sv modport之前,首先需要定义一个包含模块接口的数据类型,如struct或interface。

然后,在模块的接口声明中使用modport来定义具体的接口特性。

例如,假设我们有一个顶层模块top_module,它包含两个子模块child_module1和child_module2。

我们可以定义一个包含所有端口的接口数据类型interface_type,并在每个子模块的接口声明中使用sv modport来指定模块端口的特性。

typedef struct {logic [7:0] data;logic enable;} interface_type;module child_module1 (input sv_interface_type::modport_name1 port);...endmodulemodule child_module2 (input sv_interface_type::modport_name2 port);...endmodulemodule top_module;sv_interface_type port1, port2;child_module1 u1 (port1.modport_name1);child_module2 u2 (port2.modport_name2);...endmodule在上面的例子中,我们首先定义了一个接口数据类型interface_type,其中包含了两个信号data和enable。

并口基础知识学习

并口基础知识学习

Not pending:直接处理1.IOCTL_PAR_QUERY_LOCATION获取设备链接名,形式为LPTnSymblicname形式为/DosDevice/LPTn若输出缓冲区小于Location字节长度,则返回STATUS_BUFFER_TOO_SMALL否则,返回Location2.IOCTL_SERIAL_GET_TIMEOUTS获取超时结构,超时结构保存在设备扩展中,由设置超时的IOCTL来设置。

3.IOCTL_PAR_IS_PORT_FREE查询该IRP处理时,该并口是否是FREE状态通过设备扩展中的bAllocated变量来判断,并返回TRUE/FALSE信息若bAllocated = TURE,代表没有其他人用于该并口,该并口是FREE状态的,返回FALSE 信息到systembuffer若bAllocated = FALSE,则再通过对该并口的分配与释放操作来判断是否有其他设备占有该并口首先,先分配该并口TryAllocatePort,若成功则释放该并口FreePort,返回TRUE信息若失败则返回FALSEbAllocated:当并口设备被select后,该变量被置为TRUE当并口设备被deselect后,该变量被置为FALSE4.IOCTL_PAR_GET_READ_ADDRESS获取读地址IOCTL_PAR_SET_READ_ADDRESS为ECP或者EPP设置读地址保存在Pdx->ReverseInterfaceAddress5.IOCTL_PAR_GET_WRITE_ADDRESS获取写地址IOCTL_PAR_SET_WRITE_ADDRESS为ECP或者EPP设置写地址保存在Pdx->ForwardInterfaceAddress6.IOCTL_IEEE1284_GET_MODE获取此时使用的读写模式根据Pdx->IdxReverseProtocol在arpReverse中提取读模式根据Pdx->IdxForwardProtocol在afpForward中提取读模式7.IOCTL_PAR_GET_DEFAULT_MODES获取默认模式,读为空,写为兼容模式CENTRONICS8.IOCTL_PAR_ECP_HOST_RECOVERY设置是否允许ECPHw模式端口的恢复操作保存在Pdx->bIsHostRecoverSupported变量当ECP模式在设置读写读地址的时候,若是通过测试ECR发现设备有错误,则需要回复ECP端口设置。

报文处理流程

报文处理流程

Linux报文处理流程。

newmaker@1目的了解linux报文转发流程好比理解Linux网络的骨架。

有个宏观的概念,对于网络开发,提高性能,bug解决起到至关重要的作用。

2处理一级流程图3软中断处理与驱动3.1软中断软中断中取报文,是通过NAPI结构的poll函数取的,每个CPU拥有一个NAPI列表。

、NAPI是驱动注册的取报文的结构。

使用NAPI主要是采用poll方式取报文,而不是依赖硬中断处理,对于小包,报文数比较多的情况下,大大的提高的效率,毕竟硬中断太影响性能。

struct napi_struct{/*The poll_list must only be managed by the entity which*changes the state of the NAPI_STATE_SCHED bit.This means*whoever atomically sets that bit can add this napi_struct*to the per-cpu poll_list,and whoever clears that bit*can remove from the list right before clearing the bit.*/struct list_head poll_list;unsigned long state;int weight;//为poll函数的第2个参数,表示每次期望取多少报文。

如果取的报文不足该数,则从cpu napi队列去掉该napi。

int(*poll)(struct napi_struct*,int);//处理函数。

#ifdef CONFIG_NETPOLLspinlock_t poll_lock;int poll_owner;#endifunsigned int gro_count;struct net_device*dev;struct list_head dev_list;struct sk_buff*gro_list;struct sk_buff*skb;};static void net_rx_action(struct softirq_action*h){struct list_head*list=&__get_cpu_var(softnet_data).poll_list;unsigned long time_limit=jiffies+2;int budget=netdev_budget;void*have;local_irq_disable();while(!list_empty(list)){struct napi_struct*n;int work,weight;/*If softirq window is exhuasted then punt.*Allow this to run for2jiffies since which will allow*an average latency of1.5/HZ.*//*每次软中断取报文不要高于300,所耗时间不要超过2个时钟中断*/ if(unlikely(budget<=0||time_after(jiffies,time_limit)))goto softnet_break;local_irq_enable();/*Even though interrupts have been re-enabled,this*access is safe because interrupts can only add new*entries to the tail of this list,and only->poll()*calls can remove this head entry from the list.*/n=list_first_entry(list,struct napi_struct,poll_list);have=netpoll_poll_lock(n);weight=n->weight;/*This NAPI_STATE_SCHED test is for avoiding a race*with netpoll's poll_napi().Only the entity which*obtains the lock and sees NAPI_STATE_SCHED set will*actually make the->poll()call.Therefore we avoid*accidently calling->poll()when NAPI is not scheduled.*/work=0;if(test_bit(NAPI_STATE_SCHED,&n->state)){work=n->poll(n,weight);trace_napi_poll(n);}WARN_ON_ONCE(work>weight);budget-=work;local_irq_disable();/*Drivers must not modify the NAPI state if they*consume the entire weight.In such cases this code*still"owns"the NAPI instance and therefore can*move the instance around on the list at-will.*/if(unlikely(work==weight)){if(unlikely(napi_disable_pending(n))){local_irq_enable();napi_complete(n);local_irq_disable();}elselist_move_tail(&n->poll_list,list);/*为了napi公平,移到队尾*/ }netpoll_poll_unlock(have);}out:local_irq_enable();#ifdef CONFIG_NET_DMA/**There may not be any more sk_buffs coming right now,so push*any pending DMA copies to hardware*/dma_issue_pending_all();#endifreturn;softnet_break:__get_cpu_var(netdev_rx_stat).time_squeeze++;__raise_softirq_irqoff(NET_RX_SOFTIRQ);goto out;}3.2驱动向CPU注册NAPI以e1000e为例子,e1000e在硬中断处理函数中注册NAPI.E1000e芯片可以动态调整硬中断的频率根据收报文率。

网络子系统在链路层的收发过程剖析

网络子系统在链路层的收发过程剖析

2) ,网卡(PCI 设备的注册) 系统启动的时候, pci 会扫描所有的 PCI 设备然后根据注册驱动的 id_table, 找到相匹配 的驱动,实现关联。当找到匹配的驱动时,它会执行相关驱动程序中的 probe 函数,而网卡 的 net_device 就是在这个函数里面初始化的并注册到内核的。
pkt_len,
* The code is rearranged so that the path is the most * short when CPU is congested, but is still operating. */ local_irq_save(flags); queue = &__get_cpu_var(softnet_data); if (queue->input_pkt_queue.qlen <= netdev_max_backlog) { if (queue->input_pkt_queue.qlen) { enqueue: dev_hold(skb->dev); __skb_queue_tail(&queue->input_pkt_queue, skb); local_irq_restore(flags); return NET_RX_SUCCESS; } netif_rx_schedule(&queue->backlog_dev); goto enqueue; } } 这 段 代 码 关 键 是 , 将 这 个 SKB 加 入 到 相 应 的 input_pkt_queue 队 列 中 , 并 调 用 netif_rx_schedule(),而对于 NAPI 方式,它没有使用 input_pkt_queue 队列,而是使用私有的 队列,所以它没有这一个步骤。至此,中断的上半部已经完成,以下的工作则交由中断的下 半部来实现。 void __netif_rx_schedule(struct net_device *dev) { unsigned long flags; local_irq_save(flags); dev_hold(dev); list_add_tail(&dev->poll_list, &__get_cpu_var(softnet_data).poll_list); if (dev->quota < 0) dev->quota += dev->weight; else dev->quota = dev->weight; __raise_softirq_irqoff(NET_RX_SOFTIRQ); local_irq_restore(flags); } netif_rx_schedule()就是将有等待接收数据包的 NIC 链入 softnet_data 的 poll_list 队列,然后 触发软中断,让后半部去完成数据的处理工作。 注意:这里是否调用 netif_rx_schedule()是有条件的,即当 queue->input_pkt_queue.qlen==0

验证设备树定义gpio的方法

验证设备树定义gpio的方法

验证设备树定义gpio的方法全文共四篇示例,供读者参考第一篇示例:在嵌入式系统开发中,设备树(Device Tree)扮演着至关重要的角色。

设备树是一种用于描述硬件设备及其相互连接关系的数据结构,旨在解决硬件描述信息和软件驱动程序之间的脱节问题。

在设备树中,GPIO(General Purpose Input/Output)也是一个重要的概念,通过设备树定义GPIO,可以在内核驱动程序中方便地访问和控制GPIO引脚。

本文将介绍如何验证设备树定义GPIO的方法。

要验证设备树定义GPIO是否正确,需要了解设备树中GPIO的相关信息。

在设备树中,GPIO被定义为一个普通设备,通常包含以下属性:1. GPIO的引脚号:用于指示GPIO连接到的物理引脚号码。

2. GPIO的功能模式:用于指示GPIO的功能,比如输入、输出等。

3. GPIO的电平:用于指示GPIO的当前电平状态。

根据以上信息,可以在设备树中定义一个GPIO节点,例如:```gpio@100 {compatible = "gpio";gpio-controller;#gpio-cells = <2>;reg = <0x100 0x4>;gpio-hog;gpio-hog-state = <1>;gpio-line-names = "example-gpio";};```上述示例中,定义了一个GPIO节点,其引脚号为100,功能模式为输入,电平为高电平。

在定义GPIO节点时,需要确保属性信息与实际硬件连接状态一致。

验证设备树定义GPIO的方法可以分为以下几个步骤:1. 查阅设备树文档:首先需要查阅设备树相关文档,了解设备树中GPIO的定义规范和属性信息。

2. 编译设备树:利用设备树编译工具(如DTC)编译设备树文件,检查编译是否通过,排查可能存在的语法错误或逻辑错误。

3. 加载设备树:将编译通过的设备树文件加载到内核中,可以使用命令行工具(如Device Tree Overlay)加载设备树文件。

hal_uart_receive_it 用法 -回复

hal_uart_receive_it 用法 -回复

hal_uart_receive_it 用法-回复实现透明传输和异步接收数据是许多嵌入式系统中的一个重要需求。

HAL 库中的UART(通用异步收发传输器)驱动提供了一种简单而有效的方法来实现这一目标。

在本文中,我们将详细介绍HAL库中的hal_uart_receive_it函数的用法,并逐步解释其实现过程。

第一节:介绍HAL库和UARTHAL(硬件抽象层)是一个为各种不同的微控制器提供统一的编程接口的库。

它的目的是使开发人员能够更方便地编写可移植的代码,并且使跨不同微控制器平台的代码重用更加容易。

UART是一种常见的串行通信接口,常用于与外部设备(如传感器、模块等)进行数据传输。

在HAL库中,HAL_UART_Receive_IT函数是一个异步接收函数,其中IT 是中断的缩写。

它的作用是启动UART接收并允许将数据存储到一个缓冲区中。

每当接收到新的数据时,将触发一个中断,并可以在中断服务程序中处理接收到的数据。

第二节:使用hal_uart_receive_it函数之前的准备工作在使用hal_uart_receive_it函数之前,我们需要进行一些准备工作。

首先,我们需要初始化UART的硬件引脚和时钟。

在大多数情况下,这需要使用GPIO类进行配置。

其次,我们需要配置UART的波特率、数据位数、停止位数和校验位等参数。

最后,我们需要创建一个缓冲区来存储接收到的数据。

第三节:使用hal_uart_receive_it函数开始UART接收一旦完成了准备工作,我们就可以使用hal_uart_receive_it函数开始UART接收。

该函数的原型如下:HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)其中,huart是一个指向UART_HandleTypeDef结构的指针,该结构包含了与UART相关的各种配置参数和状态信息。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

ptype_base和ptype_all理解,netid_receive_skb()函数注解在数据包接收过程的那篇笔记中可以知道,在数据包的处理函数netif_receive_skb中,会先看ptype_all中是否有注册的协议,如果有,则调用相应的处理函数,然后再到ptype_base中,找到合适的协议,将skb发送到相关协议的处理函数.比如ip协议(ip_rcv)或者arp(arp_rcv)等等.此篇笔记讲的是有关ptype_all和ptype_base的相关知识点.ptype_base和ptype_all在内核中存储的情况如下图:可以看到,ptype_base为一个hash表,而ptype_all为一个双向链表.每一个里面注册的协议都用一个struct packet_type表示.struct packet_type{unsigned short type; /*协议类型*/struct net_device *dev;int (*func) (struct sk_buff *, struct net_device *,struct packet_type *);void *data; /* Private to the packet type */struct packet_type *next;};其中需要注意的是dev参数,此参数表明了协议只处理来自dev指向device的数据,当dev=NULL时,表示该协议处理来自所有device的数据.这样,当注册自己的协议时,就可以指定自己想要监听或者接收的device.其中注册和注销协议的函数为:dev_add_pack(...)和dev_remove_pack(...)这两个函数很简单,分别如下:void dev_add_pack(struct packet_type *pt){int hash;br_write_lock_bh(BR_NETPROTO_LOCK);#ifdef CONFIG_NET_FASTROUTE/* Hack to detect packet socket */if ((pt->data) && ((int)(pt->data)!=1)) {netdev_fastroute_obstacles++;dev_clear_fastroute(pt->dev);}#endifif (pt->type == htons(ETH_P_ALL)) {netdev_nit++;pt->next=ptype_all;ptype_all=pt;} else {hash=ntohs(pt->type)&15;pt->next = ptype_base[hash];ptype_base[hash] = pt;}br_write_unlock_bh(BR_NETPROTO_LOCK);}此函数判断协议类型,然后加到ptype_base或者ptype_all 中.void dev_remove_pack(struct packet_type *pt){struct packet_type **pt1;br_write_lock_bh(BR_NETPROTO_LOCK);if (pt->type == htons(ETH_P_ALL)) {netdev_nit--;pt1=&ptype_all;} else {pt1=&ptype_base[ntohs(pt->type)&15];}for (; (*pt1) != NULL; pt1 = &((*pt1)->next)) {if (pt == (*pt1)) {*pt1 = pt->next;#ifdef CONFIG_NET_FASTROUTEif (pt->data)netdev_fastroute_obstacles--;#endifbr_write_unlock_bh(BR_NETPROTO_LOCK);return;}}br_write_unlock_bh(BR_NETPROTO_LOCK);printk(KERN_WARNING "dev_remove_pack: %p not found.\n", pt);}此函数也很简单,只是把协议从相关的链表中移除.下面以ip协议为例子来看看相关的实现:ip协议结构体的定义如下:static struct packet_type ip_packet_type ={__constant_htons(ETH_P_IP),NULL, /* All devices */ip_rcv,(void*)1,NULL,};当ipv4协议栈初始化时,会调用ip_init.之后,所有协议类型为ETH_P_IP的包都会交由ip_rcv处理.代码如下:void __init ip_init(void){dev_add_pack(&ip_packet_type);ip_rt_init();inet_initpeers();#ifdef CONFIG_IP_MULTICASTproc_net_create("igmp", 0, ip_mc_procinfo);#endif}这样在系统启动之后,ip协议便被注册到ptype_base链表中,相应的处理函数为ip_rcv.arp协议和其他类型的协议(在ptype_base或者ptype_all中的)的执行过程同理.本人初学网络,水平很菜,如有错误,希望看到的朋友们及时指出,不胜感激.ps1:记得刚来实验室的时候,做的截包模块的第一种方法是用的netfilter,第二种方法主要就是用到的这块知识.现在总结起来,觉得还算简单,当初却用了很长时间,想想,真是难者不会,会者不难啊.今天看书的时候,好像又发现了另外一种方法可以实现我的要求,记录在ps2上.ps2:在数据链路层截包的另一种方法:用PF_PACKETsocket type.linux可以用此类型套节字直接从链路层截获或者注入数据.发送数据时,直接发送到dev_queue_xmit.而接收函数时,可以在数据包通过路由之前截获到.如tcpdump和Ethereal都是用到了此套接字.那么总结起来可以看出,截获数据包至少可以有三种方法实现,第一种的netfilter是在协议栈中截获数据包,而利用ptype_all或者ptype_base和后面这种套节字的方法是在链路层截获数据包.//当网络设备收到网络数据包时,最终会在软件中断环境里调用此函数//当网络设备收到网络数据包时,最终会在软件中断环境里调用此函数[cpp] view plaincopyprint?int netif_receive_skb(struct sk_buff *skb){//ptype_all 用于sniffer这样的程序// 发送一份拷贝给这些注册的sniffer程序list_for_each_entry_rcu(ptype, &ptype_all, list) {if (!ptype->dev || ptype->dev == skb->dev) {if (pt_prev)ret = deliver_skb(skb, pt_prev, orig_dev);pt_prev = ptype;}}// 内核编译开Bridge_config,则将该数据包让网桥函数来处理,否则handle_bridge定义为空操作,// 返回skb,让协议栈来处理上层协议。

skb = handle_bridge(skb, &pt_prev, &ret, orig_dev);if (!skb)goto out;skb = handle_macvlan(skb, &pt_prev, &ret, orig_dev);if (!skb)goto out;//对该数据包转达到其他L3协议的处理函数type = skb->protocol;list_for_each_entry_rcu(ptype,&ptype_base[ntohs(type)&15], list) {if (ptype->type == type &&(!ptype->dev || ptype->dev == skb->dev)) {if (pt_prev)ret = deliver_skb(skb, pt_prev, orig_dev);pt_prev = ptype;}}} int netif_receive_skb(struct sk_buff *skb){//ptype_all 用于sniffer这样的程序// 发送一份拷贝给这些注册的sniffer程序list_for_each_entry_rcu(ptype, &ptype_all, list) {if (!ptype->dev || ptype->dev == skb->dev) {if (pt_prev)ret = deliver_skb(skb, pt_prev, orig_dev);pt_prev = ptype;}}// 内核编译开Bridge_config,则将该数据包让网桥函数来处理,否则handle_bridge定义为空操作,// 返回skb,让协议栈来处理上层协议。

skb = handle_bridge(skb, &pt_prev, &ret, orig_dev);if (!skb)goto out;skb = handle_macvlan(skb, &pt_prev, &ret, orig_dev);if (!skb)goto out;//对该数据包转达到其他L3协议的处理函数type = skb->protocol;list_for_each_entry_rcu(ptype,&ptype_base[ntohs(type)&15], list) {if (ptype->type == type &&(!ptype->dev || ptype->dev == skb->dev)) {if (pt_prev)ret = deliver_skb(skb, pt_prev, orig_dev);pt_prev = ptype;}}}netif_receive_skb()的主要作用体现在两个遍历链表的操作中,其中之一为遍历ptype_all 链,这些为注册到内核的一些sniffer,将上传给这些sniffer,另一个就是遍历ptype_base,这个就是具体的协议类型。

相关文档
最新文档