MAC控制器驱动

news2024/12/28 5:21:23

嵌入式Linux开发模式下,以太网硬件架构一般都是 MAC与PHY是独立的。所以以太网模块的硬件相关的驱动代码主要包括 GMAC 和 PHY,其中MAC控制器驱动由SoC厂商开发,PHY芯片驱动由PHY厂商开发,PHY 驱动一般使用通用 PHY 驱动,如果有需要修改特殊寄存器,请使用对应的 PHY 驱动,代码都在 drivers/net/phy。当然,驱动之间要完成通信,必须严格按照IEEE802.3制定的协议。

以RK芯片为例,RK系列的Soc中内置了以太网MAC控制器,所以只需要搭配一颗以太网PHY芯片就可以实现以太网功能。按照规范 就算是不同的厂家的PHY,仍然有一部分寄存器的定义是通用的,只要配置了这些通用的寄存器,基本上PHY就可以正常工作。所以一般情况下,如果不需要使用PHY厂家提供的自定义的寄存器配置实现一些个性化的功能,那么PHY驱动就基本不需要修改。所以Linux驱动中有通用的PHY驱动。

本文只对MAC控制器进行简单说明

MAC控制器通过MDIO总线来管理phy设备,mdio总线与i2c总线类似,可以一个主机对应多个从设备,每个从设备都有地址。mdio最多接32个phy设备。

对应的目录是/sys/mdio,在/sys/mdio/devices目录中会有挂载在mdio的phy设备,在/sys/mdio/drivers中会有phy设备的驱动。

如:
 /sys/bus/mdio_bus/devices/stmmac-0:00 
其中 stmmac-0:00 表示 PHY 地址是 0。


该命令会读取 0~31 的所有寄存器,所以可以查看对应的寄存器值
cat /sys/bus/mdio_bus/devices/stmmac-0:00/phy_registers

Linux3.10 版本内核
GMAC 驱动代码 driver/net/ethernet/rockchip/gmac/*

其它内核
GMAC 驱动代码,高于3.10 的内核版本,
GMAC 驱动代码位置 : drivers/net/ethernet/stmicro/stmmac/*

GMAC控制器相关设备树资源:

gmac: ethernet@ffc40000 {
		compatible = "rockchip,rv1126-gmac", "snps,dwmac-4.20a";
		reg = <0xffc40000 0x0ffff>;
		interrupts = <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>,
			     <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
		interrupt-names = "macirq", "eth_wake_irq";
		rockchip,grf = <&grf>;
	...

		//关于MDC/MDIO通信相关配置信息
		mdio: mdio {
			compatible = "snps,dwmac-mdio";
			#address-cells = <0x1>;
			#size-cells = <0x0>;
		};

	...
	};

&gmac {
	phy-mode = "rmii"; //!!!  rmii模式
	clock_in_out = "output";// output 时钟由MAC输入给PHY input与之相反

    snps,reset-gpio = <&gpio3 RK_PA4 GPIO_ACTIVE_LOW>;//用于复位PHY的GPIO
    snps,reset-active-low;//复位PHY的GPIO低电平有效
    /* Reset time is 20ms, 100ms for rtl8211f */
    snps,reset-delays-us = <0 100000 100000>;// 复位PHY之前延时0ms  拉低维持的时间为100ms 拉高后延时100ms

	 // MAC时钟源
    assigned-clocks = <&cru CLK_GMAC_SRC_M0>, <&cru CLK_GMAC_SRC>, <&cru CLK_GMAC_TX_RX>;
    // MAC父时钟源
    assigned-clock-parents = <&cru CLK_GMAC_RGMII_M0>, <&cru CLK_GMAC_SRC_M0>, <&cru RMII_MODE_CLK>; 
     // MAC时钟频率
    assigned-clock-rates = <0>, <50000000>;

	pinctrl-names = "default";
    pinctrl-0 = <&rmiim0_pins &gmac_clk_m0_drv_level0_pins>;

	status = "okay";
	phy-handle = <&phy>;// 指定与MAC连接的PHY的配置信息
};



