Linux 内核源码分析---网络层分析

news2024/9/19 10:38:26

在这里插入图片描述
版本(version):指定所用 IP 协议的版本,该字段有效值为 4 或 6;
IP首部长度(IHL):定义首部的长度,由于选项数量可变;
服务区分:用于更复杂协议选项;
长度:指定分组的总长度,首部加数据的长度;
分片标识:标识一个分片的IP分组的各个部分;
TTL:指定从发送者到接收者的传输路径上中间站点的最大数目;
Proctocol 标识:IP 分组承载的高层协议(传输层);
检验和:根据首部和数据的内容计算;
IP首部所有的数值都以网络字节序存储(大端序)。

在这里插入图片描述

网络访问层仍受到传输介质的性质以及相关适配器的设备驱动程序的影响很大。网络层与网络适配器的硬件性质几乎是完全分离。
为什么说是几乎?该层不仅负责发送和接收数据,还负责在彼此不直接连接的系统之间转发和路由分组
查找最佳路由并选择适当的网络设备来发送分组,也涉及对底层地址族的处理(如特定硬件MAC地址)。

ip_rcv 函数是网络层的入口点,分组向上穿过内核的过程如下:
在这里插入图片描述

接收分组及分组转发

1、接收分组
在分组转发到 ip_rcv 之后,必须检查接收到的信息确保它是正确的。主要检查计算的校验和与首部中存储的校验和是否一致。其他的检查包括分组是否达到了 IP 首部的最小长度,分组的协议是否确实是 IPv4。
在进行检查之后,内核并不立即继续对分组的处理,而是调用一个 netfilter 挂钩,使得用户空间可以对分组数据进行操作。netfilter 挂钩插入到内核源代码中定义好的各个位置,使得分组能够被外部动态操作。

深入理解Netfilter

流程:ip_rcv()函数入口–>调用一个netfilter挂钩–>ip_oute_input()负责选择路由(判断路由的结果是选择一个函数进行一步分组处理)–>选择可用的函数ip_local_deliver()ip_forward()【具体选择那个函数,取决于分组是交付到本地计算机下一个更高协议层例程(ip _local_deliver()),还是转发到网络中另一个(ip_forward())】。

数据包在 Linux 内核中 netfilter 处理过程,其中有5个 HOOK 点执行:

  1. 数据报从进入系统进行IP检验以后,首先经过第一个HOOK函数NF_IP_PRE_ROUTING进行处理;
  2. 然后再进入路由代码,具体决定该数据报是需要转发还是发给本机;
    • 若数据报是被发到本机,由该数据经过HOOK函数NF_IP_LOCAL_IN处理以的然后传递给上层协议;
    • 若该数据报应该被转发则它被NF_IP_FORWARD处理;
  3. 经过转发的数据报经过最后个HOOK函数NF_IP_POST_ROUTING处理以后,再传输到网络上面;
  4. 本地产生的数据经过HOOK函数NF_IP_LOCAL_OUT处理后,进行路由选择处理,然后经过NF_IP_POST_ROUTING处理后发出去。

当一个IP包被接收到网卡中,函数iprcv()被执行,具体源码如下:

