Linux网络核心数据结构是套接字缓存(socket buffer),简称skb。它代表一个要发送或处理的报文,并贯穿于整个协议栈。1、套接字缓存skb由两部分组成:(1)报文数据:它保存了实际在网络中传输的数据;(2)管理数据:供内核处理报文的额外数据,这些数据构成了协议之间交换的控制信息。发送网络数据包:当应用程序向一个socket传输数据之后,该socket将创建相应的套接字缓存,并将用户数据拷贝到缓存中。当报文在各协议层传达输的过程中,每一导的报文头将插入到用户数据之前。相应的,在sk_buff结构中描述协议头信息的地址指针会被赋值。

接收网络数据包:网络适配器接收到发送给本机的网络数据后,首先产生中断通知内核收到网络数据帧。接着在网络适配器的中断处理程序中调用dev_alloc_skb函数向系统申请一个Socket Buffer,将接收到的网络数据帧从设备硬件的缓冲区复制到Socket Buffer的数据包缓冲区;填写sk_buff结构中的地址、接收时间和协议等。

skb为报文头申请了足够的空间,所以避免了由于插入报文头而对报文进行多次拷贝。用户数据只拷贝了两次:一是从用户空间拷贝到内核;二是报文数据从内核传送到网络适配器。

2.Sk_buff数据域分类

@结构管理

@常规数据域

@网络功能配置相关域

2.1.1 sk_buff的结构管理域

1.*next和*prev

sk_buff是一个复杂的双向链表,在他结构中有next和prev指针,分别指向链表的下一个节点和前一个节点。并且为了某些需求(不知道是哪些目前)需要很快定位到链表头部,所以还有一个指向链表头部的指针list(我在2.6.25内核没有发现这个指针)。

sk_buff_head结构是:

struct sk_buff_head {

/* These two members must be first. */

struct sk_buff *next;

struct sk_buff *prev;

__u32 qlen; //代表元素节点数目

spinlock_t lock; //加锁,防止对表的并发访问

};

2.struct sock *sk

这个指针指向一个套接字sock数据结构。当数据在本地产生或者本地进程接受时,需要这个指针;里面的数据会有tcp/udp和用户态程序使用。如果是转发此指针为NULL

3.unsigned int len

缓冲区中数据块大小。长度包括:主要缓冲区(head所指)的数据以及一些片断(fragment)的数据。当包在协议栈向上或向下走时,其大小会变,因为有头部的丢弃和添加。

unsigned int data_len

片段中数据大小

unsigned int mac_len

mac包头大小

atomic_t users

引用计数,使用这个sk_buff的使用者的数目,可能有多个函数要使用同一个sk_buff所以防止提前释放掉,设置此计数

unsigned int truesize

此缓冲区总大小,包括sk_buff。sk_buff只不过是个指针的集合,他所指的才是真正的数据区,所以是两部分(sk_buff数据结构的长度和数据包的长度和)。(见下图)

sk_buff_data_t tail;

sk_buff_data_t end;

unsigned char *head, *data;

这些指针很重要,他们指向的是真正的数据区,他们的边界。head和end指向的是数据区的开端和尾端,指向整个数据包缓冲区的起始和结束地址,data和tail指向的是实际数据的开头和结尾。

因为数据区在协议栈走的时候要一层层添加或去掉一些数据(比如报头)所以申请一块大的足够的内存,然后在往里放东西。真实的实际数据可能用不了这么多,所以用data,tail指向真实的,head,tail指向边界。刚开始没填充数据时前三个指针指向的是一个地方。

void (*destructor) (…….)

此函数指针被初始化一个函数,当此缓冲区删除时,完成某些工作。

2.1.2常规数据域

1.struct ktime_t tstamp

描述接收数据包到达内核的时间。网络设备驱动程序收到网络数据时调用接收数据包处理函数netif_rx,再调用net_timestamp(skb)对该数据域赋值。

时间戳,表示何时被接受或有时表示包预定的传输时间

2.struct net_device *dev

