700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > linux 内核 网络设备接口数据结构

linux 内核 网络设备接口数据结构

时间:2020-07-22 22:22:42

相关推荐

linux 内核 网络设备接口数据结构

_device{}结构

网络设备接口使用数据结构net_device,系统中所有网络接口都有一个对应的net_device结构的变量存在。

struct net_device{/** This is the first field of the "visible" part of this structure* (i.e. as seen by users in the "Space.c" file). It is the name* the interface.*//**网络接口名字。在系统中网络接口有独一无二的名称。如果name的第一个字符为NULL,name系统就会在*register_netdev函数中给他分配一个序号,而针对不同类型的名称前缀,整合起来就是整个网络接口的*名称。比如说对以太网接口设备来说,所有以太网接口的前缀都是‘eth’,如果序号为0(就是第一个接*口),name它的名称就是‘eth0’。*/charname[IFNAMSIZ];/**I/O specific fields*FIXME: Merge these and struct ifmap into one*///这些都是IO操作需要的成员变量unsigned longrmem_end;/* shmem "recv" end*/unsigned longrmem_start;/* shmem "recv" start*/unsigned longmem_end;/* shared mem end*/unsigned longmem_start;/* shared mem start*/unsigned longbase_addr;/* device I/O address*/unsigned intirq;/* device IRQ number*//**Some hardware also needs these fields, but they are not*part of the usual set specified in Space.c.*/unsigned charif_port;/* 一般用来区分相同类型的网卡的不同网络类型*/unsigned chardma;/*表示目前在使用的DMA通道,*/unsigned longstate;//网卡的状态struct net_device*next;//构建链表使用的/* The device initialization function. Called only once. *//* 检测网络接口的存在,并且做一些初始化操作。如果这个函数调用不成功,* 表示网络设备不存在,或者没有检测到,如果成功,表示可以正常使用。* 编写网络接口驱动程序的第一步就是实现这个函数指针,用来探测网络接口。*/int(*init)(struct net_device *dev);/* ------- Fields preinitialized in Space.c finish here ------- */struct net_device*next_sched;//指向下一个准备接受调度的网卡接口设备/* Interface index. Unique device identifier*/intifindex;//在系统中网络接口具有唯一的表示索引,使用dev_new_index函数生成这些索引。intiflink;//如果生成了索引,iflink也设置成ifindex的值,否则ifindex为-1,表示该结构没有通过索引进行管理。/*获取网络接口状态的函数指针。这些状态信息主要用于统计接口数据。在系统运行时,网络接口的一些*信息,如接收的字节数、发送的字节数、丢包的数据包个数等等信息,都存放在一个私有的数据结构中*(下面会讲到priv成员),这些数据可以通过netstat -i 命令获取这些信息。因为对于无线连接的接*口,需要测定的状态信息与一般网络接口有很大不同,因此还有get_wireless_stats函数来获取这*些数据信息。*/struct net_device_stats* (*get_stats)(struct net_device *dev);struct iw_statistics*(*get_wireless_stats)(struct net_device *dev);/** This marks the end of the "visible" part of the structure. All* fields hereafter are internal to the system, and may change at* will (read: may be cleaned up at will).*//* These may be needed for future network-power-down code. */unsigned longtrans_start;/* 最后一次发送数据的时间*/unsigned longlast_rx;/* 最后一个接收数据的时间*/unsigned shortflags;/* 网络接口的特性。IFF_LOOPBACK*/unsigned shortgflags;unsignedmtu;/* 是该接口可以接受的MTU值*/unsigned shorttype;/* 接口的媒质类型*/unsigned shorthard_header_len;/* 数据包中硬件头的大小, 16字节 对齐*/void*priv;/* 保存接口私有数据的指针*/struct net_device*master; /* Pointer to master device of a group,* which this device is member of.*//* Interface address info. *///接口的地址信息unsigned charbroadcast[MAX_ADDR_LEN];/* hw bcast add*/unsigned charpad;/* make dev_addr aligned to 8 bytes */unsigned chardev_addr[MAX_ADDR_LEN];/* hw address*/unsigned charaddr_len;/* hardware address length*///多播的Mac地址struct dev_mc_list*mc_list;/* Multicast mac addresses*/intmc_count;/* Number of installed mcasts*/intpromiscuity;//表示该接口是否工作在混杂模式intallmulti;//是否是接收所有的多播数据包intwatchdog_timeo;//监控定时器相关struct timer_listwatchdog_timer;/* Protocol specific pointers *//*与具体协议相关的指针.表示与该网络接口相关的上层处理网络协议是哪个。如果说在IP层如果使用*ipv4的网络协议传输的话,将ip_ptr初始化(in_device结构)*/void *atalk_ptr;/* AppleTalk link */void*ip_ptr;/* IPv4 specific data*/ void*dn_ptr; /* DECnet specific data */void*ip6_ptr; /* IPv6 specific data */void*ec_ptr;/* Econet specific data*///服务质量,对网络传输的排队做了多种排列方法struct Qdisc*qdisc;struct Qdisc*qdisc_sleeping;struct Qdisc*qdisc_list;struct Qdisc*qdisc_ingress;unsigned longtx_queue_len;/* Max frames per queue allowed *//* hard_start_xmit synchronizer */spinlock_txmit_lock;/* cpu id of processor entered to hard_start_xmit or -1,if nobody entered there.*/intxmit_lock_owner;/* device queue lock */spinlock_tqueue_lock;/* Number of references to this device */atomic_trefcnt;/* The flag marking that device is unregistered, but held by an user */intdeadbeaf;/* Net device features */intfeatures;//表示网络接口可能在硬件实现的一些特殊功能#define NETIF_F_SG1/* Scatter/gather IO. */#define NETIF_F_IP_CSUM2/* Can checksum only TCP/UDP over IPv4. */#define NETIF_F_NO_CSUM4/* Does not require checksum. F.e. loopack. */#define NETIF_F_HW_CSUM8/* Can checksum all the packets. */#define NETIF_F_DYNALLOC16/* Self-dectructable device. */#define NETIF_F_HIGHDMA32/* Can DMA to high memory. */#define NETIF_F_FRAGLIST1/* Scatter/gather IO. *//* Called after device is detached from network. */void(*uninit)(struct net_device *dev);/* Called after last user reference disappears. */void(*destructor)(struct net_device *dev);/* Pointers to interface service routines.*/int(*open)(struct net_device *dev);int(*stop)(struct net_device *dev);int(*hard_start_xmit) (struct sk_buff *skb,struct net_device *dev);//发送数据函数指针int(*hard_header) (struct sk_buff *skb,struct net_device *dev,unsigned short type,void *daddr,void *saddr,unsigned len);int(*rebuild_header)(struct sk_buff *skb);#define HAVE_MULTICAST void(*set_multicast_list)(struct net_device *dev);#define HAVE_SET_MAC_ADDR int(*set_mac_address)(struct net_device *dev,void *addr);#define HAVE_PRIVATE_IOCTLint(*do_ioctl)(struct net_device *dev,struct ifreq *ifr, int cmd);#define HAVE_SET_CONFIGint(*set_config)(struct net_device *dev,struct ifmap *map);#define HAVE_HEADER_CACHEint(*hard_header_cache)(struct neighbour *neigh,struct hh_cache *hh);void(*header_cache_update)(struct hh_cache *hh,struct net_device *dev,unsigned char * haddr);#define HAVE_CHANGE_MTUint(*change_mtu)(struct net_device *dev, int new_mtu);#define HAVE_TX_TIMEOUTvoid(*tx_timeout) (struct net_device *dev);int(*hard_header_parse)(struct sk_buff *skb,unsigned char *haddr);int(*neigh_setup)(struct net_device *dev, struct neigh_parms *);int(*accept_fastpath)(struct net_device *, struct dst_entry*);/* open/release and usage marking */struct module *owner;//指向该网络设备使用的模块方式的驱动程序的module结构。/* bridge stuff */struct net_bridge_port*br_port;//网桥的设置#ifdef CONFIG_NET_FASTROUTE#define NETDEV_FASTROUTE_HMASK 0xF/* Semi-private data. Keep it at the end of device struct. */rwlock_tfastpath_lock;struct dst_entry*fastpath[NETDEV_FASTROUTE_HMASK+1];//快速转发的信息#endif#ifdef CONFIG_NET_DIVERT/* this will get initialized at each interface type init routine */struct divert_blk*divert;#endif /* CONFIG_NET_DIVERT */};

_device对应操作函数

注册与注销

注册与注销过程分别通过register_netdev和unregister_netdev函数完成。

register_netdev

/*用来向系统登记网络接口的函数,在这个函数中需要分配给网络接口在系统中惟一的名称,并且将该网络接口*设备添加到系统管理的链表dev_base中进行管理*/int register_netdev(struct net_device *dev){int err;rtnl_lock();//并发控制/**If the name is a format string the caller wants us to*do a name allocation*/if (strchr(dev->name, '%')){err = -EBUSY;/**给该设备分配一个合适的索引名称。基本原理是在当前已经注册的网络接口链表中寻找第一个没有*被分配的适合该网络接口的设备索引号,然后根据这个索引和传入的前缀结合,成为网络接口名*称。*/if(dev_alloc_name(dev, dev->name)<0)goto out;}/**Back compatibility hook. Kill this one in 2.5*///兼容操作,如果第一个字符为0或者空格,就假定传给dev_alloc_name的参数前缀为eth,即以太网卡类型if (dev->name[0]==0 || dev->name[0]==' '){err = -EBUSY;if(dev_alloc_name(dev, "eth%d")<0)goto out;}err = -EIO;if (register_netdevice(dev))//完成这个网络接口添加到dev_base链表中的任务。goto out;err = 0;out:rtnl_unlock();return err;}

register_netdev=>register_netdevice

/** 实际的注册设备过程*/int register_netdevice(struct net_device *dev){struct net_device *d, **dp;#ifdef CONFIG_NET_DIVERTint ret;#endifspin_lock_init(&dev->queue_lock);spin_lock_init(&dev->xmit_lock);dev->xmit_lock_owner = -1;#ifdef CONFIG_NET_FASTROUTEdev->fastpath_lock=RW_LOCK_UNLOCKED;#endif/*判断全局变量dev_boot_phase的值。默认值为1,在系统启动的时候调用net_dev_init已经将变量*设为0了,说明在内核启动初始化的过程已经结束。在为1的情况下才会执行这段代码。在内核启动检测*时,对dev_base中的每一个节点都检测一遍,不存在的网络设备节点删除,不存在在内核启动的方式*下在dev_base中增加节点的情况。为了开发者方便,register_netdev并不是专门为模块方式设计*的。在开发驱动程序时,可以在内核启动检测结束之前,自己增加代码,检测特殊网卡,然后调用*register_netdev将这种新的网络接口设备结构注册。这种情况下才会进入到这段代码。*/if (dev_boot_phase) {#ifdef CONFIG_NET_DIVERTret = alloc_divert_blk(dev);if (ret)return ret;#endif /* CONFIG_NET_DIVERT *//* This is NOT bug, but I am not sure, that all thedevices, initialized before netdev module is startedare sane. Now they are chained to device boot listand probed later. If a module is initializedbefore netdev, but assumes that dev->initis really called by register_netdev(), it will fail.So that this message should be printed for a while.*///打印提前检测网络设备的信息printk(KERN_INFO "early initialization of device %s is deferred\n", dev->name);/* Check for existence, and append to tail of chain *///遍历dev_base链表,检查是否已存在,存在报错。for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) {if (d == dev || strcmp(d->name, dev->name) == 0) {return -EEXIST;}}//插入链表,尾部dev->next = NULL;write_lock_bh(&dev_base_lock);*dp = dev;dev_hold(dev);write_unlock_bh(&dev_base_lock);/**Default initial state at registry is that the*device is present.*/set_bit(__LINK_STATE_PRESENT, &dev->state);//将state置位return 0;}#ifdef CONFIG_NET_DIVERTret = alloc_divert_blk(dev);if (ret)return ret;#endif /* CONFIG_NET_DIVERT */dev->iflink = -1;/* Init, if this function is available */if (dev->init && dev->init(dev) != 0)//调用该接口的init函数来检测和初始化网络接口return -EIO;dev->ifindex = dev_new_index();//初始化接口索引if (dev->iflink == -1)dev->iflink = dev->ifindex;//拷贝索引/* Check for existence, and append to tail of chain *///逻辑和特殊网络设备一样for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) {if (d == dev || strcmp(d->name, dev->name) == 0) {return -EEXIST;}}/**nil rebuild_header routine,*that should be never called and used as just bug trap.*/if (dev->rebuild_header == NULL)dev->rebuild_header = default_rebuild_header;/**Default initial state at registry is that the*device is present.*/set_bit(__LINK_STATE_PRESENT, &dev->state);dev->next = NULL;dev_init_scheduler(dev);write_lock_bh(&dev_base_lock);*dp = dev;dev_hold(dev);dev->deadbeaf = 0;write_unlock_bh(&dev_base_lock);/* Notify protocols, that a new device appeared. *///通知链相关。通知所有的协议,新增加了一个网络设备接口notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);net_run_sbin_hotplug(dev, "register");return 0;}

unregister_netdev

void unregister_netdev(struct net_device *dev){rtnl_lock();unregister_netdevice(dev);//注销一个网络接口设备rtnl_unlock();}

unregister_netdev=>unregister_netdevice

/***用来停止一个网络接口设备的工作,并且把它从dev_base链表中删除*/int unregister_netdevice(struct net_device *dev){unsigned long now, warning_time;struct net_device *d, **dp;/* If device is running, close it first. */if (dev->flags & IFF_UP)//如果这个设备是激活状态,调用dev_close先关闭这个设备dev_close(dev);BUG_TRAP(dev->deadbeaf==0);dev->deadbeaf = 1;/* And unlink it from device chain. */for (dp = &dev_base; (d=*dp) != NULL; dp=&d->next) {//从链表中删除if (d == dev) {write_lock_bh(&dev_base_lock);*dp = d->next;write_unlock_bh(&dev_base_lock);break;}}if (d == NULL) {//不存在,打印错误信息,返回。printk(KERN_DEBUG "unregister_netdevice: device %s/%p never was registered\n", dev->name, dev);return -ENODEV;}/* Synchronize to net_rx_action. */br_write_lock_bh(BR_NETPROTO_LOCK);br_write_unlock_bh(BR_NETPROTO_LOCK);//在dev_boot_phase 为0的情况下才能运行这段代码。表示在启动初始化完成之后才能继续这段注销网卡设备的过程。if (dev_boot_phase == 0) {#ifdef CONFIG_NET_FASTROUTEdev_clear_fastroute(dev);#endif/* Shutdown queueing discipline. */dev_shutdown(dev);//将接口关闭net_run_sbin_hotplug(dev, "unregister");/* Notify protocols, that we are about to destroythis device. They should clean all the things.*///调用notifier_call_chain通知netdev_chain上所有元素该接口已经注销。notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev);/**Flush the multicast chain*/dev_mc_discard(dev);//更新整个多播链表}if (dev->uninit)dev->uninit(dev);//一般是将dev->priv中保存的和系统其他部分数据相关的指针关闭。/* Notifier chain MUST detach us from master device. */BUG_TRAP(dev->master==NULL);#ifdef CONFIG_NET_DIVERTfree_divert_blk(dev);#endif//网卡有NETIF_F_DYNALLOC特性,不要验证refcnt的值,直接将dev空间释放就可以了if (dev->features & NETIF_F_DYNALLOC) {#ifdef NET_REFCNT_DEBUGif (atomic_read(&dev->refcnt) != 1)printk(KERN_DEBUG "unregister_netdevice: holding %s refcnt=%d\n", dev->name, atomic_read(&dev->refcnt)-1);#endifdev_put(dev);return 0;}/* Last reference is our one */if (atomic_read(&dev->refcnt) == 1) {dev_put(dev);//没有引用,直接释放return 0;}#ifdef NET_REFCNT_DEBUGprintk("unregister_netdevice: waiting %s refcnt=%d\n", dev->name, atomic_read(&dev->refcnt));#endif/* EXPLANATION. If dev->refcnt is not now 1 (our own reference)it means that someone in the kernel still has a referenceto this device and we cannot release it."New style" devices have destructors, hence we can return from thisfunction and destructor will do all the work later. As of kernel 2.4.0there are very few "New Style" devices."Old style" devices expect that the device is free of any referencesupon exit from this function.We cannot return from this function until all such references havefallen away. This is because the caller of this function will probablyimmediately kfree(*dev) and then be unloaded via sys_delete_module.So, we linger until all references fall away. The duration of thelinger is basically unbounded! It is driven by, for example, thecurrent setting of sysctl_ipfrag_time.After 1 second, we start to rebroadcast unregister notificationsin hope that careless clients will release the device.*/now = warning_time = jiffies;while (atomic_read(&dev->refcnt) != 1) {//循环将引用计数减一if ((jiffies - now) > 1*HZ) {//1秒检测/* Rebroadcast unregister notification */notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev);}current->state = TASK_INTERRUPTIBLE;schedule_timeout(HZ/4);current->state = TASK_RUNNING;if ((jiffies - warning_time) > 10*HZ) {//10秒检测printk(KERN_EMERG "unregister_netdevice: waiting for %s to ""become free. Usage count = %d\n",dev->name, atomic_read(&dev->refcnt));warning_time = jiffies;}}dev_put(dev);//释放return 0;}

net_device{}基本操作

dev_put

static inline void dev_put(struct net_device *dev){if (atomic_dec_and_test(&dev->refcnt))//引用计数减一netdev_finish_unregister(dev);//注销dev}

dev_put=>netdev_finish_unregister

int netdev_finish_unregister(struct net_device *dev){//保证三个指针都为空BUG_TRAP(dev->ip_ptr==NULL);BUG_TRAP(dev->ip6_ptr==NULL);BUG_TRAP(dev->dn_ptr==NULL);if (!dev->deadbeaf) {//还没有注销部分函数,打印告警,返回printk(KERN_ERR "Freeing alive device %p, %s\n", dev, dev->name);return 0;}#ifdef NET_REFCNT_DEBUGprintk(KERN_DEBUG "netdev_finish_unregister: %s%s.\n", dev->name,(dev->features & NETIF_F_DYNALLOC)?"":", old style");#endifif (dev->destructor)//对于某些新的网卡设备,会初始化他自己的destructor函数指针。特殊处理dev->destructor(dev);if (dev->features & NETIF_F_DYNALLOC)//有NETIF_F_DYNALLOC特性,直接释放kfree(dev);return 0;}

通知链

在系统中网络接口的设备的数据做更新的时候,会将netdev_chain链表中的所有的元素都通知到。实现通知的方法是通知链。这里分析netdev_chain通知链。

系统中调用notifier_call_chain函数完成通知的过程,系统中调用函数register_netdevice_notifier更新netdev_chain链表的内容。这里介绍和路由相关的一些东西。fib_netdev_notifier元素的添加和更新。

struct notifier_block fib_netdev_notifier = {fib_netdev_event,NULL,0};void __init ip_fib_init(void){#ifdef CONFIG_PROC_FSproc_net_create("route",0,fib_get_procinfo);#endif/* CONFIG_PROC_FS */#ifndef CONFIG_IP_MULTIPLE_TABLESlocal_table = fib_hash_init(RT_TABLE_LOCAL);main_table = fib_hash_init(RT_TABLE_MAIN);#elsefib_rules_init();#endifregister_netdevice_notifier(&fib_netdev_notifier);//将fib_netdev_notifier注册到netdev_chain中register_inetaddr_notifier(&fib_inetaddr_notifier);}

ip_fib_init=>register_netdevice_notifier

int register_netdevice_notifier(struct notifier_block *nb){return notifier_chain_register(&netdev_chain, nb);}

ip_fib_init=>register_netdevice_notifier=>notifier_chain_register

int notifier_chain_register(struct notifier_block **list, struct notifier_block *n){write_lock(&notifier_lock);while(*list){if(n->priority > (*list)->priority)//根据优先级添加到通知链中break;list= &((*list)->next);}n->next = *list;*list=n;write_unlock(&notifier_lock);return 0;}

notifier_call_chain

int notifier_call_chain(struct notifier_block **n, unsigned long val, void *v){int ret=NOTIFY_DONE;struct notifier_block *nb = *n;while(nb)//循环遍历链表中的所有元素{ret=nb->notifier_call(nb,val,v);//调用每个元素的notifier_call函数指针,完成对本节点对应于通知的动作。if(ret&NOTIFY_STOP_MASK)//如果返回值中设置了NOTIFY_STOP_MASK,停止循环返回。{return ret;}nb=nb->next;}return ret;}

notifier_call_chain=>fib_netdev_event

static int fib_netdev_event(struct notifier_block *this, unsigned long event, void *ptr){struct net_device *dev = ptr;//对应于net_device结构的指针struct in_device *in_dev = __in_dev_get(dev);if (!in_dev)return NOTIFY_DONE;switch (event) {//通知事件case NETDEV_UP://如果接口启动,对该dev对应的in_device结构增加对应的fib项,更新路由缓存for_ifa(in_dev) {fib_add_ifaddr(ifa);} endfor_ifa(in_dev);#ifdef CONFIG_IP_ROUTE_MULTIPATHfib_sync_up(dev);#endifrt_cache_flush(-1);break;case NETDEV_DOWN://删除fib项fib_disable_ip(dev, 0);break;case NETDEV_UNREGISTER://删除fib项fib_disable_ip(dev, 1);break;case NETDEV_CHANGEMTU:case NETDEV_CHANGE:rt_cache_flush(0);break;}return NOTIFY_DONE;}

这里在NETDEV_REGISTER通知的时候,对fib_netdev_notifier并没有对应的事件发生,后面分析dev_open时可以知道,在dev_open的时候,会通知fib_netdev_notifier一个NETDEV_UP事件,更新fib项。

打开和关闭函数

dev_open

/***对所有网络接口设备打开的通用过程 */int dev_open(struct net_device *dev){int ret = 0;/**Is it already up?*/if (dev->flags&IFF_UP)//已经打开,直接返回return 0;/**Is it even present?*/if (!netif_device_present(dev))//设备是否存在return -ENODEV;/**Call device private open method*/if (try_inc_mod_count(dev->owner)) {if (dev->open) {ret = dev->open(dev);if (ret != 0 && dev->owner)//open失败,返回__MOD_DEC_USE_COUNT(dev->owner);}} else {ret = -ENODEV;}/**If it went open OK then:*/if (ret == 0) //open 成功{/**Set the flags.*/dev->flags |= IFF_UP;//设置up标志set_bit(__LINK_STATE_START, &dev->state);//设置可以进行传输的状态/**Initialize multicasting status */dev_mc_upload(dev);//初始化多播的地址列表/**Wakeup transmit queue engine*/dev_activate(dev);//激活该设备上进行传输的数据包队列/**... and announce new interface.*///发送NETDEV_UP通知时间,表示设备打开动作完成。与前面对应notifier_call_chain(&netdev_chain, NETDEV_UP, dev);}return(ret);}

dev_close

int dev_close(struct net_device *dev){if (!(dev->flags&IFF_UP))//已经关闭,返回return 0;/**Tell people we are going down, so that they can*prepare to death, when device is still operating.*/notifier_call_chain(&netdev_chain, NETDEV_GOING_DOWN, dev);dev_deactivate(dev);clear_bit(__LINK_STATE_START, &dev->state);/**Call the device specific close. This cannot fail.*Only if device is UP**We allow it to be called even after a DETACH hot-plug*event.*/if (dev->stop)dev->stop(dev);/**Device is now down.*/dev->flags &= ~IFF_UP;//清除IFF_UP标志#ifdef CONFIG_NET_FASTROUTEdev_clear_fastroute(dev);#endif/**Tell people we are down*/notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);//发送NETDEV_DOWN通知事件/** Drop the module refcount*/if (dev->owner)__MOD_DEC_USE_COUNT(dev->owner);//如果网络接口是通过模块方式启动的,那么需要将这个模块的使用计数减一。return(0);}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。