/*
 * 	Main IP Receive routine.	对IP头部合法性严格检査,然后把具体功能给 ip_rcv_finish 函数
 */
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
{
	const struct iphdr *iph;
	struct net *net;
	u32 len;

	/* When the interface is in promisc. mode, drop all the crap
	 * that it receives, do not try to analyse it.
	 */
	if (skb->pkt_type == PACKET_OTHERHOST)
		goto drop;

	// 关于网络层 SNMP 统计的信息,也可以通过netstat指令看到统计值信息
	net = dev_net(dev);
	__IP_UPD_PO_STATS(net, IPSTATS_MIB_IN, skb->len);

	skb = skb_share_check(skb, GFP_ATOMIC);
	if (!skb) {
		__IP_INC_STATS(net, IPSTATS_MIB_INDISCARDS);
		goto out;
	}
	// 确保 skb->data 指向内存包含的数据到少为 IP 头部大小,由于每个 IP 数据包包括 IP 分片必须包含一个完整的 IP 信息;
	// 如果小于头部大小,则缺失的分部将从数据分片中拷贝。
	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
		goto inhdr_error;

	// pskb_may_pul1可能会调整skb中指针,所以需要重新定义IP头部
	iph = ip_hdr(skb);

	/*
	 *	RFC1122: 3.2.1.2 MUST silently discard any IP frame that fails the checksum.
	 *
	 *	Is the datagram acceptable?
	 *
	 *	1.	Length at least the size of an ip header
	 *	2.	Version of 4
	 *	3.	Checksums correctly. [Speed optimisation for later, skip loopback checksums]
	 *	4.	Doesn't have a bogus length
	 */

	// 检测IP首部长度及协议版本
	if (iph->ihl < 5 || iph->version != 4)
		goto inhdr_error;

	BUILD_BUG_ON(IPSTATS_MIB_ECT1PKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_ECT_1);
	BUILD_BUG_ON(IPSTATS_MIB_ECT0PKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_ECT_0);
	BUILD_BUG_ON(IPSTATS_MIB_CEPKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_CE);
	__IP_ADD_STATS(net,
		       IPSTATS_MIB_NOECTPKTS + (iph->tos & INET_ECN_MASK),
		       max_t(unsigned short, 1, skb_shinfo(skb)->gso_segs));

	// 确保IP完整头部包括项在内存中相关信息
	if (!pskb_may_pull(skb, iph->ihl*4))
		goto inhdr_error;

	iph = ip_hdr(skb);

	// 验证IP头部的检验和
	if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
		goto csum_error;
	// 检测 IP 报文长度是否小于 skb->len
	len = ntohs(iph->tot_len);
	if (skb->len < len) {
		__IP_INC_STATS(net, IPSTATS_MIB_INTRUNCATEDPKTS);
		goto drop;
	} else if (len < (iph->ihl*4))
		goto inhdr_error;

	/* Our transport medium may have padded the buffer out. Now we know it
	 * is IP we can trim to the true length of the frame.
	 * Note this now means skb->len holds ntohs(iph->tot_len).
	 */
	if (pskb_trim_rcsum(skb, len)) {
		__IP_INC_STATS(net, IPSTATS_MIB_INDISCARDS);
		goto drop;
	}

	// 设置TCP报头指针
	skb->transport_header = skb->network_header + iph->ihl*4;

	/* Remove any debris in the socket control block */
	// 删除任何套接字控制块碎片
	memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
	IPCB(skb)->iif = skb->skb_iif;

	/* Must drop socket now because of tproxy. */
	// 因为tproxy,现在必须丢掉socket,tproxy是iptables的一附加控件,在mangle表的PREROUTING链中使用,不修改数据包包头
	// 直接把数据传递给一个本地soket
	skb_orphan(skb);

	// 数据包在网络栈中传输过程,Netfilter子系统能够让你的5个挂接点注册回调函数,将指出这些挂接点名称
	/*上面所有工作全部完成之后 ,就直接交给 NF_HOOK 管理,NF_HOOK 在做完 PRE_ROUTING 鍗选后,
	PRE_ROUTING 点上注册的所有钩子都返回 NF_ACCEPT 才会执行后面的 ip_rcv_finish 函数,
	然后继续执行路由等处理,如果是本地就会交给更高层的协议进行处理否则就执行 FORWARD*/
	return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
		       net, NULL, skb, dev, NULL,
		       ip_rcv_finish);

csum_error:
	__IP_INC_STATS(net, IPSTATS_MIB_CSUMERRORS);
inhdr_error:
	__IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS);
drop:
	kfree_skb(skb);
out:
	return NET_RX_DROP;
}

在这里插入图片描述
在这里插入图片描述

2、分组转发
IP 分组可能如上所述交付给本地计算机处理,它们也可能离开互联网络层,转发到另一台计算机,而不牵涉本地计算机的高层协议实例。
分组的目标地址可分为以下两类:
(1) 目标计算机在某个本地网络中,发送计算机与该网络有连接;
(2) 目标计算机在地理上属于远程计算机,不连接到本地网络,只能通过网关访问。
第二种场景要复杂得多。首先必须找到剩余路由中的第一个站点,将分组转发到该站点,这是向最终目标地址的第一步传输。因此,不仅需要计算机所属本地网络结构的相关信息,还需要相邻网络结构和相关的外出路径的信息。

发送分组

内核提供几个通过互联网络层发送数据的函数,可由较高协议层使用。其中 ip_queue_xmit 是最常使用的一个,其代码流程图如下:
在这里插入图片描述