&mdio {
  //指定与MAC连接的PHY的配置信息
  phy: phy@1 {
	  // mdio和PHY之间的通信协议为22
    compatible = "ethernet-phy-ieee802.3-c22";
	  // phy硬件地址与与硬件上的phy地址相对应
    reg = <0x1>;
    clocks = <&cru CLK_GMAC_ETHERNET_OUT>;
  };
};

drivers\net\ethernet\stmicro\stmmac\dwmac-rk.c

static const struct of_device_id rk_gmac_dwmac_match[] = {
	...
	{ .compatible = "rockchip,px30-gmac",	.data = &px30_ops   },
	{ .compatible = "rockchip,rk3288-gmac", .data = &rk3288_ops },
	{ .compatible = "rockchip,rv1126-gmac", .data = &rv1126_ops },
	{ }
};
MODULE_DEVICE_TABLE(of, rk_gmac_dwmac_match);



static int rk_gmac_probe(struct platform_device *pdev)
{
	//gmac控制器所有配置信息
	struct plat_stmmacenet_data *plat_dat;
	
	//gmac控制器资源数据
	struct stmmac_resources stmmac_res;
	
	...

	/* 从设备树解析 gmac控制器所有配置信息
	如:
	struct plat_stmmacenet_data *plat;
		int interface;//接口类型 rmii  PHY_INTERFACE_MODE_RMII
		int phy_addr;// phy 地址 默认设置为-1 表示phy自动检测
		struct device_node *phy_node; //phy配置节点
		struct device_node *mdio_node; // mdio总线配置节点
		struct stmmac_mdio_bus_data *mdio_bus_data;//mdio总线数据	
		...
		
	*/
	plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);


...

	
	/* 主要工作
		struct platform_device
			struct device	dev;
			
		struct plat_stmmacenet_data //gmac控制器所有配置信息

		struct stmmac_resources //gmac控制器资源
	*/
	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
	if (ret)
		goto err_gmac_powerdown;

	...
}

drivers\net\ethernet\stmicro\stmmac\stmmac_platform.c

struct plat_stmmacenet_data *
stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
{
	struct device_node *np = pdev->dev.of_node;
	struct plat_stmmacenet_data *plat;
	struct stmmac_dma_cfg *dma_cfg;
	int rc;

	// gmac控制器所有配置信息
	plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
	if (!plat)
		return ERR_PTR(-ENOMEM);

	//设备树中没有指定固定的 MAC地址,所以这里还是空
	*mac = of_get_mac_address(np);
	
	//Interface Mode definitions    PHY_INTERFACE_MODE_RMII  -- rmii
	/*
	struct plat_stmmacenet_data *plat;
		int interface;//接口类型 rmii  PHY_INTERFACE_MODE_RMII
	*/
	plat->interface = of_get_phy_mode(np);

	/* Get max speed of operation from device tree */
	//没有
	if (of_property_read_u32(np, "max-speed", &plat->max_speed))
		plat->max_speed = -1;

...

	/* Default to phy auto-detection */
	//默认 phy 自动检测
	/*
	struct plat_stmmacenet_data *plat;
		int phy_addr;// phy 地址 默认设置为-1 表示phy自动检测
	*/
	plat->phy_addr = -1;

...

	/* To Configure PHY by using all device-tree supported properties */
	//解析设备树驱动参数来分配PHY资源
	/*
	struct plat_stmmacenet_data *plat

	struct platform_device
		struct device	dev;
			struct device_node	*of_node;
			
	struct platform_device
		struct device	dev;

	解析设备树获取:
	struct plat_stmmacenet_data *plat;
		struct device_node *phy_node; //phy配置节点
		struct device_node *mdio_node; // mdio总线配置节点
	*/
	rc = stmmac_dt_phy(plat, np, &pdev->dev);
	...
}

.

//解析设备树驱动参数来分配PHY资源
/*
struct plat_stmmacenet_data *plat

struct platform_device
	struct device	dev;
		struct device_node	*of_node;
		
struct platform_device
	struct device	dev;

*/