dev是指向代表设备数据结构的指针,表明该数据包是通过那个网络设备接收或传送的。

3.

sk_buff_data_t transport_header; //L4传输层协议头在网络数据包中的地址;

sk_buff_data_t network_header; //L3网络层协议头在网络数据包中的地址;

sk_buff_data_t mac_header; //L2数据链路层协议头在网络数据包中的地址;

如果上述的协议头在网络数据包中的地址以skb->head为起始的偏移量(64位的CPU体系结构),否则sk_buff_data_t的类型为指针(32位的CPU体系结构),存放各协议层在网络数据包中的起始地址。

这些指针分别指向报文头部,和2.4版本比较有了变化,不再是联合体,使用更加方便了,Linux给出了很方便的函数直接定位到各层的头部。下图是2.4版本的,只是说明一下。

4 union

{

.  structdst_entry *dst;

Structrtable   *rtable;

};

路由子系统使用。该数据域指明如何将数据包向前发送。其中存放了数据包的发送目标IP地址等。

5.char cb[48]

缓冲控制区,用来存储私有信息的空间。比如tcp/udp用这个空间存储一个结构体tcp_skb_cb/udp_skb_cp ,可以用宏TCP_SKB_CB(__skb)/UDP_SKB_CB(_skb)定位到他,然后使用里面的变量。

6.csum

_u8 Ip_summed

Cusm域存放数据包的校验和,检验数据包在接收或发送过程中是否有损坏。其中必须包含csum_start/csum_offset对。

csum_start:以skb_head为起始地址的偏移量。指出检验和从数据的什么位置开始计算。

csum_offset:以csum_start为起始地址的偏移量,指明检验和存放的位置。

Ip_summed 描述网络设备是否可用硬件对IP数据进行检验编码或解码。

7.unsigned char pkt_type

数据包的类型根据L2层帧的目的地址进行类型划分。

8.fclone:sk_buff克隆类别。

9.__u32 priority; priority数据域实现质量服务QoS功能特性。

10.QoS等级:描述数据包传送的优先级别。

11.__be16 protocol;

接收数据包的网络层协议,表明网络数据包应该传给TCP/IP协议栈网络层的哪个协议处理函数。(include/linux/if_ether.h)

12._u16 queue_mapping:描述发送网络数据包的所在队列与设备硬件发送队列的映射关系。

13._32 mark:数据包为常规数据包的标志。

14._u16 vlan_tci:虚拟局域网的标记控制信息。、

15.struct sec_path *sp:IPsec协议跟踪网络数据包的传送路径。

2.1.3 Sk_buff的网络功能配置域

操作sk_buff相关的函数(net/core/skbuff.c和incldue/linux/skbuff.h)与sk_buff相关的函数涉及到网络报文存储结构和控制结构的分配、复制、释放,以及控制结构里的各指针的操作,还有各种标志的检查。重要的函数说明如下:内核在系统初始化时创建两个sk_buff的内存对象池。Skbuff_head_cache和skbuff_fclone_cache。1.(1)_alloc_skb(unsigned int size,int gfp_t gfp_mask,int fclone,int node)(skbuff.c)分配大小为size的存储空间存放网络报文,同时分配它的控制结构。size的值是16字节对齐的,gfp_mask是内存分配的优先级。常见的内存分配优先级有GFP_ATOMIC,代表分配过程不能被中断,一般用于中断上下文中分配内存;GFP_KERNEL,代表分配过程可以被中断,相应的分配请求被放到等待队列中。fclone为1,表示从内存对象池skbuff_fclone_cache中获取sk_buff数据结构所需的内存空间,为0,则从内存对象池Skbuff_head_cache。

alloc_skb(unsigned int size,gfp_t priority)和alloc_skb_fclone(unsigned int size,gfp_t priority)是_alloc_skb的包装函数,前者fclone=0,后者fclone=1

(2)dev_alloc_skb(unsigned int lenth)(skbuff.c)

_dev_alloc_skb(unsigned int lenth,gfp_t gfp_mask)