/* Note: skb->sk can be different from sk, in case of tunnels */
int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl)
{
	struct inet_sock *inet = inet_sk(sk);
	struct net *net = sock_net(sk);
	struct ip_options_rcu *inet_opt;
	struct flowi4 *fl4;
	struct rtable *rt;
	struct iphdr *iph;
	int res;

	/* Skip all of this if the packet is already routed,
	 * f.e. by something like SCTP.
	 */
	rcu_read_lock();
	inet_opt = rcu_dereference(inet->inet_opt);
	fl4 = &fl->u.ip4;

	// 获取skb中的路由缓存
	rt = skb_rtable(skb);
	// skb中有缓存则跳转缓存
	if (rt)
		goto packet_routed;

	/* Make sure we can route this packet. */
	// 检查控制块中的路由缓存
	rt = (struct rtable *)__sk_dst_check(sk, 0);
	if (!rt) {	// 缓存过期
		__be32 daddr;

		/* Use correct destination address if we have options. */
		daddr = inet->inet_daddr;	// 目的地址
		if (inet_opt && inet_opt->opt.srr)	// 严格路由选项
			daddr = inet_opt->opt.faddr;

		/* If this fails, retransmit mechanism of transport layer will
		 * keep trying until route appears or the connection times
		 * itself out.
		 */
		// 查找路由缓存
		rt = ip_route_output_ports(net, fl4, sk,
					   daddr, inet->inet_saddr,
					   inet->inet_dport,
					   inet->inet_sport,
					   sk->sk_protocol,
					   RT_CONN_FLAGS(sk),
					   sk->sk_bound_dev_if);
		if (IS_ERR(rt))
			goto no_route;
		// 设置控制块的路由缓存
		sk_setup_caps(sk, &rt->dst);
	}
	// 将路由设置到skb中
	skb_dst_set_noref(skb, &rt->dst);

packet_routed:
	// 严格路由选项 使用网关 无路由
	if (inet_opt && inet_opt->opt.is_strictroute && rt->rt_uses_gateway)
		goto no_route;

	/* OK, we know where to send it, allocate and build IP header. */
	// 加入ip头
	skb_push(skb, sizeof(struct iphdr) + (inet_opt ? inet_opt->opt.optlen : 0));
	skb_reset_network_header(skb);
	// 构造ip头
	iph = ip_hdr(skb);
	*((__be16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff));
	if (ip_dont_fragment(sk, &rt->dst) && !skb->ignore_df)
		iph->frag_off = htons(IP_DF);
	else
		iph->frag_off = 0;
	iph->ttl      = ip_select_ttl(inet, &rt->dst);
	iph->protocol = sk->sk_protocol;
	ip_copy_addrs(iph, fl4);

	/* Transport layer set skb->h.foo itself. */
	// 构造ip选项
	if (inet_opt && inet_opt->opt.optlen) {
		iph->ihl += inet_opt->opt.optlen >> 2;
		ip_options_build(skb, &inet_opt->opt, inet->inet_daddr, rt, 0);
	}

	// 设置id
	ip_select_ident_segs(net, skb, sk,
			     skb_shinfo(skb)->gso_segs ?: 1);

	/* TODO : should we use skb->sk here instead of sk ? */
	skb->priority = sk->sk_priority;
	skb->mark = sk->sk_mark;

	res = ip_local_out(net, sk, skb);
	rcu_read_unlock();
	return res;

no_route:
	rcu_read_unlock();
	IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);
	kfree_skb(skb);
	return -EHOSTUNREACH;
}
EXPORT_SYMBOL(ip_queue_xmit);

ip_queue_xmit() 完成面向连接套接字的包输出,当套接字处于连接状态时,所有从套接字发出的包都具有确定的路由,无需为每一个输出包查询它的目的入口,可将套接字直接绑定到路由入口上,这由套接字的目的缓冲指针dst_cache完成ip_queue_xmit首先为输入包建立 IP 包头,经过本地包过滤器后,再将 IP 包分片输出(ip_fragment)。是 ip 层提供给 tcp 层发送回调。
ip_build_and_send_pkt 函数是服务器端在给客户端回复syn+ack时调用,该函数在构造ip头之后,调用ip_local_out发送数据包。

转移到网络访问层: ip_output 函数代码流程,其中根据分组是否需要分片。
在这里插入图片描述