/*

	gmac: ethernet@ffc40000 {
		...
		mdio: mdio {
			compatible = "snps,dwmac-mdio";
		}
		...
	}

	&gmac{
		...
		 //指定与MAC连接的PHY的配置信息
		 //phy: phy@1
		phy-handle = <&phy>;

	}

*/	

static int stmmac_dt_phy(struct plat_stmmacenet_data *plat,
			 struct device_node *np, struct device *dev)
{
	bool mdio = true;
	static const struct of_device_id need_mdio_ids[] = {
		{ .compatible = "snps,dwc-qos-ethernet-4.10" },
		{},
	};

	/* If phy-handle property is passed from DT, use it as the PHY */
	//获取 设备树中   与MAC连接的PHY的配置信息
	plat->phy_node = of_parse_phandle(np, "phy-handle", 0);
	if (plat->phy_node)
		dev_dbg(dev, "Found phy-handle subnode\n");

	...

	if (of_match_node(need_mdio_ids, np)) {
		plat->mdio_node = of_get_child_by_name(np, "mdio");
	} else {

		// 关于MDC/MDIO通信相关配置信息
		/**
		 * If snps,dwmac-mdio is passed from DT, always register
		 * the MDIO
		 */
		//遍历 gmac: ethernet 的所有子节点 找到 mdio 节点
		/*
			gmac: ethernet@ffc40000 {
				...
				mdio: mdio {
					compatible = "snps,dwmac-mdio";
				}
				...
			}
		*/
		for_each_child_of_node(np, plat->mdio_node) {
			if (of_device_is_compatible(plat->mdio_node,
						    "snps,dwmac-mdio"))
				break;
		}
	}

	//至此 找到了 gmac 控制器节点下的 mdio总线节点信息
	if (plat->mdio_node) {
		dev_dbg(dev, "Found MDIO subnode\n");
		mdio = true;
	}

	if (mdio)
		plat->mdio_bus_data =
			devm_kzalloc(dev, sizeof(struct stmmac_mdio_bus_data),
				     GFP_KERNEL);
	return 0;
}

drivers\net\ethernet\stmicro\stmmac\stmmac_main.c
工作:

在这里插入图片描述

/*
	struct platform_device
		struct device	dev;
		
	struct plat_stmmacenet_data //gmac控制器所有配置信息

	struct stmmac_resources //gmac控制器资源
*/

int stmmac_dvr_probe(struct device *device,
		     struct plat_stmmacenet_data *plat_dat,
		     struct stmmac_resources *res)
{
	
	struct net_device *ndev = NULL;
	struct stmmac_priv *priv;
	u32 queue, maxq;
	int ret = 0;

	//申请网卡
	ndev = alloc_etherdev_mqs(sizeof(struct stmmac_priv),
				  MTL_MAX_TX_QUEUES,
				  MTL_MAX_RX_QUEUES);
	if (!ndev)
		return -ENOMEM;

	SET_NETDEV_DEV(ndev, device);

//即通过struct net_device *dev首地址加对齐后的偏移量就得到了私有数据的首地址
//直接返回了net_device结构末端地址,也就是网卡私有数据结构的起始地址。当然其中考虑了字节对齐的问题
/*
	struct net_device *ndev 
		...
	}+ 网卡私有数据
	struct stmmac_priv
*/
	priv = netdev_priv(ndev);
	priv->device = device;
	priv->dev = ndev;
	
	
/*网卡操作集合
struct net_device *ndev 
	const struct ethtool_ops *ethtool_ops;
*/
	stmmac_set_ethtool_ops(ndev);
	
	
/*

	struct net_device {

	}++网卡私有数据
	struct stmmac_priv 							  //gmac控制器所有配置信息
		struct plat_stmmacenet_data *plat; ------ struct plat_stmmacenet_data *plat;

*/
	priv->plat = plat_dat;
	priv->ioaddr = res->addr;
	priv->dev->base_addr = (unsigned long)res->addr;
	