_dev_alloc_skb是给网络设备驱动程序使用的函数,当网络设备从网络上收到一个数据包时,它调用该函数向系统申请缓冲区来存放数据包,调用alloc_skb来分配SocketBuffer。dev_alloc_skb是_dev_alloc_skb的包装函数。由于网络数据包是中断处理程序中接收的,中断不能休眠,dev_alloc_skb用GFP_ATOMIC标志传给_dev_alloc_skb来分配内存。

(3)netdev_alloc_skb(struct net_decice *dev,unsigned int lenth)

_netdev_alloc_skb(struct net_decice *dev,unsigned int lenth,gfp_tgfp_mask)

_netdev_alloc_skb与_dev_alloc_skb类似,指定了接收数据包的网络设备用_alloc_skb来分配Socket Buffer,返回分配的sk_buff前,初始化sk_buff的设备指针*dev域。同上面一样,netdev_alloc_skb是包装函数。2.struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask)

当同一个scoket buffer由不同的进程独立处理,但进程只操作sk_buff数据结构描述符,内核只对sk_buff数据结构做完全复制。从控制结构skb中clone出一个新的控制结构,它们都指向同一个网络报文。clone成功之后,将新的控制结构和原来的控制结构的is_clone,cloned两个标记都置位。同时还增加网络报文的引用计数(这个引用计数存放在存储空间的结束地址的内存中,由函数atomic_t *skb_datarefp(struct sk_buff *skb)访问,引用计数记录了这个存储空间有多少个控制结构)。由于存在多个控制结构指向同一个存储空间的情况,所以在修改存储空间里面的内容时,先要确定这个存储空间的引用计数为1,或者用下面的拷贝函数复制一个新的存储空间,然后才可以修改它里面的内容。

克隆的sk_buff具有以下特点:不放入任何的sk_buff的管理队列,不属于任何套接字,两个sk_buff结构的skb->cloned域都置为1,当一个sk_buff被克隆后,它的数据包的值为只读。

3. 多个进程对同一个scoket buffer修改sk_buff数据结构中的内容,也要修改数据包内容,对scoket buffer进行复制。struct sk_buff *skb_copy(struct sk_buff *skb, int gfp_mask)

既修改主数据包的内容,又要操作分片数据的值。

struct sk_buff *pskb_copy(struct sk_buff *skb, int gfp_mask)

只修改主数据包的内容复制控制结构skb和它所指的存储空间的内容。复制成功之后,新的控制结构和存储空间与原来的控制结构和存储空间相对独立。所以新的控制结构里的is_clone,cloned两个标记都是0,而且新的存储空间的引用计数是1。4.void kfree_skb(struct sk_buff *skb)释放控制结构skb和它所指的存储空间。由于一个存储空间可以有多个控制结构,所以只有在存储空间的引用计数为1的情况下才释放存储空间,一般情况下,只释放控制结构skb。

Kfree_release_all:释放与sk_buff相连的其他数据结构的引用计数。

Kfree_release_data:释放数据包的主缓冲区和数据片缓冲区。kfree_skbmem:将sk_buff数据结构返回内存对象池。

dst_release:释放对路由表的引用。5.unsigned char *skb_put(struct sk_buff *skb, unsigned int len)将tail指针下移,并增加skb的len值。data和tail之间的空间就是可以存放网络报文的空间。这个操作增加了可以存储网络报文的空间,但是增加不能使tail的值大于end的值,skb的len值大于truesize的值。unsigned char *skb_push(struct sk_buff *skb, unsigned int len)将data指针上移,并增加skb的len值。这个操作在存储空间的头部增加了一段可以存储网络报文的空间,上一个操作在存储空间的尾部增加了一段可以存储网络报文的空间。但是增加不能使data的值小于head的值,skb的len值大于truesize的值。unsigned char * skb_pull(struct sk_buff *skb, unsigned int len)将data指针下移,并减小skb的len值。这个操作使data指针指向下一层网络报文的头部。void skb_reserve(struct sk_buff *skb, unsigned int len)将data指针和tail指针同时下移。这个操作在存储空间的头部预留len长度的空隙。void skb_trim(struct sk_buff *skb, unsigned int len)将网络报文的长度缩减到len。这个操作丢弃了网络报文尾部的填充值。6.int skb_cloned(struct sk_buff *skb)判断skb是否是一个clone的控制结构。如果是clone的,它的cloned标记是1,而且它指向的存储空间的引用计数大于1。7.sk_buff引用的计数加1:static inline strcut sk_buff *skb_get(struct sk_buff *skb)

