<kernel>kernel 6.4 USB-之-usb_new_device()分析

news2024/12/28 19:28:15

<kernel>kernel 6.4 USB-之-usb_new_device()分析

kernel 6.4 USB系列文章如下:
<kernel>kernel 6.4 USB-之-hub_event()分析
<kernel>kernel 6.4 USB-之-port_event()分析
<kernel>kernel 6.4 USB-之-hub_port_connect_change()分析
<kernel>kernel 6.4 USB-之-hub_port_connect()分析
<kernel>kernel 6.4 USB-之-hub_port_init()分析
<kernel>kernel 6.4 USB-之-usb_new_device()分析

本文是基于linux kernel 6.4版本内核分析;源码下载路径:linux kernel
在这里插入图片描述

一、前言

在Linux内核的USB子系统中,用于处理新连接的USB设备的函数usb_new_device。它的主要作用是初始化新连接的USB设备,并将其添加到设备系统中。
这个函数接受一个指向struct usb_device的指针作为参数,这个结构体包含了新连接的USB设备的所有信息。
函数的主要步骤和作用如下:
(1) 如果USB设备不是根集线器,则禁用设备唤醒功能。
(2) 向运行时电源管理框架报告设备处于活动状态,并启用设备的自动挂起功能。
(3) 禁用所有设备的自动挂起功能。
(4) 调用usb_enumerate_device函数读取设备的描述符。
(5) 如果读取描述符失败,则跳转到错误处理部分。
(6) 将设备的设备号设置为USB设备的主设备号和次设备号的组合。
(7) 调用announce_device函数公告设备。
(8) 如果设备有序列号、产品名或制造商名,则将这些信息添加到设备的随机性中。
(9) 启用设备的异步挂起功能。
(10) 检查集线器或固件是否将此端口标记为不可移动。
(11) 调用device_add函数将设备注册到设备系统中。
(12) 如果设备有父设备,则在设备和USB端口设备之间创建链接文件。
(13) 调用usb_create_ep_devs函数为设备创建端点设备。
(14) 将设备标记为最后一次忙碌。

如果所有步骤都成功,则返回0;否则,将设备状态设置为未连接,并禁用运行时电源管理,然后返回错误代码。

二、usb_new_device()函数

usb_new_device()函数内容如下:

int usb_new_device(struct usb_device *udev)
{
	int err;

	if (udev->parent) {
		/* Initialize non-root-hub device wakeup to disabled;
		 * device (un)configuration controls wakeup capable
		 * sysfs power/wakeup controls wakeup enabled/disabled
		 */
		device_init_wakeup(&udev->dev, 0);
	}

	/* Tell the runtime-PM framework the device is active */
	pm_runtime_set_active(&udev->dev);
	pm_runtime_get_noresume(&udev->dev);
	pm_runtime_use_autosuspend(&udev->dev);
	pm_runtime_enable(&udev->dev);

	/* By default, forbid autosuspend for all devices.  It will be
	 * allowed for hubs during binding.
	 */
	usb_disable_autosuspend(udev);

	err = usb_enumerate_device(udev);	/* Read descriptors */
	if (err < 0)
		goto fail;
	dev_dbg(&udev->dev, "udev %d, busnum %d, minor = %d\n",
			udev->devnum, udev->bus->busnum,
			(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
	/* export the usbdev device-node for libusb */
	udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
			(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));

	/* Tell the world! */
	announce_device(udev);

	if (udev->serial)
		add_device_randomness(udev->serial, strlen(udev->serial));
	if (udev->product)
		add_device_randomness(udev->product, strlen(udev->product));
	if (udev->manufacturer)
		add_device_randomness(udev->manufacturer,
				      strlen(udev->manufacturer));

	device_enable_async_suspend(&udev->dev);

	/* check whether the hub or firmware marks this port as non-removable */
	set_usb_port_removable(udev);

	/* Register the device.  The device driver is responsible
	 * for configuring the device and invoking the add-device
	 * notifier chain (used by usbfs and possibly others).
	 */
	err = device_add(&udev->dev);
	if (err) {
		dev_err(&udev->dev, "can't device_add, error %d\n", err);
		goto fail;
	}

	/* Create link files between child device and usb port device. */
	if (udev->parent) {
		struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
		int port1 = udev->portnum;
		struct usb_port	*port_dev = hub->ports[port1 - 1];

		err = sysfs_create_link(&udev->dev.kobj,
				&port_dev->dev.kobj, "port");
		if (err)
			goto fail;

		err = sysfs_create_link(&port_dev->dev.kobj,
				&udev->dev.kobj, "device");
		if (err) {
			sysfs_remove_link(&udev->dev.kobj, "port");
			goto fail;
		}

		if (!test_and_set_bit(port1, hub->child_usage_bits))
			pm_runtime_get_sync(&port_dev->dev);
	}

	(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
	usb_mark_last_busy(udev);
	pm_runtime_put_sync_autosuspend(&udev->dev);
	return err;

fail:
	usb_set_device_state(udev, USB_STATE_NOTATTACHED);
	pm_runtime_disable(&udev->dev);
	pm_runtime_set_suspended(&udev->dev);
	return err;
}

下面就对usb_new_device()函数内容详细分析:

2.1 第5-11行:初始化非根集线器(root hub)设备的唤醒状态

第5-11行:主要作用是初始化非根集线器(root hub)设备的唤醒状态。

这段代码的详细过程如下:

首先,检查udev(指向struct usb_device的指针,它包含了USB设备的所有信息)的parent成员。如果parent成员存在,那么说明这个设备不是根集线器(root hub)。

然后,调用device_init_wakeup函数来初始化这个设备的唤醒状态。这个函数的第一个参数是一个指向设备的指针,第二个参数是唤醒的初始状态。在这里,设备的唤醒状态被初始化为0,也就是禁用状态。

这段代码的主要作用是控制非根集线器设备的唤醒能力。在USB子系统中,设备的唤醒能力是可以被配置的。设备的唤醒能力被禁用时,设备在系统挂起(suspend)时无法唤醒系统。设备的唤醒能力被启用时,如果设备在系统挂起时有活动(例如,接收到数据),那么设备可以唤醒系统。

2.2 第13-17行:USB 设备进行电源管理

第13-17行:对 USB 设备进行电源管理的部分。

pm_runtime_set_active(&udev->dev); 这行告诉运行时电源管理框架,该设备是活动的。运行时电源管理框架是 Linux 内核中用于管理设备电源的子系统。当设备是活动的,意味着设备正在使用中,系统不会尝试将其电源关闭以节省能源。

pm_runtime_get_noresume(&udev->dev); 这行是获取设备的运行时引用,但不会尝试恢复设备。这意味着,如果设备当前处于挂起状态(即电源关闭状态),这个函数不会尝试将其唤醒。

pm_runtime_use_autosuspend(&udev->dev); 这行是启用设备的自动挂起功能。当设备启用了自动挂起后,如果一段时间内没有使用设备,系统会自动将其电源关闭以节省能源。

pm_runtime_enable(&udev->dev); 这行是启用设备的运行时电源管理。当设备的运行时电源管理被启用后,系统将会根据设备的使用情况,自动地调整设备的电源状态,以平衡能源消耗和性能。

总的来说,这段代码的作用是启用并配置 USB 设备的运行时电源管理,以达到节省能源的目的。

2.3 第22行:禁止 USB 设备(udev)的自动挂起功能

第22行:作用是禁止 USB 设备(udev)的自动挂起功能。

自动挂起是 Linux 内核中的一个电源管理机制,当设备在一段时间内没有被使用时,系统会自动将其电源关闭,以节省能源。然而,对于某些设备,我们可能不希望它们在空闲时被自动关闭电源,因此需要禁用它们的自动挂起功能。

这段代码的注释提到,“默认情况下,禁止所有设备的自动挂起。在绑定过程中,将允许对集线器使用此功能。” 这意味着在 USB 设备被初始化时,会禁止其自动挂起功能,但在后续的过程中,可能会根据需要启用集线器(即 USB 设备的一种)的自动挂起功能。

2.3.1 usb_disable_autosuspend()函数

路径:drivers\usb\core\driver.c
void usb_disable_autosuspend(struct usb_device *udev)
{
	pm_runtime_forbid(&udev->dev);
}
EXPORT_SYMBOL_GPL(usb_disable_autosuspend);

“这个函数阻止 @udev 设备被自动挂起,并且如果它已经被自动挂起,那么将唤醒它。调用者必须持有 @udev 设备的锁。” 这意味着,在调用这个函数之前,必须确保已经获取了设备的锁,以防止并发操作造成的问题。

路径:
void pm_runtime_forbid(struct device *dev)
{
	spin_lock_irq(&dev->power.lock);
	if (!dev->power.runtime_auto)
		goto out;

	dev->power.runtime_auto = false;
	atomic_inc(&dev->power.usage_count);
	rpm_resume(dev, 0);

 out:
	spin_unlock_irq(&dev->power.lock);
}
EXPORT_SYMBOL_GPL(pm_runtime_forbid);

其主要作用是阻止设备的运行时电源管理(runtime PM)。

struct device *dev 是函数的参数,表示要操作的设备。

spin_lock_irq(&dev->power.lock); 这行代码是获取设备电源管理的锁,防止并发操作。spin_lock_irq 是一个忙等待锁,如果锁已经被其他线程获取,那么调用线程会一直忙等待,直到获取到锁。irq 表示在获取锁的同时,禁止中断。

if (!dev->power.runtime_auto) goto out; 这行代码是检查设备的 runtime_auto 标志,如果该标志为 false,则直接跳转到 out 标签,释放锁。

dev->power.runtime_auto = false; 这行代码是将 runtime_auto 标志设置为 false,表示禁止设备的自动挂起。

atomic_inc(&dev->power.usage_count); 这行代码是原子地增加设备的使用计数。原子操作是一种不可中断的操作,保证了在多线程环境下的安全性。

rpm_resume(dev, 0); 这行代码是尝试恢复设备,即如果设备当前处于挂起状态,那么尝试唤醒设备。

spin_unlock_irq(&dev->power.lock); 这行代码是释放设备电源管理的锁,并允许中断。

EXPORT_SYMBOL_GPL(pm_runtime_forbid); 这行代码是将 pm_runtime_forbid 函数导出,使得其他的内核模块也可以调用这个函数。

总的来说,这个函数的作用是禁止设备的运行时电源管理,即使设备闲置,也不会自动挂起。

2.4 第24-32行:USB设备枚举过程中执行一些操作

第24-32行:在USB设备枚举过程中执行一些操作。

err = usb_enumerate_device(udev); 这行代码调用函数usb_enumerate_device,这个函数的作用是读取USB设备的描述符。这是USB设备枚举过程的一部分,其中设备将其能力和特性报告给主机。

if (err < 0) goto fail; 这行代码检查usb_enumerate_device函数的返回值。如果返回值小于0,表示函数执行失败,代码将跳转到fail标签处,这通常意味着出现错误处理。

dev_dbg(&udev->dev, “udev %d, busnum %d, minor = %d\n”, udev->devnum, udev->bus->busnum, (((udev->bus->busnum-1) * 128) + (udev->devnum-1))); 这行代码是用于调试的,它将USB设备的一些信息(设备编号,总线编号,次设备号)打印出来。

最后两行代码设置udev设备的设备类型和设备号。MKDEV宏用于创建一个设备号,这个设备号由主设备号(这里是USB_DEVICE_MAJOR)和次设备号组成。这里的次设备号是通过一个算式计算得到的,算式中使用了USB设备所在的总线编号和设备编号。这个设备号将用于创建设备节点,使得用户空间的程序(例如libusb)可以访问这个USB设备。

总的来说,这段代码的主要作用是在USB设备枚举过程中读取设备描述符,打印设备信息,并设置设备号,以便用户空间程序可以访问USB设备。

2.5 第35行:将USB设备的信息打印到日志中

第35行:announce_device函数的作用是将USB设备的信息打印到日志中。这通常用于调试和诊断问题,也有助于了解USB设备的状态和属性。

路径:drivers\usb\core\hub.c
static void announce_device(struct usb_device *udev)
{
	u16 bcdDevice = le16_to_cpu(udev->descriptor.bcdDevice);

	dev_info(&udev->dev,
		"New USB device found, idVendor=%04x, idProduct=%04x, bcdDevice=%2x.%02x\n",
		le16_to_cpu(udev->descriptor.idVendor),
		le16_to_cpu(udev->descriptor.idProduct),
		bcdDevice >> 8, bcdDevice & 0xff);
	dev_info(&udev->dev,
		"New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",
		udev->descriptor.iManufacturer,
		udev->descriptor.iProduct,
		udev->descriptor.iSerialNumber);
	show_string(udev, "Product", udev->product);
	show_string(udev, "Manufacturer", udev->manufacturer);
	show_string(udev, "SerialNumber", udev->serial);
}

2.6 第37-43行:USB设备枚举过程中执行一些操作

第37-43行:在USB设备枚举过程中执行一些操作。

这段代码的作用是将USB设备的一些属性(序列号、产品名称、制造商名称)添加到系统的熵池中。熵池是Linux内核用于生成随机数的一个数据结构。当需要生成一个随机数时,内核会从熵池中取出一些数据,并使用这些数据作为随机数生成算法的输入。

在这段代码中,add_device_randomness函数被用于将数据添加到熵池。这个函数的第一个参数是要添加的数据,第二个参数是数据的长度。这里的数据是USB设备的序列号、产品名称和制造商名称,这些数据都是字符串,所以使用strlen函数来获取它们的长度。

总的来说,这段代码的作用是增加系统的熵,以提高随机数的质量。这对于需要高质量随机数的应用(如加密应用)来说是非常重要的。

2.6.1 add_device_randomness()函数

路径:drivers\char\random.c
void add_device_randomness(const void *buf, size_t len)
{
	unsigned long entropy = random_get_entropy();
	unsigned long flags;

	spin_lock_irqsave(&input_pool.lock, flags);
	_mix_pool_bytes(&entropy, sizeof(entropy));
	_mix_pool_bytes(buf, len);
	spin_unlock_irqrestore(&input_pool.lock, flags);
}
EXPORT_SYMBOL(add_device_randomness);

用于向输入池添加设备或启动特定的数据,以帮助初始化输入池。

这段代码的工作流程如下:

调用 random_get_entropy 函数获取一些随机性(熵),并将其存储在 entropy 变量中。

使用 spin_lock_irqsave 函数锁定输入池,以防止其他进程在此过程中修改输入池。这个函数还会保存当前的中断状态,并在稍后恢复它。

调用 _mix_pool_bytes 函数两次,首先将 entropy 变量的内容混入输入池,然后将 buf 参数指向的数据混入输入池。

使用 spin_unlock_irqrestore 函数解锁输入池,并恢复之前保存的中断状态。

这段代码的主要作用是增加系统的随机性,以避免在大量相同的设备上,熵池具有相似的初始状态。需要注意的是,这里添加的数据并不增加熵池的熵,也就是说,它们不会增加熵池的随机性。这些数据只是用来改变熵池的初始状态,以防止在大量相同的设备上出现相同的初始状态。

2.7 第45行:启用设备的异步挂起

第45行:device_enable_async_suspend是一个内核函数,它的作用是启用设备的异步挂起(asynchronous suspend)功能。异步挂起是一种电源管理策略,它允许设备在系统空闲时进入低功耗状态,以节省电能。当设备被需要时,系统会自动将其唤醒。

在这段代码中,&udev->dev是一个指向USB设备结构体的指针。这个结构体包含了设备的各种信息,包括设备的类型、制造商、序列号等。调用device_enable_async_suspend函数后,这个USB设备就被设置为支持异步挂起。

总的来说,这段代码的作用是启用USB设备的电源管理功能,以节省系统的电能。

2.8 第48行:检查 USB 设备是否被标记为不可移除

第48行:用于检查 USB 设备是否被标记为不可移除。

set_usb_port_removable(udev); 这行代码调用了一个函数 set_usb_port_removable,该函数的参数是 udev,代表了一个 USB 设备。

set_usb_port_removable 函数的主要作用是设置 USB 设备的可移动性。具体来说,它会检查 USB 设备(或者固件)是否将这个端口标记为不可移动。如果是,那么在系统中,这个 USB 设备将被视为不可移动设备,即使物理上可以拔插。

这个函数的具体实现可能会依赖于具体的硬件和固件实现,可能会涉及到读取硬件寄存器或者固件设置等操作。

2.8.1 set_usb_port_removable()

static void set_usb_port_removable(struct usb_device *udev)
{
	struct usb_device *hdev = udev->parent;
	struct usb_hub *hub;
	u8 port = udev->portnum;
	u16 wHubCharacteristics;
	bool removable = true;

	dev_set_removable(&udev->dev, DEVICE_REMOVABLE_UNKNOWN);

	if (!hdev)
		return;

	hub = usb_hub_to_struct_hub(udev->parent);

	/*
	 * If the platform firmware has provided information about a port,
	 * use that to determine whether it's removable.
	 */
	switch (hub->ports[udev->portnum - 1]->connect_type) {
	case USB_PORT_CONNECT_TYPE_HOT_PLUG:
		dev_set_removable(&udev->dev, DEVICE_REMOVABLE);
		return;
	case USB_PORT_CONNECT_TYPE_HARD_WIRED:
	case USB_PORT_NOT_USED:
		dev_set_removable(&udev->dev, DEVICE_FIXED);
		return;
	default:
		break;
	}

	/*
	 * Otherwise, check whether the hub knows whether a port is removable
	 * or not
	 */
	wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);

	if (!(wHubCharacteristics & HUB_CHAR_COMPOUND))
		return;

	if (hub_is_superspeed(hdev)) {
		if (le16_to_cpu(hub->descriptor->u.ss.DeviceRemovable)
				& (1 << port))
			removable = false;
	} else {
		if (hub->descriptor->u.hs.DeviceRemovable[port / 8] & (1 << (port % 8)))
			removable = false;
	}

	if (removable)
		dev_set_removable(&udev->dev, DEVICE_REMOVABLE);
	else
		dev_set_removable(&udev->dev, DEVICE_FIXED);

}

这个函数的目的是设置USB设备的移除性。

函数首先获取USB设备的父设备和对应的USB集线器。然后,它设置设备的移除性为未知。

如果没有父设备,函数就会返回。否则,它会获取设备对应的USB集线器。

接着,函数会检查平台固件是否为该端口提供了信息。如果是,它会根据此信息来确定设备是否可移除。如果设备是热插拔类型,它会设置设备为可移除并返回。如果设备是硬连线类型或端口未使用,它会设置设备为固定并返回。

如果固件没有提供端口信息,函数会检查集线器是否知道端口是否可移除。它获取集线器的特性,并检查集线器是否复合。如果不是,函数会返回。

然后,函数会根据集线器的速度来确定设备是否可移除。对于超速集线器,如果设备可移除位图中的对应位被设置,它会设置设备为不可移除。对于高速集线器,如果设备可移除位图的对应位被设置,它也会设置设备为不可移除。

最后,函数会根据设备是否可移除来设置设备的移除性。如果设备可移除,它会设置设备为可移除。否则,它会设置设备为固定。

总的来说,这个函数的作用是根据设备和集线器的信息来设置USB设备的移除性。

2.9 第54-58行:注册 USB 设备

第54-58行:主要负责注册 USB 设备。

首先,它调用 device_add(&udev->dev) 函数试图将 USB 设备(由 udev 指针表示)注册到系统中。device_add 函数是设备驱动模型(device driver model)的一部分,它将设备添加到系统中并初始化设备所需的所有数据结构。

如果 device_add 函数成功执行,那么 USB 设备就被成功注册到系统中,设备驱动程序负责配置设备并调用添加设备通知链(被 usbfs 和可能的其他一些模块使用)。

如果 device_add 函数执行失败,它会返回一个错误代码。这个错误代码被存储在 err 变量中。然后,dev_err(&udev->dev, “can’t device_add, error %d\n”, err) 语句会将错误信息打印到日志中,其中 %d 是一个占位符,它会被 err 的值替换,表示具体的错误代码。最后,goto fail 语句会使程序跳转到 fail 标签所在的位置,执行错误处理的代码。

2.10 第61-80行: sysfs 文件系统中创建链接

第61-80行:主要负责在 sysfs 文件系统中创建链接,以表示 USB 设备(udev)和其父设备(一个 USB 集线器)之间的关系。

首先,它检查 udev 是否有父设备。如果没有,那么这段代码就不会执行。

如果 udev 有父设备,那么它首先获取这个父设备对应的 USB 集线器(hub)。然后,它获取 udev 所在的端口号(port1)。接着,它获取这个端口对应的 USB 端口设备(port_dev)。

接下来,它试图在 sysfs 中创建一个从 udev 到 port_dev 的链接,链接名为 “port”。如果创建失败,它会跳转到 fail 标签执行错误处理。

如果上一步成功,它会继续尝试在 sysfs 中创建一个从 port_dev 到 udev 的链接,链接名为 “device”。如果创建失败,它会首先删除刚刚创建的 “port” 链接,然后跳转到 fail 标签执行错误处理。

最后,它会检查 port1 是否在 hub 的 child_usage_bits 中被设置。如果没有被设置,那么它会设置这个位,并调用 pm_runtime_get_sync 函数增加 port_dev 的运行时 PM 引用计数。这是为了防止 port_dev 在 udev 还在使用它时被系统挂起或关闭。

总的来说,这段代码的作用是在 sysfs 中表示 USB 设备和其父设备(一个 USB 集线器)之间的关系,并在需要时防止父设备被系统挂起或关闭。

2.11 第82-85行:创建 USB 设备的端点设备,并处理设备的电源管理

第82-85行:主要负责创建 USB 设备的端点设备,并处理设备的电源管理。

首先,它调用 usb_create_ep_devs(&udev->dev, &udev->ep0, udev); 函数,该函数的目的是在 sysfs 文件系统中为 USB 设备的每个端点创建设备节点。这样,用户空间的程序就可以通过这些设备节点来访问 USB 设备的端点。在这里,&udev->dev 是 USB 设备的设备结构体,&udev->ep0 是 USB 设备的端点0的端点结构体,udev 是 USB 设备的设备结构体。

然后,它调用 usb_mark_last_busy(udev); 函数,该函数的目的是标记 USB 设备的最后一次活动时间。这个时间戳用于设备的电源管理,例如,如果设备在一段时间内没有活动,系统可能会将其挂起以节省电源。

接着,它调用 pm_runtime_put_sync_autosuspend(&udev->dev); 函数,该函数的目的是减少设备的运行时电源管理引用计数,并可能触发设备的自动挂起。在这里,&udev->dev 是 USB 设备的设备结构体。

最后,它返回 err,这是一个错误代码。如果前面的操作都成功,err 应该是 0。如果有任何操作失败,err 将是一个错误代码。

总的来说,这段代码的作用是创建 USB 设备的端点设备,并处理设备的电源管理。

2.12 第87-91行:处理 USB 设备连接失败

第87-91行:用于处理 USB 设备连接失败的情况。

首先,usb_set_device_state(udev, USB_STATE_NOTATTACHED); 这行代码将 USB 设备的状态设置为 USB_STATE_NOTATTACHED,表示设备没有被连接到系统。

接着,pm_runtime_disable(&udev->dev); 这行代码调用电源管理(Power Management,简称 PM)函数,禁用设备的运行时电源管理。这意味着设备将不会在运行时进入低功耗状态。

然后,pm_runtime_set_suspended(&udev->dev); 这行代码将设备的运行时 PM 状态设置为挂起(suspended)。这通常意味着设备已被关闭或者不再活动。

最后,return err; 这行代码返回一个错误代码,表示设备连接失败。

总的来说,这段代码的作用是在 USB 设备连接失败时,将设备状态设置为未连接,并禁用设备的运行时电源管理。

三、总结

usb_new_device()主要是将USB设备添加到系统中,通过device_add()添加到系统中,该函数会匹配对于USB的驱动函数 进一步调用设备驱动的probe函数,后续就是设备驱动的内容了。

USB插入的整体流程大部是在hub驱动中完成的。目前hub部分的内容基本分析完成。

后面会继续分析 usb_control_msg_send()等函数 与USB设备的通信过程,以及以U盘插入后匹配storage 驱动的内容等等。

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

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

相关文章

​直播预告丨特邀中国人寿Soul专家分享APP合规与稳定性治理经验

由软件绿色联盟主办的月度技术活动将于7月28日19点进行线上直播。本次直播以“APP安全合规与稳定性治理”为主题&#xff0c;特邀中国人寿、Soul的专家&#xff0c;分别为大家带来《中国人寿寿险APP合规之路经验分享》、《Soul Android 端稳定性治理》议题直播。赶紧预约↑吧&a…

JAVA JNA 调用C接口的三种方式

文章目录 1. 准备一个共享库文件2. JNA姿势1—继承Library接口3. JNA姿势2—直接NativeLibrary.getInstance3. JNA姿势3—Native方法 1. 准备一个共享库文件 test.c #include <stdio.h> int test(char *input){printf("input:%s\n",input);return 0; }libtes…

智能离子风棒联网监控静电消除器的主要特点和功能

智能离子风棒联网监控静电消除器是一种利用互联网技术实现远程监测和控制的设备。它可以通过传感器感知静电水平&#xff0c;并将数据传输到云端服务器进行处理和存储。用户可以通过手机、电脑等终端设备&#xff0c;通过互联网接入平台&#xff0c;实时查看静电水平、工作状态…

EasyExcel实现Excel导出

文章目录 EasyExcel引入依赖项目结构导出模板类ControllerServiceServiceImplmapper 启动项目PostMan测试 EasyExcel EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。 他能让你在不用考虑性能、内存的等因素的情况下&#xff0c;快速完成Excel的…

SQL 数据库

安装配置 【1】 MySQL安装配置教程&#xff08;超级详细、保姆级&#xff09; 【2】 MySQLNavicat安装配置教程&#xff08;超级详细、保姆级&#xff09; 学习资料 【戴师兄】SQL入门免费教程 刷题链接&#xff1a;https://share.mubu.com/doc/4BHMMbbvIMb 学习笔记&#xf…

【无标题】CloudOS:物联网开发平台,云上开发,边端交付

什么是物联网&#xff1f; 物联网&#xff08;Internet of Things&#xff0c;简称IoT&#xff09;是指通过各种信息传感器、射频识别技术、全球定位系统、红外感应器、激光扫描器等各种装置与技术&#xff0c;实时采集任何需要监控、 连接、互动的物体或过程&#xff0c;采集…

RFID数据采集设备怎么选择?

RFID技术具有非接触识别的特性&#xff0c;可对贴有RFID标签的物体进行远距离非接触的批量识别&#xff0c;也是当下发展成熟的数据采集技术之一。RFID数据采集设备可对标签信息进行处理&#xff0c;并将读取到的标签信息传输到上位机中&#xff0c;针对不同的应用场景&#xf…

Linux系统root用户使用mv命令移动“/“根目录所有文件恢复方法

在使用mv命令对文件进行移动时&#xff0c;由于操作失误移动了根目录&#xff0c;导致除了cd命令&#xff0c;其他命令都无法使用。此时可以使用./ tab键查看当前目录下的文件此时无法再通过mv命令移动回去&#xff0c;因为执行mv命令的脚本ld-linux-x86-64.so.2找不到了。 正…

代码实现判断程序是32位还是64位

nuget 引入 Vanara.PInvoke.Kernel32 测试程序&#xff1a; using Vanara.PInvoke;var isExe Kernel32.GetBinaryType("C:\Windows\notepad.exe", out var type); if (!isExe) {return; } Console.WriteLine(type); // SCS_64BIT_BINARY如果是 32 位程序&#xff0…

学习JAVA打卡第三十六天

应用举例 例子&#xff1a;熟悉带finally子语句的try-catch语句&#xff0c;语句格式如下&#xff1a; 其执行机制是&#xff1a;在执行try-catch语句后执行finally语句&#xff0c;也就是说无论在try部分是否发生异常finally语句都会被执行。 但需要注意以下两种特殊情况&am…

Python“牵手”速卖通商品详情API接口运用场景及功能介绍

速卖通电商API接口是针对速卖通提供的电商服务平台&#xff0c;为开发人员提供了简单、可靠的技术来与速卖通电商平台进行数据交互&#xff0c;实现一系列开发、管理和营销等操作。其中包括商品详情API接口&#xff0c;通过这个API接口商家可以获取商品的详细信息&#xff0c;包…

【C++】C++11中比较重要的内容介绍

C11 前言正式开始统一的初始化列表{ }初始化对象{ }为容器初始化赋值运算符重载也支持{} 声明autodecltypenullptr STL中一些变化arrayforward_listunordered_map 和 unordered_set 右值引用表达式左值和右值左值右值 右值引用的使用场景移动构造和移动赋值重载右值分类移动构造…

使用 OpenTelemetry 构建可观测性 05 - 传播和行李(Propagation Baggage)

我们开发的应用程序可能具有不同的形态和架构&#xff1a;有些是单体应用&#xff0c;有些是微服务。为单体应用程序添加遥测数据相对来说简单&#xff0c;因为所有数据都在同一进程中。然而对于微服务应用程序&#xff0c;情况可能会更具挑战性。 通常&#xff0c;分布式微服…

C++信息学奥赛1135:配对碱基链

#include <iostream> #include <string> using namespace std;int main() {string arr;cin >> arr; // 输入字符串for (int i 0; i < arr.length(); i) {if (arr[i] A) {cout << "T"; // 如果当前字符是A&#xff0c;则输出T}else if…

PPPoE vs 静态:网络中的最佳选择

在企业网络中&#xff0c;选择适合的网络连接方式对于网络性能和安全至关重要。今天我将和大家分享关于PPPoE和静态IP地址的知识&#xff0c;探讨它们在企业网络中的优劣和最佳选择。本文将为您提供详细的分析和解决方案&#xff0c;帮助您在选择网络连接方式时做出明智的决策。…

【论文阅读】自动驾驶安全的研究现状与挑战

文章目录 摘要1.引言1.1.自动驾驶安全1.2.攻击面1.3.内容和路线图 2.自动驾驶技术2.1.组成2.2.技术 3.传感器安全3.1.照相机3.2.GNSS&#xff08;全球导航系统&#xff09;/IMU&#xff08;惯性测量单元&#xff09;3.3.超声波传感器3.4.毫米波雷达3.5.激光雷达3.6.多传感器交叉…

psycopg2 使用dbutils 工具封装

1.什么是dbutils Dbutils是一套工具&#xff0c;可为数据库提供可靠&#xff0c;持久和汇总的连接&#xff0c;该连接可在各种多线程环境中使用。 2.使用代码记录 db_config.py 数据库配置类&#xff1a; # -*- coding: UTF-8 -*- import psycopg2# 数据库信息 DB_TEST_HO…

0823|C++day3 类+this指针+类中的特殊成员函数(6种)

一、思维导图 二、作业 要求&#xff1a; 设计一个Per类。类中包含私有成员&#xff1a;姓名、年龄、指针成员身高、体重&#xff1b; 再设计一个Stu类&#xff0c;类中包含私有成员&#xff1a;成绩、Per类对象 p1&#xff1b; 设计这两个类的构造函数、析构函数和拷贝构造函数…

Spring练习30---用户列表的展示,其实用户列表的展示(上)

1、我们其实刚才分析了&#xff0c;用户表与表之间的关系是多对多&#xff0c;我们内部已经用到角色的信息了&#xff0c;那么在实体描述的&#xff0c;就得描述user实体和角色的关系 2、一个用户可以有多个角色 3、所以我在描述实体关系的时候&#xff0c;我咋写&#xff0c;…

玩转Mysql系列 - 第5天:DML操作汇总,确定你都会?

这是Mysql系列第5篇。 环境&#xff1a;mysql5.7.25&#xff0c;cmd命令中进行演示。 DML(Data Manipulation Language)数据操作语言&#xff0c;以INSERT、UPDATE、DELETE三种指令为核心&#xff0c;分别代表插入、更新与删除&#xff0c;是必须要掌握的指令&#xff0c;DML…