linux接收网络数据并存存储,linux网络数据包数据结构 Socket Buffer
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相关推荐
- linux接收网络数据并存存储,Linux网络设备驱动之数据接收流程(六)
网络设备接收数据的主要方法是由中断引发设备的中断处理函数,中断处理函数判断中断类型,如果为接收中断,则读取接收到的数据,分配 sk_buffer 数据结构和数据缓冲区,将接收到的数据复制到数据缓冲区, ...
- LinUX接收蓝牙音频,面向嵌入式Linux的蓝牙音频技术的研究
摘要: 近几年来,随着人们需求的不断提高,手持设备,无线设备,数字家庭设备等嵌入式产品迅速的发展,特别是嵌入式Linux的成熟,使本来只属于PC的一些高端应用也出现在这种嵌入式系统上面,并得到广泛的发 ...
- [转]Linux TCP/IP 协议栈的关键数据结构Socket Buffer(sk_buff )
因为太喜欢这篇文章,所以有保存在自己blog里的冲动,同时也对文章代码的相关部分加上了颜色,给阅读时黑压压的一片带来一些亮色,也减少了阅读时的单调情愫. sk_buff结构可能是linux网络代码中最 ...
- python爬各国疫情数据,存储并进行数据可视化
实验内容: 编写程序采用爬虫技术爬取各国疫情数据,进行存储并进行数据可视化,要求可以看到各国今日新增确诊数.累计确诊数. 实验方法: 爬取数据 通过以下的步骤,获取到了各国和我国今日的新增确诊数.累计 ...
- python数据的存储结构是指_python数据结构
一.算法入门 程序设计 = 数据结构 + 算法 算法时为了解决实际问题而设计的,数据结构是算法需要处理的问题载体 数据结构只是静态的描述呢数据元素之间的关系 高效的程序需要再数据结构的基础上设计和选择 ...
- 数据的存储--深度解剖数据在内存中的存储
目录 本章重点 一 数据类型介绍 1.1 类型的基本分类 整形家族 浮点数家族 构造类型 指针类型 空类型 二 整形在内存中的存储 2.1 原码.反码.补码 2.2 大小端介绍 设计一个程序,判断当前 ...
- Linux与网络服务(一)网络服务相关概念通俗解释(科普向)
Linux与网络服务(一)网络服务相关概念通俗解释(科普向) 前言 专业名词及概念的通俗解释 云服务器 云/云服务 云计算 弹性伸缩 负载均衡 公网IP 域名/域名备案 端口 SSH(安全外壳协议)/ ...
- 云计算和大数据时代网络技术揭秘(八)数据中心存储FCoE
数据中心存储演化--FCoE 数据中心三大基础:主机 网络 存储 在云计算推动下,存储基础架构在发生演变 传统存储结构DAS.SAN在发展中遇到了布线复杂.能耗增多的缺点(原生性),需要对架构做根 ...
- Windows/Linux客户端挂载NFS共享存储
Windows/Linux客户端挂载NFS共享存储 1. Linux搭建NFS共享存储 1.1. NFS概述 1.2. 安装并配置NFS Server 1.3. 启动并验证NFS Server 2. ...
最新文章
- 数据库初学者_面向初学者的免费6小时数据科学课程
- HDUOJ-----Brave Game
- 具有Spring Boot和数据功能的Java头优先弹性搜索
- ubuntu要更新18.04了,lei了lei了~~~
- HIT Software Construction Review Notes(1-2 Quality Objectives of Software Construction)
- a标签去掉下划线_html常用标签、包含关系、常用术语,以及网页设计中的字体分类
- Hive安装部署及简单测试 网页《一》
- android 支付宝接口开发,android 实现支付宝wap接口编程
- 计算机的排版方法,计算机编辑排版系统及其方法
- 【财富空间】张泉灵:时代抛弃你时,连一声再见都不会说
- 英语发音规则---ir字母组合发音规律
- 人工智能必读书籍推荐—“花书”/计算机视觉/深度学习书籍
- PaddleSpeech:windows下用python快速安装和使用
- 手机抓包+注入黑科技HttpCanary——最强大的Android抓包注入工具
- 一本好的“错题集”如何做?看这里
- Hackthebox:Granny Walkthrough(not use metasploit)
- ASP.NET统计在线人数
- Netty 学习 之(1)Netty是什么
- jsjs调用app下载或者打开app 实现有所变动
- 【前端面试】同学,你会手写代码吗?