分片数据引用计数加1:static void skb_clone_fraglist(struct sk_buff *skb)

8.数据分片和分段

Strcut skb_shared_info数据结构。3、套接字缓存队列(Socket-Buffer Queues)

3.1、sk_buff_head在网络协议栈的实现中,有时需要把许多网络报文放到一个队列中做异步处理。LINUX 为此定义了相关的数据结构sk_buff_head。这是一个双向链表的头,它把sk_buff链接成一个双向链表。

//套接字缓存队列头structsk_buff_head {/* These twomembers must be first. */structsk_buff *next;structsk_buff *prev;

__u32 qlen;//队列的长度,即队列中报文的数量spinlock_tlock;

};

3.2、与sk_buff_head相关的函数void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)将newsk加到链表list的头部。void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)将newsk加到链表list的尾部。struct sk_buff *skb_dequeue(struct sk_buff_head *list)从链表list的头部取下一个sk_buff。struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list)从链表list的尾部取下一个sk_buff。skb_insert(struct sk_buff *old, struct sk_buff *newsk)将newsk加到old所在的链表上,并且newsk在old的前面。void skb_append(struct sk_buff *old, struct sk_buff *newsk)将newsk加到old所在的链表上,并且newsk在old的后面。void skb_unlink(struct sk_buff *skb)将skb从它所在的链表上取下。以上的链表操作都是先关中断的。这在中断上下文中是不需要的,所以另外有一套与上面函数同名但是有前缀“__”的函数供运行在中断上下文中的函数调用。

功能专用字段

Linux是模块化的,你编译时可以带上特定功能,比如netfilter等,相应的字段才会生效。应该是那些预定义控制的。

管理函数

下面这个图是:(a*)skb_put; (b*) skb_push; (c*)skb_pull (d*) skb_reserve的使用,主要是对skb_buf所指向的数据区的指针移动。(数据预留以及对齐)

下图是用skb_reserve函数,把一个14字节的ethernet帧拷贝到缓冲区。skb_reserve(skb, 2), 2表示16字节对齐。14+2=16

下图是穿过协议栈从tcp层向下到链路层的过程

分配内存:

alloc_skb 分配缓冲区和一个sk_buff结构

dev_alloc_skb 设备驱动程序使用的缓冲区分配函数

释放内存:

kfree_skb 只有skb->users计数器为1时才释放

dev_kfree_skb

缓冲区克隆函数 skb_clone

列表管理函数:

skb_queue_head_init

队列初始化

skb_queue_head , skb_queue_tail

把一个缓冲区添加到队列头或尾

skb_dequeue, skb_dequeue_tail

从头或尾去掉

skb_queue_purge

把队列变空

skb_queue_walk

循环队列每个元素

内核也新增了几个函数,来提供获取这些偏移的接口:

#ifdefNET_SKBUFF_DATA_USES_OFFSET如果使用了offset来表示偏移的话,就是说是一个相对偏移的情况:staticinline unsignedchar*skb_transport_header(conststructsk_buff *skb)

{returnskb->head + skb->transport_header;

}staticinline voidskb_reset_transport_header(structsk_buff *skb)

{

skb->transport_header = skb->data - skb->head;

}staticinline voidskb_set_transport_header(structsk_buff *skb,constintoffset)

{

skb_reset_transport_header(skb);

skb->transport_header += offset;

}staticinline unsignedchar*skb_network_header(conststructsk_buff *skb)

{returnskb->head + skb->network_header;

}staticinline voidskb_reset_network_header(structsk_buff *skb)