	...
	
	/* MDIO bus Registration */
	/*

	暂时分析的主要工作:

	1 设置总线信息,并注册0-32地址中识别到的phy到 mdio总线,即添加到 mdio_map[]数组

							  +---struct net_device *ndev 					+-------struct device_node *phy_node; //phy配置节点
			//mido总线		  |												|
		+---struct mii_bus	  |												|
		|		void *priv;---+												|
		|		struct device dev;											|
		|			struct device_node	*of_node;---------------------------+
		|		const char *name;//"stmmac";
		|		int (*read)(struct mii_bus *bus, int addr, int regnum);
		|		int (*write)(struct mii_bus *bus, int addr, int regnum, u16 val);
		|		int (*reset)(struct mii_bus *bus);
		|		struct device dev;
		|			struct class	*class; 
		|				.name		= "mdio_bus",
		|				
		|		/* list of all PHYs on bus */
		|	+---struct mdio_device *mdio_map[PHY_MAX_ADDR];//mdio设备数组 ---------------------------------------+
		|	|																									 |
		|	|																									 |
		|	|																									 |
		|	|	// phy 设备					  // phy 设备 					// phy 设备				             |
		|	|	struct phy_device				struct phy_device				 struct phy_device				 |
		|	+-------struct mdio_device mdio;--------struct mdio_device mdio;--------struct mdio_device mdio;-----+
		|				//phy地址 						//phy地址 						//phy地址
		|				int addr;						int addr;						int addr;
		+---------------struct mii_bus *bus;------------struct mii_bus *bus;------------struct mii_bus *bus;
						struct device dev;				struct device dev;				struct device dev;
	
	*/
	ret = stmmac_mdio_register(ndev);
	
	//注册网卡设备
	ret = register_netdev(ndev);
}

drivers\net\ethernet\stmicro\stmmac\stmmac_mdio.c

/*


							//网卡设备												//gmac控制器所有配置信息
					+---- struct net_device *ndev									struct plat_stmmacenet_data *plat;
					|																+----struct device_node *phy_node; //phy配置节点
					|                                                               |
					|                                                               |
	//mido总线		|	                                                            |
+---struct mii_bus  |                                                               |
|		void *priv;-+                                                               |
|		struct device dev;                                                          |
|			struct device_node	*of_node;-------------------------------------------+
|		const char *name;//"stmmac";
|		int (*read)(struct mii_bus *bus, int addr, int regnum);
|		int (*write)(struct mii_bus *bus, int addr, int regnum, u16 val);
|		int (*reset)(struct mii_bus *bus);
|		struct device dev;
|			struct class	*class; 
|				.name		= "mdio_bus",
|				
|		/* list of all PHYs on bus */
|	+---struct mdio_device *mdio_map[PHY_MAX_ADDR];//mdio设备数组 ---------------------------------------+
|	|                                                                                                    |
|	|                                                                                                    |
|	|                                                                                                    |
|	|	// phy 设备                     // phy 设备                     // phy 设备                      |
|	|	struct phy_device 				struct phy_device				 struct phy_device               |
|	+-------struct mdio_device mdio;--------struct mdio_device mdio;--------struct mdio_device mdio;-----+
|				//phy地址						//phy地址						//phy地址
|				int addr;						int addr;						int addr;
+---------------struct mii_bus *bus;------------struct mii_bus *bus;------------struct mii_bus *bus;
				struct device dev;				struct device dev;				struct device dev;
		
