1.net_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’。*/char         name[IFNAMSIZ];/**  I/O specific fields*    FIXME: Merge these and struct ifmap into one*///这些都是IO操作需要的成员变量unsigned long        rmem_end;   /* shmem "recv" end   */unsigned long     rmem_start; /* shmem "recv" start */unsigned long     mem_end;    /* shared mem end   */unsigned long     mem_start;  /* shared mem start */unsigned long     base_addr;  /* device I/O address   */unsigned int      irq;        /* device IRQ number    *//**   Some hardware also needs these fields, but they are not*    part of the usual set specified in Space.c.*/unsigned char      if_port;    /* 一般用来区分相同类型的网卡的不同网络类型*/unsigned char      dma;        /*表示目前在使用的DMA通道,     */unsigned long     state;//网卡的状态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    */int           ifindex;//在系统中网络接口具有唯一的表示索引,使用dev_new_index函数生成这些索引。int          iflink;//如果生成了索引,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 long     trans_start;    /* 最后一次发送数据的时间*/unsigned long       last_rx;    /* 最后一个接收数据的时间  */unsigned short        flags;  /* 网络接口的特性。IFF_LOOPBACK */unsigned short        gflags;unsigned     mtu;    /* 是该接口可以接受的MTU值        */unsigned short        type;   /* 接口的媒质类型  */unsigned short        hard_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 char      broadcast[MAX_ADDR_LEN];    /* hw bcast add */unsigned char     pad;        /* make dev_addr aligned to 8 bytes */unsigned char     dev_addr[MAX_ADDR_LEN]; /* hw address   */unsigned char     addr_len;   /* hardware address length  *///多播的Mac地址struct dev_mc_list  *mc_list;   /* Multicast mac addresses  */int           mc_count;   /* Number of installed mcasts   */int           promiscuity;//表示该接口是否工作在混杂模式int         allmulti;//是否是接收所有的多播数据包int         watchdog_timeo;//监控定时器相关struct timer_list   watchdog_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 long        tx_queue_len;   /* Max frames per queue allowed *//* hard_start_xmit synchronizer */spinlock_t      xmit_lock;/* cpu id of processor entered to hard_start_xmit or -1,if nobody entered there.*/int         xmit_lock_owner;/* device queue lock */spinlock_t       queue_lock;/* Number of references to this device */atomic_t        refcnt;/* The flag marking that device is unregistered, but held by an user */int           deadbeaf;/* Net device features */int           features;//表示网络接口可能在硬件实现的一些特殊功能
#define NETIF_F_SG      1   /* Scatter/gather IO. */
#define NETIF_F_IP_CSUM     2   /* Can checksum only TCP/UDP over IPv4. */
#define NETIF_F_NO_CSUM     4   /* Does not require checksum. F.e. loopack. */
#define NETIF_F_HW_CSUM     8   /* Can checksum all the packets. */
#define NETIF_F_DYNALLOC    16  /* Self-dectructable device. */
#define NETIF_F_HIGHDMA     32  /* Can DMA to high memory. */
#define NETIF_F_FRAGLIST    1   /* 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_t     fastpath_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 */
};

2.net_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);
}