{

skb->network_header = skb->data - skb->head;

}staticinline voidskb_set_network_header(structsk_buff *skb,constintoffset)

{

skb_reset_network_header(skb);

skb->network_header += offset;

}staticinline unsignedchar*skb_mac_header(conststructsk_buff *skb)

{returnskb->head + skb->mac_header;

}staticinline intskb_mac_header_was_set(conststructsk_buff *skb)

{returnskb->mac_header != ~0U;

}staticinline voidskb_reset_mac_header(structsk_buff *skb)

{

skb->mac_header = skb->data - skb->head;

}staticinline voidskb_set_mac_header(structsk_buff *skb,constintoffset)

{

skb_reset_mac_header(skb);

skb->mac_header += offset;

}#else/* NET_SKBUFF_DATA_USES_OFFSET */不使用相对偏移的情况staticinline unsignedchar*skb_transport_header(conststructsk_buff *skb)

{returnskb->transport_header;

}staticinline voidskb_reset_transport_header(structsk_buff *skb)

{

skb->transport_header = skb->data;

}staticinline voidskb_set_transport_header(structsk_buff *skb,constintoffset)

{

skb->transport_header = skb->data + offset;

}staticinline unsignedchar*skb_network_header(conststructsk_buff *skb)

{returnskb->network_header;

}staticinline voidskb_reset_network_header(structsk_buff *skb)

{

skb->network_header = skb->data;

}staticinline voidskb_set_network_header(structsk_buff *skb,constintoffset)

{

skb->network_header = skb->data + offset;

}staticinline unsignedchar*skb_mac_header(conststructsk_buff *skb)

{returnskb->mac_header;

}staticinline intskb_mac_header_was_set(conststructsk_buff *skb)

{returnskb->mac_header != NULL;

}staticinline voidskb_reset_mac_header(structsk_buff *skb)

{

skb->mac_header = skb->data;

}staticinline voidskb_set_mac_header(structsk_buff *skb,constintoffset)

{

skb->mac_header = skb->data + offset;

}#endif/* NET_SKBUFF_DATA_USES_OFFSET */1、TCP层获取相关偏移的函数staticinline structtcphdr *tcp_hdr(conststructsk_buff *skb)

{return(structtcphdr *)skb_transport_header(skb);

}这个函数用来获得sk_buff结构中TCP头的指针staticinline unsignedinttcp_hdrlen(conststructsk_buff *skb)

{returntcp_hdr(skb)->doff *4;

}这个函数用来获得TCP头的长度staticinline unsignedinttcp_optlen(conststructsk_buff *skb)

{return(tcp_hdr(skb)->doff -5) *4;

}获取tcp option的长度2、IP相关的函数staticinline structiphdr *ip_hdr(conststructsk_buff *skb)

{return(structiphdr *)skb_network_header(skb);

}该函数获得ip头staticinline structiphdr *ipip_hdr(conststructsk_buff *skb)

{return(structiphdr *)skb_transport_header(skb);

}该函数获得ipip头,实际上偏移已经跑到了传输层的开始3、MAC相关函数staticinline structebt_802_3_hdr *ebt_802_3_hdr(conststructsk_buff *skb)

{return(structebt_802_3_hdr *)skb_mac_header(skb);

}获取802.3MAC头指针。staticinline structethhdr *eth_hdr(conststructsk_buff *skb)

{return(structethhdr *)skb_mac_header(skb);

}获取以太网MAC头指针。以太网头指针结构体:structethhdr {

unsignedcharh_dest[ETH_ALEN];/* destination eth addr */unsignedcharh_source[ETH_ALEN];/* source ether addr */__be16 h_proto;/* packet type ID field */} __attribute__((packed));内核中网络地址转化为字符串形式的IP地址的宏定义:#defineNIPQUAD(addr) \

((unsignedchar*)&addr)[0], \

((unsignedchar*)&addr)[1], \

((unsignedchar*)&addr)[2], \