*/
int stmmac_mdio_register(struct net_device *ndev)
{
	int err = 0;
	struct mii_bus *new_bus;
	struct stmmac_priv *priv = netdev_priv(ndev);
	struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data;
	struct device_node *mdio_node = priv->plat->mdio_node;
	struct device *dev = ndev->dev.parent;
	int addr, found, max_addr;

...

	//分配 mii_bus
	new_bus = mdiobus_alloc();
	if (!new_bus)
		return -ENOMEM;

	if (mdio_bus_data->irqs)
		memcpy(new_bus->irq, mdio_bus_data->irqs, sizeof(new_bus->irq));

#ifdef CONFIG_OF
	if (priv->device->of_node)
		mdio_bus_data->reset_gpio = -1;
#endif

	//总线名
	new_bus->name = "stmmac";

	//总线读写 复位 函数
	...
	new_bus->read = &stmmac_mdio_read;
	new_bus->write = &stmmac_mdio_write;
	max_addr = PHY_MAX_ADDR;//32
	new_bus->reset = &stmmac_mdio_reset;


	/*
	总线 ---bind--- 网卡设备
	struct mii_bus
		void *priv;---------+ struct net_device *ndev

	*/
	new_bus->priv = ndev;
	new_bus->phy_mask = mdio_bus_data->phy_mask;
	new_bus->parent = priv->device;

	/*

	struct mii_bus

	struct stmmac_priv 
		struct plat_stmmacenet_data *plat
			struct device_node *mdio_node;
	*/
	/* 注册 mdio总线
	
	目前分析的主要工作:
	1
	struct mii_bus
		struct device dev;								struct plat_stmmacenet_data
			struct device_node	*of_node;-------------------struct device_node *phy_node; //phy配置节点
	
	2
	将总线上 0- 32 地址 识别到但是并未注册的 phy设备,注册到 mdio总线,即添加到 将该phy添加到 mdio_map[]数组

	*/
	err = of_mdiobus_register(new_bus, mdio_node);
	if (err != 0) {
		dev_err(dev, "Cannot register the MDIO bus\n");
		goto bus_register_fail;
	}

...

}

drivers\of\of_mdio.c

/* 注册 mii_bus 并且根据设备树信息创建 PHYS 

struct mii_bus

struct stmmac_priv 
	struct plat_stmmacenet_data *plat
		struct device_node *mdio_node;


目前分析的主要工作:
1
struct mii_bus
	struct device dev;								struct plat_stmmacenet_data
		struct device_node	*of_node;-------------------struct device_node *phy_node; //phy配置节点

2
将总线上 0- 32 地址 识别到但是并未注册的 phy设备,注册到 mdio总线,即添加到 将该phy添加到 mdio_map[]数组

*/
int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
{
	struct device_node *child;
	bool scanphys = false;
	int addr, rc;

	if (!np)
		return mdiobus_register(mdio);

	/* Do not continue if the node is disabled */
	if (!of_device_is_available(np))
		return -ENODEV;

	/* PHY addresses to be ignored when probing */
	//设置为全真
	mdio->phy_mask = ~0;

	/*

	struct mii_bus
		struct device dev; 								struct plat_stmmacenet_data
			struct device_node	*of_node;-------------------struct device_node *phy_node; //phy配置节点

	*/
	mdio->dev.of_node = np;
	mdio->dev.fwnode = of_fwnode_handle(np);


	// Register the MDIO bus 
	/*
	
	主要工作:
	将总线上 0- 32 地址 识别到但是并未注册的 phy设备,注册到 mdio总线,即添加到 将该phy添加到 mdio_map[]数组
	
	*/

	rc = mdiobus_register(mdio);

	...

	return 0;

unregister:
	mdiobus_unregister(mdio);
	return rc;
}
EXPORT_SYMBOL(of_mdiobus_register)

#define mdiobus_register(bus) __mdiobus_register(bus, THIS_MODULE)

drivers\net\phy\mdio_bus.c