linux 内核 网络设备接口数据结构相关推荐

  1. Linux内核网络设备驱动

    本文首先从宏观上介绍数据包的接收过程,然后详细介绍了Linux网络设备驱动的工作过程,最后介绍网卡监控与调优,包括网络数据包总数.丢包.错包数量的相关统计. 1. 接收数据包过程概述 介绍数据包收包过 ...

  2. 深入讲解Linux内核网络设备驱动(图例解析)

    1. 接收数据包过程概述 介绍数据包收包过程,有助于我们了解Linux内核网络设备在数据收包过程中的位置,下面从宏观的角度介绍数据包从被网卡接收到进入 socket 接收队列的整个过程: 加载网卡驱动 ...

  3. Linux 内核中的数据结构:双链表,基数树,位图

    Linux 内核中的数据结构 rtoax 2021年3月 1. 双向链表 Linux 内核自己实现了双向链表,可以在 include/linux/list.h 找到定义.我们将会从双向链表数据结构开始 ...

  4. Linux 内核里的数据结构——基数树

    Linux 内核里的数据结构--基数树 正如你所知道的,Linux内核提供了许多不同的库和函数,它们实现了不同的数据结构和算法.在这部分,我们将研究其中一种数据结构--基数树Radix tree.在 ...

  5. Linux内核编程接口函数

    Linux内核编程接口函数 转载请注明出处: http://blog.csdn.net/drivelinux/article/details/8656280 字符设备相关函数 1.alloc_chrd ...

  6. i.MX6ULL驱动开发 | 31 - Linux内核网络设备驱动框架

    一.Linux网络设备驱动整体架构 网络设备是完成用户数据包在网络媒介上发送和接收的设备,它将上层协议传递下来的数据包,以特定的媒介访问控制方式进行发送,并将接收到的数据包传递给上层协议. Linux ...

  7. linux内核中的数据结构

    http://vinllen.com/linuxnei-he-zhong-de-shu-ju-jie-gou/ https://zhuanlan.zhihu.com/p/58087261 https: ...

  8. Linux 内核完成接口

    Linux 内核里面有一个函数wait_for_completion,这是一个内核同步机制的函数,同步机制如果是早期的读者应该看过我发的文章,如果没有看过的可以看看Linux 专辑文章里面找找. 既然 ...

  9. 计算机原理-操作系统- 转发 微博 Qzone 微信 Linux内核中的数据结构和算法

    原创 底层软件架构 2019-07-12 22:40:12 Linux内核(源代码的链接在github) 1.链表.双向链表.无锁链表. 2.B+ 树,这是一些你无法在教科书上找到的说明. 一个相对简 ...

最新文章

  1. java用户的登录图片_Java 如何用 token 做用户登录认证
  2. Spring模板对象
  3. -------------初识----------动态规划。--------------------------------------------
  4. php orm 内存泄漏,Lavarel Eloquent ORM常驻进程下的内存溢出问题
  5. HDU1108 最小公倍数【欧几里得算法】
  6. 安装程序检测到无法验证文件的发行者_文件的校验方法
  7. 麻省理工18年春软件构造课程阅读08“可变性与不变性”
  8. WIN10环境JAVA的JDK环境变量设置教程
  9. 浙江新华书店的数字化新尝试
  10. Few-shot transfer learning for intelligent fault diagnosis of machine(机器智能故障诊断中的小样本迁移学习)
  11. 反问疑问_反问、疑问还是设问?
  12. 现代电商会员管理新玩法——付费会员
  13. 2020华为软件精英挑战赛历程总结——复赛篇
  14. Profibus网络故障诊断技术总结
  15. 续谈大数据之足球盘口赔率水位分析思路及其实现利器
  16. 网站域名备案需要多长时间?个人经验分享
  17. 上计算机课如何摆脱桌面控制,计算机课如何摆脱老师的控制
  18. Quartz cron表达式格式
  19. EHT 开发框架 Form 表单动态绑定的实现
  20. 【 Failure [INSTALL_FAILED_SHARED_USER_INCOMPATIBLE】

热门文章

  1. 耀眼的天才——Aaron Swartz提高效率的秘诀
  2. 以工程化路径破题,中国系统推动数据要素化
  3. F28335 - bootloader升级相关cmd配置
  4. 国外新闻网站发稿有多重要?这些步骤和注意事项要知道
  5. 1.1.5 蚂蚁。一根长度为L厘米的木棍上有n只蚂蚁,每只蚂蚁要么朝左爬,要么朝右爬,速度为1厘米/秒。
  6. 神探Python程序员,带你千里捉小三!
  7. 美国次贷危机如何形成的。
  8. ibatis mysql iterate_IBATIS的iterate标签
  9. c++的 trivial constructor
  10. 用心谱写爱情经典将这些字符编写成只属于80后