在这里插入图片描述

通过网关访问

相邻网络结构和外出路径信息,该信息由路由表提供,路由表由内核通过多种数据结构实现并管理,在接收分组时调用的 ip_route_input 函数充当路由实现的接口,此方面因为为该函数能够识别出分组是交付到本地还是转发出去,另一个方面该函数能够找到通向目标地址的路由。目标地址存储在套接字缓冲区的 dst 字段当中。

ip forward 流程主要功能:根据报文信息得到路由、ipset安全检测、转发的基本逻辑,IP层提交本地处理的流程等,根据IP地址决定是提交给本地处理,还是报文转发的,报文转发的入口函数ip_forward

在这里插入图片描述

// 报文转发的入口函数
int ip_forward(struct sk_buff *skb)
{
	u32 mtu;
	struct iphdr *iph;	/* Our header */
	struct rtable *rt;	/* Route we use */
	struct ip_options *opt	= &(IPCB(skb)->opt);
	struct net *net;

	/* that should never happen */
	// 不允许处理非本host的报文,即报文目的 mac 是本机
	if (skb->pkt_type != PACKET_HOST)
		goto drop;

	if (unlikely(skb->sk))
		goto drop;

	// 报文为非线性,gso_size 不为 0,但是 gso_type 为 0,丢弃此类报文
	if (skb_warn_if_lro(skb))
		goto drop;

	if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb))
		goto drop;

	if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
		return NET_RX_SUCCESS;

	skb_forward_csum(skb);
	net = dev_net(skb->dev);

	/*
	 *	According to the RFC, we must first decrease the TTL field. If
	 *	that reaches zero, we must reply an ICMP control message telling
	 *	that the packet's lifetime expired.
	 */
	if (ip_hdr(skb)->ttl <= 1)	// ttl减至0,丢弃报文
		goto too_many_hops;

	if (!xfrm4_route_forward(skb))	// ipset路由安全规则检测,得到路由的信息
		goto drop;

	rt = skb_rtable(skb);	// 得到路由表项

	if (opt->is_strictroute && rt->rt_uses_gateway)
		goto sr_failed;

	IPCB(skb)->flags |= IPSKB_FORWARDED;
	mtu = ip_dst_mtu_maybe_forward(&rt->dst, true);
	if (ip_exceeds_mtu(skb, mtu)) {
		IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,	// 文长度超过 mtu 且不允许 ip 分片,发送 icmp 消息给发送者
			  htonl(mtu));
		goto drop;
	}

	/* We are about to mangle packet. Copy it! */
	if (skb_cow(skb, LL_RESERVED_SPACE(rt->dst.dev)+rt->dst.header_len))
		goto drop;
	iph = ip_hdr(skb);

	/* Decrease ttl after skb cow done */
	ip_decrease_ttl(iph);

	/*
	 *	We now generate an ICMP HOST REDIRECT giving the route
	 *	we calculated.
	 */
	if (IPCB(skb)->flags & IPSKB_DOREDIRECT && !opt->srr &&
	    !skb_sec_path(skb))
		ip_rt_send_redirect(skb);	// 通知发送端,路由重定向

	skb->priority = rt_tos2priority(iph->tos);	// 根据tos值计算priority

	return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD,
		       net, NULL, skb, skb->dev, rt->dst.dev,
		       ip_forward_finish);	// 调用netfilter,实现iptables功能,通过后调用ip_forward_finish

sr_failed:
	/*
	 *	Strict routing permits no gatewaying
	 */
	 icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0);
	 goto drop;

too_many_hops:
	/* Tell the sender its packet died... */
	__IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS);
	icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
drop:
	kfree_skb(skb);
	return NET_RX_DROP;
}

在这里插入图片描述

路由:在任何IP实现中,不仅在转发外部分组时需要,而且也用于发送本地计算机产生的分组。
每个接收到的分组属于3个类别之一:
(1)其目标是本地主机;
(2)其目标是当前主机直接连接的计算机;
(3)其目标是远程计算机,只能经由中间系统到达。

路由结果关联到一个套接字缓冲区,套接字缓冲区的 dst 成员指向一个dest_entry结构的实例,此实例的内容是在路由查找期间填充的,具体内核源码如下:

在这里插入图片描述
struct neighbour成员存储计算机在本地网络中的IP和硬件地址,这样就可以通过网络访问层直接到达。
neighbour 实例由内核中实现 ARP 的ARP层创建,ARP协议负责将IP地址转换为硬件地址

tcp/ip 发包流程

在这里插入图片描述

https://blog.csdn.net/qq_53111905/article/details/126251324

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2036733.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI技术重塑招聘流程

一、引言 随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;技术正以前所未有的速度渗透到社会的各个领域&#xff0c;其中&#xff0c;人力资源管理领域也不例外。在全员招聘这一关键环节中&#xff0c;AI技术的应用不仅极大地提高了招聘效率&#xff0c;还…

【Java数据结构】---Queue

乐观学习&#xff0c;乐观生活&#xff0c;才能不断前进啊&#xff01;&#xff01;&#xff01; 我的主页&#xff1a;optimistic_chen 我的专栏&#xff1a;c语言 &#xff0c;Java 欢迎大家访问~ 创作不易&#xff0c;大佬们点赞鼓励下吧~ 文章目录 前言队列Queue队列的模拟…

fme处理空间数据入门v0

概述 本教程是最最最基本的fme处理空间数据内容&#xff0c;给新同事介绍我以前是怎么干活用的&#xff0c;基本啥有用的东西。 本教程主要说如何在 FME Workbench中创建模板文件&#xff08;一般我们把fme写的工作空间就叫模板&#xff09;&#xff0c;教程里面主要是利用GI…

Linux2.6内核进程调度队列详细讲解

上图是 Linux2.6 内核中进程队列的数据结构&#xff0c;之间关系也已经给大家画出来&#xff0c;方便大家理解。 一个 CPU 拥有一个 runqueue。 Linux真正的调度方式是通过runqueue进行调度的&#xff0c;我们知道进程的优先级范围是根据nice值确定的&#xff0c;而nice值的范围…

怎么配置Datagrip中字体的大小,修改注释的颜色呢

新手刚安装Datagrip时&#xff0c;都会遇到怎么调字体大小的问题&#xff0c;接下来解决一下吧 具体步骤&#xff1a; 1.进入Datagrip的操作窗口后&#xff0c;点击左上方的菜单栏中file(文件)。 2.在文件中点击设置setting。 3.在设置窗口点击Editor,再点击Font。 4.在Fo…

.[[Hoeosi@airmail.cc]].rntc勒索病毒数据怎么处理|数据解密恢复

导言&#xff1a; 近年来&#xff0c;勒索病毒&#xff08;也称为勒索软件&#xff09;已成为网络安全领域的一大威胁。其中&#xff0c;.[[Hoeosiairmail.cc]].rntc勒索病毒作为一种新型恶意软件&#xff0c;通过加密用户的重要文件并要求支付赎金来解锁&#xff0c;给个人和…

深度学习 —— 个人学习笔记20(转置卷积、全卷积网络)

声明 本文章为个人学习使用&#xff0c;版面观感若有不适请谅解&#xff0c;文中知识仅代表个人观点&#xff0c;若出现错误&#xff0c;欢迎各位批评指正。 三十九、转置卷积 import torch from torch import nndef trans_conv(X, K):h, w K.shapeY torch.zeros((X.shape[…

Datawhale AI夏令营第四期魔搭- AIGC文生图方向 task02笔记

1 前言 本次是学习内容是Datawhale AI夏令营第四期-AIGC文生图方向的学习笔记。 2 AIGC简介 AIGC&#xff08;Artificial Intelligence Generated Content&#xff09;即人工智能生成内容&#xff0c;即人工智能通过学习大量的数据&#xff0c;来实现自动生成各种内容&#xf…

仿RabbitMQ实现消息队列

前言&#xff1a;本项目是仿照RabbitMQ并基于SpringBoot Mybatis SQLite3实现的消息队列&#xff0c;该项目实现了MQ的核心功能&#xff1a;生产者、消费者、中间人、发布、订阅等。 源码链接&#xff1a;仿Rabbit MQ实现消息队列 目录 前言&#xff1a;本项目是仿照Rabbi…

JVM运行时数据区之虚拟机栈

【1】概述 Java虚拟机栈&#xff08;Java Virtual Machine Stack&#xff09;&#xff0c;早期也叫Java栈。每个线程在创建时都会创建一个虚拟机栈&#xff0c;其内部保存一个个的栈帧&#xff08;Stack Frame&#xff09;&#xff0c;对应着一次次的Java方法调用。 栈是运行…