/*

主要工作:
将总线上 0- 32 地址 识别到但是并未注册的 phy设备,注册到 mdio总线,即添加到 将该phy添加到	mdio_map[]数组

*/
int __mdiobus_register(struct mii_bus *bus, struct module *owner)
{
	struct mdio_device *mdiodev;
	int i, err;
	struct gpio_desc *gpiod;
...

	bus->owner = owner;
	bus->dev.parent = bus->parent;
	bus->dev.class = &mdio_bus_class;
	bus->dev.groups = NULL;
	dev_set_name(&bus->dev, "%s", bus->id);

...

	//32
	/*
		检索 探测时忽略的PHY地址
	*/
	for (i = 0; i < PHY_MAX_ADDR; i++) {
		if ((bus->phy_mask & (1 << i)) == 0) {
			struct phy_device *phydev;

		 /*
		
		遍历 0 - 32 地址中每一个地址,检查该地址是否有 phy,并检测有没有注册到mdio总线,没有的话 注册到mdio总线,
		即 将该phy添加到	mdio_map[]数组
		*/

			phydev = mdiobus_scan(bus, i);
			if (IS_ERR(phydev) && (PTR_ERR(phydev) != -ENODEV)) {
				err = PTR_ERR(phydev);
				goto error;
			}
		}
	}

...
}
EXPORT_SYMBOL(__mdiobus_register);


/*

遍历 0 - 32 地址中每一个地址,检查该地址是否有 phy,并检测有没有注册到mdio总线,没有的话 注册到mdio总线,
即 将该phy添加到  mdio_map[]数组
*/
struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)
{
	struct phy_device *phydev;
	int err;

	//检查该地址 是否有 phy
	phydev = get_phy_device(bus, addr, false);
	if (IS_ERR(phydev))
		return phydev;

...

	//注册该 PHY
	//就是将该 phy 添加到 mdio_map[]数组。 即注册
	err = phy_device_register(phydev);
	if (err) {
		phy_device_free(phydev);
		return ERR_PTR(-ENODEV);
	}

	return phydev;
}
EXPORT_SYMBOL(mdiobus_scan);

drivers\net\phy\phy_device.c

 /*
向 mdio总线添加 phy 设备
	
*/
int phy_device_register(struct phy_device *phydev)
{
	int err;

/*
struct phy_device
	struct mdio_device mdio;

*/
/*
工作:
	检查 0 - 32 之前没有检测的地址 是否有phy存在,有的添加到 mdio_map[]数组
*/
	err = mdiobus_register_device(&phydev->mdio);
	if (err)
		return err;

	/* Deassert the reset signal */
	//复位 PHY
	phy_device_reset(phydev, 0);

	/* Run all of the fixups for this PHY */
	//暂时不知道 phy_fixup_list 是干啥的
	// 如果该phy有设定一些额外的操作 这里就会执行phy 所设定的额外操作 如led灯控制等
	err = phy_scan_fixups(phydev);

	...

}
EXPORT_SYMBOL(phy_device_register);

drivers\net\phy\mdio_bus.c

/*
struct phy_device
	struct mdio_device mdio;

*/
/*
工作:
	检查 0 - 32 之前没有检测的地址 是否有phy存在,有的添加到 mdio_map[]数组
*/

int mdiobus_register_device(struct mdio_device *mdiodev)
{
	int err;

/*
struct phy_device
	struct mdio_device mdio;
		struct mii_bus *bus;
			//list of all PHYs on bus
			struct mdio_device *mdio_map[PHY_MAX_ADDR];
*/
//如果已经存在于 mdio_map[]数组,直接返回
	if (mdiodev->bus->mdio_map[mdiodev->addr])
		return -EBUSY;

	if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY) {
		err = mdiobus_register_gpiod(mdiodev);
		if (err)
			return err;
	}

//添加到 mdio_map[]数组。 即注册
	mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev;

	return 0;
}
EXPORT_SYMBOL(mdiobus_register_device);

至此暂时分析到的 GMAC主要工作
1 创建网卡设备 struct net_device,并根据 GMAC控制器 设备树资源,整合信息。
2 创建 mido总线 struct mii_bus,并遍历总线上 0 - 31 每一个地址,检查该地址是否有 phy,并检测有没有注册到mdio总线,没有的话 注册到mdio总线,就是 将该phy添加到 mdio_map[]数组
3 注册网卡设备

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

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

相关文章

无线烧录器(2)