((unsignedchar*)&addr)[3]#defineNIPQUAD_FMT "%u.%u.%u.%u"

2.2 sk_buff套接字缓存结构:

代码:

//套接字缓存structsk_buff {/* These two members must be first. */structsk_buff *next;structsk_buff *prev;structsk_buff_head *list;structsock *sk;//指向创建报文的socketstructtimeval stamp;//此报文收到时的时间structnet_device *dev;//收到此报文的网络设备structnet_device *input_dev;structnet_device *real_dev;//TCP报头union {structtcphdr *th;//tcp头structudphdr *uh;//udp头structicmphdr *icmph;structigmphdr *igmph;structiphdr *ipiph;structipv6hdr *ipv6h;

unsignedchar*raw;

} h;//IP报头union {structiphdr *iph;structipv6hdr *ipv6h;structarphdr *arph;

unsignedchar*raw;

} nh;//链路层帧头union {

unsignedchar*raw;

} mac;structdst_entry *dst;//此报文的路由,路由确定后赋此值structsec_path *sp;/*

* This is the control buffer. It is free to use for every

* layer. Please put your private variables there. If you

* want to keep them across layers you have to do a skb_clone()

* first. This is owned by whoever has the skb queued ATM.

*/charcb[40];//此报文的长度,这是指网络报文在不同协议层中的长度,包括头部和数据。在协议栈的不同层,这个长度是不同的。unsignedintlen,

data_len,

mac_len,

csum;

unsignedcharlocal_df,

cloned,

pkt_type,//网络报文的类型,常见的有PACKET_HOST,代表发给本机的报文;还有PACKET_OUTGOING,代表本机发出的报文。ip_summed;

__u32 priority;

unsignedshortprotocol,//链路层协议security;void(*destructor)(structsk_buff *skb);

#ifdef CONFIG_NETFILTER

unsignedlongnfmark;

__u32 nfcache;

__u32 nfctinfo;structnf_conntrack *nfct;

#ifdef CONFIG_NETFILTER_DEBUG

unsignedintnf_debug;#endif#ifdef CONFIG_BRIDGE_NETFILTERstructnf_bridge_info *nf_bridge;#endif#endif/* CONFIG_NETFILTER */#ifdefined(CONFIG_HIPPI)

union {

__u32 ifield;

}private;#endif#ifdef CONFIG_NET_SCHED

__u32 tc_index;/* traffic control index */#ifdef CONFIG_NET_CLS_ACT

__u32 tc_verd;/* traffic control verdict */__u32 tc_classid;/* traffic control classid */#endif#endif/* These elements must be at the end, see alloc_skb() for details. *///此报文存储区的长度,这个长度是16字节对齐的,一般要比报文的长度大unsignedinttruesize;

atomic_t users;/*head和end指向报文数据的整个单元.head与data之间的空间称为headroom,tail与end之间的空间称为tailroom.

*/unsignedchar*head, *data, *tail, *end; };