World of Warcraft [CLASSIC] 80 WLK [Gundrak] BUG

World of Warcraft [CLASSIC] 80 WLK [Gundrak] BUG 魔兽世界怀旧版&#xff0c;80级&#xff0c;5人副本古达克&#xff0c;科技队伍&#xff08;BUG队伍&#xff09; 副本有两个门口 这样看&#xff0c;是不是觉得很怪。是的&#xff0c;和图1刚好相反的。 因此应该翻转180…

24电赛H题总结

一、题目 题目链接&#xff1a;自动行驶小车&#xff08;H题&#xff09; 我们截取一些重要信息 1. 小车行驶场地示意图 2.要求 二、赛题分析 技术挑战与准备 MCU熟悉度&#xff1a;尽管TI MSPM0系列MCU在使用上类似于STM32CUBEIDEKeil&#xff0c;但其开发环境也需要熟悉。因…

数据结构入门——04栈

1.栈 栈是限制在一端进行插入操作和删除操作的线性表&#xff08;俗称堆栈&#xff09; 允许进行操作的一端称为“栈顶”&#xff0c;另一固定端称为“栈底”&#xff0c;当栈中没有元素时称为“空栈”。 栈的特点 &#xff1a;后进先出LIFO&#xff08;Last In First Out&a…

支持I2C接口、抗干扰性强、14通道触摸按键的电容式触摸芯片-GTX314L

电容式触摸芯片 - GTX314L是具有多通道触发传感器的14位触摸传感器系列&#xff0c;它是通过持续模式提供中断功能和唤醒功能&#xff0c;广泛适用于各种控制面板应用&#xff0c;可直接兼容原机械式轻触按键的处理信号。 GTX314L芯片内部采用特殊的集成电路&#xff0c;具有高…

C++进阶-智能指针

1. 为什么需要智能指针&#xff1f; 下面我们先分析一下下面这段程序有没有什么内存方面的问题&#xff1f;提示一下&#xff1a;注意分析MergeSort函数中的问题。 int div() {int a, b;cin >> a >> b;if (b 0)throw invalid_argument("除0错误");retur…

【C语言】内存管理

C语言-内存管理 一、C进程内存布局二、栈内存1、存储在栈内存中的参数有哪些&#xff1f;2、栈内存的特点&#xff1f; 三、静态数据四、数据段与代码段五、堆内存 一、C进程内存布局 \qquad 任何一个程序&#xff0c;正常运行都需要内存资源&#xff0c;用来存放诸如变量、常量…

第九届“创客中国”武汉区域赛正式启幕 灵途科技勇夺前三,晋级决赛!

8月8日&#xff0c;第九届“创客中国”武汉区域赛正式启幕&#xff0c;首场聚焦先进制造领域。灵途科技勇夺先进制造领域专场企业组前三名&#xff0c;成功晋级决赛。 “创客中国”大赛是工业和信息化部组织开展的双创赛事活动&#xff0c;以构建产业链协同发展为出发点&#…

Win10 VisualStudio 2022编译ollvm 13.x

VisualStudio配置 1&#xff0c;正常配置C桌面环境 2&#xff0c;在单个组件中选择用于Windows得C Cmake工具 下载OLLVM13.x https://github.com/heroims/obfuscator/tree/llvm-13.x 解压后进入文件夹&#xff0c;命令行输入 cmake -G “Visual Studio 17 2022” -DLLVM_EN…

Java面试--设计模式

设计模式 目录 设计模式1.单例模式&#xff1f;2.代理模式&#xff1f;3.策略模式&#xff1f;4.工厂模式&#xff1f; 1.单例模式&#xff1f; 单例模式是Java的一种设计思想&#xff0c;用此模式下&#xff0c;某个对象在jvm只允许有一个实例&#xff0c;防止这个对象多次引…

依赖倒置原则:构建灵活软件架构的基石 - 通过代码实例深入解析

1.引言 1.1为什么要学习依赖倒置原则 在软件开发过程中&#xff0c;我们经常需要对代码进行修改和扩展。如果代码之间的耦合度过高&#xff0c;那么在进行修改或扩展时&#xff0c;可能会对其他部分的代码产生影响&#xff0c;甚至引发错误。这就要求我们在编写代码时&#xf…