传送门&#xff1a;连接 继上次完成了ESP32无线烧录器。那么有一个问题就是&#xff0c;一堆人都在一起想用这个来无线烧录呢&#xff0c;那么固件中定死的热点名称岂不是会互相的扰乱。所以需要自己编译属于自己的热点名称。 首先需要准备以下几样东西。 Ubuntu &#xff08;需…

excel数据透视表:善用这些功能,提高工作效率!下篇

在上篇文章中&#xff0c;我们为大家分享了透视表的前5条妙用&#xff0c;分别是合并同类项、按条件汇总数据、统计非重复数据、排名、批量创建表格&#xff0c;不知道大家都还记得吗&#xff1f;那么今天我们书接上回&#xff0c;继续为大家分享关于透视表的后5条妙用。 六、…

密码学_SM4国密算法

目录 简介 两类参数 加密流程 RKi和Ki4的生成方法 Xi4的生成方法​编辑 左移的概念 简介 在商用密码体系中&#xff0c;SM4主要用于数据加密&#xff0c;其算法公开&#xff0c;分组长度与密钥长度均为128bit&#xff0c;加密算法与密钥扩展算法都采用32轮非线性迭代结构&…

2022年终总结——脚踏实地,勇敢做自己

不知不觉又来到了年尾&#xff0c;即便都要迈入2023年了&#xff0c;但是在写日期的时候还会下意识的写成2021&#xff0c;这可能就是感觉一年额外漫长的原因吧。其实还是不希望过年的&#xff0c;毕竟跨过了新年要长岁了&#xff0c;看着日益增长的年岁还是会有点难过的&#…

JVM- 第一章-JVM与Java体系结构

JVM-第一章-JVM与Java体系结构1.1. 前言1.2开发人员如何看待上层框架1.3架构师每天都在思考什么&#xff1f;1.4我们为什么要学习JVM1.5 Java VS C1.6面向人群及参考书籍1.1. 前言 作为Java工程师的你曾被伤害过吗&#xff1f;你是否也遇到过这些问题&#xff1f; ● 运行着…

尚医通-数据字典添加缓存-配置nginx(十五)

目录&#xff1a; &#xff08;1&#xff09;后台系统 数据字典-添加缓存 &#xff08;2&#xff09;后台系统配置nginx &#xff08;1&#xff09;后台系统 数据字典-添加缓存 Spring Cache Redis 缓存数据 Spring Cache 是一个非常优秀的缓存组件。自Spring 3.1起&#xff…

CSDN第19次竞赛题解与总结

2022/12/28 19:00 CSDN第19次竞赛火热进行。 本场竞赛由「电子工业出版社 & CSDN」联合主办。 《Python机器学习算法与实战》 本书基于Python语言&#xff0c;结合实际的数据集&#xff0c;介绍如何使用机器学习与深度学习算法&#xff0c;对数据进行实战分析&#xff0c;在…

MySQL 启动日志报错:Starting MySQL. ERROR! The server quit without updating PID file

一 问题描述 1.1 问题描述 1.首先通过切换到mysql数据库所属用户&#xff1a; hd-mysql [rootlocalhost local]# su hd-mysql 2.启动mysql&#xff0c;报错 [hd-mysqllocalhost data]$ service mysql start Starting MySQL. ERROR! The server quit without updating PID…

50+Vue经典面试题源码级详解,你值得收藏!(一天更新一题,持续更新!!!)

Vue经典面试题源码级详解1、Vue组件之间通信方式有哪些&#xff1f;分析&#xff1a;思路分析&#xff1a;回答范例&#xff1a;1. 组件通信常用方式有以下8种&#xff1a;2、根据组件之间关系讨论组件通信最为清晰有效2、v-if 和 v-for哪个优先级更高分析&#xff1a;思路分析…

学习C语言笔记:数据和C

C Primer Plus &#xff08;第6版&#xff09;中文版第3章 学习内容&#xff1a; 1.关键字——int、short、long、unsigned、char、float、double、_Bool、_Complex、_Imaginary; 2.运算符——sizeof(); 3.函数——scanf(); 4.整数类型和浮点数类型的区别; 5.如何书写整型和浮点…

