目录
传统艺能😎
小编是双非本科大二菜鸟不赘述,欢迎米娜桑来指点江山哦
1319365055
🎉🎉非科班转码社区诚邀您入驻🎉🎉
小伙伴们,满怀希望,所向披靡,打码一路向北
一个人的单打独斗不如一群人的砥砺前行
这是和梦想合伙人组建的社区,诚邀各位有志之士的加入!!
社区用户好文均加精(“标兵”文章字数2000+加精,“达人”文章字数1500+加精)
直达: 社区链接点我
概念😍
其实在 c++ 的笔记里面我已经详细提过了,这时还是做一下书面总结:IO 即 input/output ,在冯诺依曼体系中将数据从输入设备拷贝到内存就叫做输入,将数据从内存拷贝到输出设备就叫做输出
对文件进行读写操作本质是一种IO,文件IO对应的外设是磁盘;对网络进行读写操作本质也是一种IO,网络IO对应的外设是网卡。
O S 如何得知外设当中有数据可读? \color{red} {OS 如何得知外设当中有数据可读?} OS如何得知外设当中有数据可读?
操作系统不会主动检测外设上是否有数据就绪,这种做法一定会降低工作效率,因为大部分情况下外设都是没数据的,因此操作系统所做的大部分检测工作其实是徒劳。
系统实际采用的是中断的方式来得知外设上是否有数据就绪的,当某个外设上面有数据就绪时,该外设就会向 CPU 当中的中断控制器发送中断信号,中断控制器再根据产生的中断信号的优先级按顺序发送给 CPU。
每一个中断信号都有一个对应的中断处理程序,存储中断信号和中断处理程序映射关系的表叫做中断向量表,当CPU收到某个中断信号时就会自动停止正在运行的程序,然后根据该中断向量表执行该中断信号对应的中断处理程序,处理完毕后再返回原被暂停的程序继续运行(需要注意的是,CPU不直接和外设打交道指的是在数据层面上,而外设其实是可以直接将某些控制信号发送给 CPU 的某些控制器的)
O S 如何处理网卡读到的数据包? \color{red} {OS 如何处理网卡读到的数据包?} OS如何处理网卡读到的数据包?
操作系统任何时刻都可能收到大量数据包,因此操作系统必须将数据包管理起来。所谓的管理就是 “先描述,再组织” 原则,此时内核当中会维护一个结构叫 sk_buff,该结构就是用来管理和控制接收或发送数据包的信息的,下面给出一个简化版的 sk_buff 结构:
当操作系统从网卡当中读取到一个数据包后,会将该数据依次交给链路层、网络层、传输层、应用层进行解包和分用,最终将数据包中的数据交给了上层用户,那对应 sk_buff 结构具体是如何进行数据包解包和分用的呢?
当从网卡中读取到一个数据包后,就会定义出 sk_buff 结构,然后用 sk_buff 结构当中的 data 指针指向读到的数据包,并将定义出来的这个 sk_buff 与其他 sk_buff 用双链表的形式组织起来,此时操作系统对各个数据包的管理就变成了对双链表的增删查改等操作。
接下来需要将读取的数据包交给最底层的链路层处理,进行解包和分用,此时就是让 sk_buff 结构中的 mac_header 指针指向最初的数据包,然后向后读取链路层的报头,剩下的就是需要交给网络层处理的有效载荷了解包。
这时链路层就需要将有效载荷向上交付给网络层进行解包和分用了,这里所说的向上交付只是形象的说法,实际向上交付并不是直接交付数据,我们只需要让 sk_buff 结构当中的 network_header 指针,指向数据包中链路层报头之后的数据即可,然后继续向后读取网络层的报头,便完成了网络层的解包。
紧接着就是传输层对数据进行处理了,同样的道理,让 sk_buff 结构当中的 transport_header 指针,指向数据包中网络层报头之后的数据,然后继续向后读取传输层的报头,便完成了传输层的解包。传输层解包后就可以根据具体使用的传输层协议,对应将剩下的数据拷贝到 TCP 或 UDP 的接收缓冲区供用户读取即可
发送数据时对数据进行封装也是同样的道理,就是依次在数据前面拷贝上对应的报头,最后再将数据发送出去(UDP)或拷贝到发送缓冲区(TCP)即可。也就是说,在封装和解包的过程中,本质存储位置是没有发生变化的,实际只是在用不同指针对数据进行操作而已。
但内核中的sk_buff并不像上面那样简单:
一方面,为了保证高效的网络报文处理效率,这就要求sk_buff的结构也必须是高效的
另一方面,sk_buff 结构需要被内核协议中的各个协议共同使用,因此 sk_buff 必须能够兼容所有网络协议
因此 sk_buff 实际是非常复杂的,在云服务器中 sk_buff 结构定义如下(带伙看个大概乐子就行了,实在太多了):
struct sk_buff {
#ifdef __GENKSYMS__
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev;
ktime_t tstamp;
#else
union {
struct {
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev;
union {
ktime_t tstamp;
struct skb_mstamp skb_mstamp;
__RH_KABI_CHECK_SIZE_ALIGN(ktime_t a,
struct skb_mstamp b);
};
};
struct rb_node rbnode; /* used in netem, ip4 defrag, and tcp stack */
};
#endif
struct sock *sk;
struct net_device *dev;
/*
* 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.
*/
char cb[48] __aligned(8);
unsigned long _skb_refdst;
#ifdef CONFIG_XFRM
struct sec_path *sp;
#endif
unsigned int len,
data_len;
__u16 mac_len,
hdr_len;
union {
__wsum csum;
struct {
__u16 csum_start;
__u16 csum_offset;
};
};
__u32 priority;
kmemcheck_bitfield_begin(flags1);
__u8 RH_KABI_RENAME(local_df, ignore_df) :1,
cloned : 1,
ip_summed : 2,
nohdr : 1,
nfctinfo : 3;
__u8 pkt_type : 3,
fclone : 2,
ipvs_property : 1,
peeked : 1,
nf_trace : 1;
kmemcheck_bitfield_end(flags1);
__be16 protocol;
void(*destructor)(struct sk_buff *skb);
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct nf_conntrack *nfct;
#endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
struct nf_bridge_info *nf_bridge