linux接收网络数据并存存储,linux网络数据包数据结构 Socket Buffer相关推荐

  1. linux接收网络数据并存存储,Linux网络设备驱动之数据接收流程(六)

    网络设备接收数据的主要方法是由中断引发设备的中断处理函数,中断处理函数判断中断类型,如果为接收中断,则读取接收到的数据,分配 sk_buffer 数据结构和数据缓冲区,将接收到的数据复制到数据缓冲区, ...

  2. LinUX接收蓝牙音频,面向嵌入式Linux的蓝牙音频技术的研究

    摘要: 近几年来,随着人们需求的不断提高,手持设备,无线设备,数字家庭设备等嵌入式产品迅速的发展,特别是嵌入式Linux的成熟,使本来只属于PC的一些高端应用也出现在这种嵌入式系统上面,并得到广泛的发 ...

  3. [转]Linux TCP/IP 协议栈的关键数据结构Socket Buffer(sk_buff )

    因为太喜欢这篇文章,所以有保存在自己blog里的冲动,同时也对文章代码的相关部分加上了颜色,给阅读时黑压压的一片带来一些亮色,也减少了阅读时的单调情愫. sk_buff结构可能是linux网络代码中最 ...

  4. python爬各国疫情数据,存储并进行数据可视化

    实验内容: 编写程序采用爬虫技术爬取各国疫情数据,进行存储并进行数据可视化,要求可以看到各国今日新增确诊数.累计确诊数. 实验方法: 爬取数据 通过以下的步骤,获取到了各国和我国今日的新增确诊数.累计 ...

  5. python数据的存储结构是指_python数据结构

    一.算法入门 程序设计 = 数据结构 + 算法 算法时为了解决实际问题而设计的,数据结构是算法需要处理的问题载体 数据结构只是静态的描述呢数据元素之间的关系 高效的程序需要再数据结构的基础上设计和选择 ...

  6. 数据的存储--深度解剖数据在内存中的存储

    目录 本章重点 一 数据类型介绍 1.1 类型的基本分类 整形家族 浮点数家族 构造类型 指针类型 空类型 二 整形在内存中的存储 2.1 原码.反码.补码 2.2 大小端介绍 设计一个程序,判断当前 ...

  7. Linux与网络服务(一)网络服务相关概念通俗解释(科普向)

    Linux与网络服务(一)网络服务相关概念通俗解释(科普向) 前言 专业名词及概念的通俗解释 云服务器 云/云服务 云计算 弹性伸缩 负载均衡 公网IP 域名/域名备案 端口 SSH(安全外壳协议)/ ...

  8. 云计算和大数据时代网络技术揭秘(八)数据中心存储FCoE

    数据中心存储演化--FCoE   数据中心三大基础:主机 网络 存储 在云计算推动下,存储基础架构在发生演变 传统存储结构DAS.SAN在发展中遇到了布线复杂.能耗增多的缺点(原生性),需要对架构做根 ...

  9. Windows/Linux客户端挂载NFS共享存储

    Windows/Linux客户端挂载NFS共享存储 1. Linux搭建NFS共享存储 1.1. NFS概述 1.2. 安装并配置NFS Server 1.3. 启动并验证NFS Server 2. ...

最新文章

  1. 数据库初学者_面向初学者的免费6小时数据科学课程
  2. HDUOJ-----Brave Game
  3. 具有Spring Boot和数据功能的Java头优先弹性搜索
  4. ubuntu要更新18.04了,lei了lei了~~~
  5. HIT Software Construction Review Notes(1-2 Quality Objectives of Software Construction)
  6. a标签去掉下划线_html常用标签、包含关系、常用术语,以及网页设计中的字体分类
  7. Hive安装部署及简单测试 网页《一》
  8. android 支付宝接口开发,android 实现支付宝wap接口编程
  9. 计算机的排版方法,计算机编辑排版系统及其方法
  10. 【财富空间】张泉灵:时代抛弃你时,连一声再见都不会说
  11. 英语发音规则---ir字母组合发音规律
  12. 人工智能必读书籍推荐—“花书”/计算机视觉/深度学习书籍
  13. PaddleSpeech:windows下用python快速安装和使用
  14. 手机抓包+注入黑科技HttpCanary——最强大的Android抓包注入工具
  15. 一本好的“错题集”如何做?看这里
  16. Hackthebox:Granny Walkthrough(not use metasploit)
  17. ASP.NET统计在线人数
  18. Netty 学习 之(1)Netty是什么
  19. jsjs调用app下载或者打开app 实现有所变动
  20. 【前端面试】同学,你会手写代码吗?

热门文章

  1. PIL 转opencv
  2. mysq改变字段类型
  3. gatewayproperties 是空_一个空手套白狼的商业模式
  4. pytorch 笔记:gather 函数
  5. 1 文巾解题 191. 位1的个数
  6. tableau实战系列(八)-用数据桶实现图表的固定轴距
  7. SVM-支持向量机原理详解与实践之三
  8. HDFS--Hadoop分布式文件系统
  9. Python多线程学习教程
  10. Cracer渗透视频课程学习笔记——基础知识(1)