STL简介

目录 什么是STL STL的版本 STL的六大组件 如何学习STL STL的缺陷 结束语 什么是STL STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架 STL的版本…

公司来了个卷王,我愿称之为王中王,让人崩溃

前几天我们公司一下子也来了几个新人&#xff0c;这些年前人是真能熬啊&#xff0c;本来我们几个老油子都是每天稍微加会班就打算走了&#xff0c;这几个新人一直不走&#xff0c;搞得我们也不好走。 2023年春招就要开始了&#xff0c;最近内卷严重&#xff0c;各种跳槽裁员&a…

从TikTok功能优化,抓住直播带货机遇

从今年流出的各类消息总结&#xff0c;TikTok的发展势头愈发迅猛&#xff0c;无论是布局东南亚各个站点&#xff0c;还是在其下载量的激增&#xff0c;都迸发出了前所未有的劲头&#xff0c;其对本身的优化也一直在进行。那么今年&#xff0c;TikTok都进行了哪些功能优化呢&…

p5.js 使用npm安装p5.js后如何使用?

本文简介 点赞 关注 收藏 学会了 在 《p5.js 光速入门》 中都是使用 CDN 的方式去使用 p5.js 的&#xff0c;不太符合当下的开发习惯。 不管是使用 Vue 还是 React&#xff0c;我们日常项目大部分应该都是使用脚手架的方式进行开发了&#xff0c;按照 《p5.js 光速入门》 …

互联网营销的基础方法

推广目的 1.让目标消费群在最短的时间内认知新产品的功能、效果,缩短新产品推广期的时间长度,尽快进入成长期,创造效益。 2.使目标消费群产生试用的欲 望,并逐步将其培育成品牌忠诚者。 3.提高品牌知名度和美誉度。 4.提高现场售点的产品的销量。 5.巩固通路经销商的客…

ARM64内存虚拟化分析(1)基础知识

1 两级转换 1.1 什么是stage2转换 Stage2转换允许hypervisor控制虚拟机VM中内存视图。它允许hypervisor控制一个虚拟机可以访问哪块内存映射的系统资源&#xff0c;以及这些资源应该出现在虚拟机地址空间的哪个位置。 这种控制内存访问的能力对于隔离是非常重要的。Stage2转换…

【C语言 全局 整形变量 布尔变量 数组变量 指针变量 结构体位域变量 枚举变量被其他.C文件相互访问】

【C语言 全局-> 整形变量 布尔变量 数组变量 指针变量 结构体位域变量 枚举变量被其他.C文件相互访问】【0】UI设计【1】项目概述【2】mainwindow.h头文件解析【3】头文件源码【4】mianwindow.c源文件解析【5】源文件源码【6】main文件解析源码【7】编译-链接-运行结果学完这…

对Java中String类的解释

文章目录一、String 的常用方法1.字符串的构造以及字符串常量池2.String 类中对象的比较3.字符串查找4.转化5.字符串替换6.字符串拆分7.字符串截取二、StringBuilder 和 StringBuffer1.引入两者的原因2.相关修改操作的介绍一、String 的常用方法 首先&#xff0c;在介绍 Strin…

[年终总结] 2023,希望一切都能够好起来

0. 引言 2022年&#xff0c;依旧是一个特殊的年份&#xff0c;从前年开始&#xff0c;我就开始撰写年终总结&#xff0c;感兴趣的大家可以看一看2020年终总结和2021年年终总结。 一年比一年过的快速&#xff0c;我也感觉到一年比一年过的更加珍惜。曾经以为自己尚且年轻&…

linux /dev/mapper/rhel-root分区扩容

命令查看linux内存占用情况 /dev/mapper/rhel-root分区已满&#xff0c;主要是东西太多&#xff0c;改善了也删了&#xff0c;但是还是不够用。在扩容之前在首先针对虚拟机硬盘进行扩展 接下来就是在linux上操作了 1、输入fdisk -l 这里显示我已经有了三个分区&#xff0c;sd…