Rockchip RK3399 - linux下抓取usb数据包

news2024/9/27 10:06:18

----------------------------------------------------------------------------------------------------------------------------

开发板 :NanoPC-T4开发板eMMC16GBLPDDR34GB
显示屏 :15.6英寸HDMI接口显示屏u-boot2017.09linux4.19
----------------------------------------------------------------------------------------------------------------------------

注意:本节介绍的内容基于《Rockchip RK3399 - linux-headers制作》中移植的运行环境:内核版本4.19.193以及debian 11根文件系统。

一、安装usbmon

usbmonusb monitor,是linux内置的usb抓包工具。usbmon本质是一个内核模块,模块的位置:/lib/modules/4.19.193/kernel/drivers/usb/mon/usbmon.ko

想要启用usbmon,必须挂载debugfs并加载usbmon模块。

1.1 挂载debugfs文件系统

在开发板debian执行如下命令,如果提示已经挂载,则下次抓包就无需运行该命令了,表示系统默认会挂载该文件系统。

root@SOM-RK3399v2:/# mount -t debugfs none /sys/kernel/debug
mount: /sys/kernel/debug: none_debugs already mounted or mount point busy.

如上所示, debian系统默认已经挂载了debugfs文件系统,无需再去手动挂载。

1.2 安装usbmon模块

需要注意的是:如果你已经将usbmon编译到内核中,就不需要安装了。

确认内核支持usbmon模块

root@SOM-RK3399v2:/# ls /sys/module/usbmon
ls: cannot access '/sys/module/usbmon': No such file or directory

如上所示,目前内核不支持usbmon模块,需要手动安装usbmon模块。

执行如下命令安装usbmon

root@SOM-RK3399v2:/# modprobe usbmon
bash: modprobe: command not found

提示未找到命令则是因为/usr/sbin(或者/sbin)默认没有加到PATH;通过修改profile文件:

root@SOM-RK3399v2:/# vim /etc/profile

找到设置·PATH·的行,添加:

export PATH=/usr/sbin:$PATH

要想马上生效还要运行 source /etc/profile不然只能在下次重进此用户时生效。

重新运行命令:

root@SOM-RK3399v2:/# modprobe usbmon
root@SOM-RK3399v2:/# ls /sys/module/usbmon
coresize  holders  initsize  initstate  notes  refcnt  sections  srcversion  taint  uevent

这里的原理是,usbmon是一个模块,使用modprobe安装该模块后,该模块内部调用debugfs相关的API,这样在 /sys/kernel/debug/usb目录下便形成了usbmon这个目录。

查看/sys/kernel/debug/usb/usbmon目录,

root@SOM-RK3399v2:/# ls  /sys/kernel/debug/usb/usbmon
0s  0u  1s  1t  1u  2s  2t  2u  3s  3t  3u  4s  4t  4u  5s  5t  5u  6s  6t  6u

发现该目录下有以下内容:0s、0u、1s、1t、1u、2s、2t、2u等,其中1代表bus1,2代表bus2,0代表所有USB总线。

1.2.1 开机自动加载模块

这里有一个问题,就是内核4.19.193版本启动的时候为啥没有自动去加载/lib/modules/4.19.193/目录下的驱动模块,如果我们想让系统能自动加载该目录下的驱动模块,我们应该怎么做?

(1) 使用命令depmod -a

depmod命令用于分析可载入模块的相依性,-a参数的作用是探测所有的模块,建立模块的依赖关系,更新/lib/modules/4.19.193/modules.dep文件;

(2) 将模块设置为自动加载

/etc/modules文件里面添加我们要加载的驱动名(一个驱动占一行);

# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
usbmon
1.2.2 重启验证

上述操作完成后,重启系统,使用 lsmodcat /proc/modulesls /sys/module/usbmon命令查看驱动是否已经加载。

root@SOM-RK3399v2:/# lsmod
Module                  Size  Used by
algif_hash             20480  1
algif_skcipher         16384  1
af_alg                 24576  6 algif_hash,algif_skcipher
bnep                   24576  2
hci_uart               61440  1
btbcm                  16384  1 hci_uart
serdev                 20480  1 hci_uart
hid_logitech_hidpp     36864  0
crct10dif_ce           16384  0
l2tp_ppp               24576  0
l2tp_netlink           24576  1 l2tp_ppp
l2tp_core              28672  2 l2tp_ppp,l2tp_netlink
ip6_udp_tunnel         16384  1 l2tp_core
udp_tunnel             16384  1 l2tp_core
pppox                  16384  1 l2tp_ppp
joydev                 28672  0
bcmdhd               1695744  0
cfg80211              638976  1 bcmdhd
hid_logitech_dj        20480  0
uio_pdrv_genirq        16384  0
uio                    20480  1 uio_pdrv_genirq
binfmt_misc            20480  1
usbmon                 36864  0                       # 这里
ledtrig_netdev         16384  0
nfsd                  344064  1
ip_tables              28672  0
root@SOM-RK3399v2:/# ls /sys/module/usbmon
coresize  holders  initsize  initstate  notes  refcnt  sections  srcversion  taint  uevent

二、抓取USB数据

2.1 确usb设备挂在哪条总线

首先需要获取想要监测的设备所在的总线以及设备号。linux中查看USB设备列表以及USB设备详细信息的有多种方法:

2.1.1 内核日志

我们在开发板上随便找一个USB接口插入USB触摸屏,内核输出日志如下:

[ 1173.581227] logitech-hidpp-device 0003:046D:4052.0006: HID++ 4.5 device connected.
[ 1182.859503] usb 1-1.2: new full-speed USB device number 4 using ehci-platform
[ 1182.959039] usb 1-1.2: New USB device found, idVendor=1a86, idProduct=e5e3, bcdDevice= 0.00
[ 1182.959245] usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 1182.959281] usb 1-1.2: Product: USB2IIC_CTP_CONTROL
[ 1182.959343] usb 1-1.2: Manufacturer: wch.cn
[ 1182.977982] input: wch.cn USB2IIC_CTP_CONTROL as /devices/platform/fe3c0000.usb/usb1/1-1/1-1.2/1-1.2:1.0/0003:1A86:E5E3.0007/input/input13
[ 1183.034737] hid-generic 0003:1A86:E5E3.0007: input,hidraw4: USB HID v1.00 Device [wch.cn USB2IIC_CTP_CONTROL] on usb-fe3c0000.usb-1.2/input0
[ 1183.142034] input: wch.cn USB2IIC_CTP_CONTROL as /devices/platform/fe3c0000.usb/usb1/1-1/1-1.2/1-1.2:1.0/0003:1A86:E5E3.0007/input/input14
[ 1183.144619] hid-multitouch 0003:1A86:E5E3.0007: input,hidraw4: USB HID v1.00 Device [wch.cn USB2IIC_CTP_CONTROL] on usb-fe3c0000.usb-1.2/input0

从上面的输出信息可以看到PID=e5e3,VID=1a86USB总线编号为1,设备地址为4。

2.1.2 lsusb

我们可以通过lsusb命令查看USB设备信息:

root@SOM-RK3399v2:/# lsusb
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 004: ID 1a86:e5e3 QinHeng Electronics USB2IIC_CTP_CONTROL    # 这里
Bus 001 Device 003: ID 1a2c:4d7e China Resource Semico Co., Ltd USB Keyboard
Bus 001 Device 002: ID 14cd:8601 Super Top 4-Port hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 006 Device 002: ID 046d:c52b Logitech, Inc. Unifying Receiver
Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 005 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

USB总线1上,有1个root hub,1个4-Port hub、1个USB键盘、1个USB触摸屏。

2.1.3 查看设备文件
root@SOM-RK3399v2:/# cat /sys/kernel/debug/usb/devices
T:  Bus=01 Lev=02 Prnt=02 Port=01 Cnt=02 Dev#=  4 Spd=12   MxCh= 0
D:  Ver= 0.01 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
P:  Vendor=1a86 ProdID=e5e3 Rev= 0.00
S:  Manufacturer=wch.cn
S:  Product=USB2IIC_CTP_CONTROL
C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr= 64mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=03(HID  ) Sub=00 Prot=00 Driver=usbhid
E:  Ad=82(I) Atr=03(Int.) MxPS=  64 Ivl=1ms

这里看到的信息实际上和内核日志输出的信息一样的。

2.2 监测USB总线上的数据

查看USB总线上的数据;

root@SOM-RK3399v2:/# cat /sys/kernel/debug/usb/usbmon/1u | grep "1:004"
ffffffc0c88dab00 1925763364 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925763683 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925775309 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925775578 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925787174 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925787301 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925800237 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925800380 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925811243 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925811395 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925821178 C Ii:1:004:2 0:1 52 = 01000075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925821328 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925822163 C Ii:1:004:2 0:1 52 = 01000075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925822309 S Ii:1:004:2 -115:1 52 <

其中的1:004中1表示USB总线编号,004时设备编号。

执行该命令的同时我们点击一下USB触摸屏,就可以查看USB总线上的数据传输

三、USB传输基础

USB数据抓取到了,但是放眼一看,密密麻麻的全是数字,它们代表什么含义呢?

在解读usbmon抓取的数据包的含义之前,我们需要了解一下与USB传输有关的基础知识,这样才能更好的理解数据包的各个字段所代表的含义。

USB总线上传输的数据是以包为基本单位的,但是不能随意的使用包来传输数据,必须按照一定的关系把这些不同的包组织成事务(transaction)进行传输。

3.1 USB信息包的种类

USB传输由一个或多个事务组成,每个事务又进一步含有多个USB包(packets)。

USB包的种类,总体上分为四类:令牌包、数据包、握手包、特殊包;

3.1.1 令牌包

令牌包用来发起一次USB传输,因为USB是主从结构的拓扑结构,所有的数据传输都是由主机发起的,设备只能被动的响应,这就需要主机发送一个令牌包来通知哪个设备进行响应,如何响应。

令牌包有 4 种,分别为输出(OUT)、输入(IN)、建立(SETUP)和帧起始(SOF)。

3.1.2 数据包

数据包就是用来传输数据的,可以从主机到设备,也可以从设备到主机,方向由令牌包来指定。

3.1.3 握手包

握手包的发送者一般为数据接收者,用来表示一个传输是否被对方确认。在传输正常的情况下,主机/设备会发送一个表示传输正确的 ACK握手包。

3.1.4 特殊包

特殊包用在一些特殊的场合,这里就不介绍了。

3.2 USB事务

介绍了USB信息包的分类,下面就要着重介绍USB的事务及传输类型。前面已经说了,我们不能随意的使用USB包来传输数据,必须按照一定的关系把这些不同的包组织成事务才能传输数据。

那么事务是什么呢? 事务通常由三个包组成:令牌包、数据包和握手包。

        img

注意:usbmon只抓取事务中的数据包,不会抓取令牌包和握手包。

3.3 USB传输类型

USB协议规定了4种传输类型:批量传输、等时传输、中断传输和控制传输。其中;

  • 批量传输、等时传输、中断传输每传输一次数据都是一个事务;
  • 控制传输包括三个过程,建立过程和状态过程分别是一个事务,数据过程可能包含多个事务。
3.3.1 批量传输

批量传输用于传输大量数据。USB协议不保证这些数据传输可以在特定的时间内完成,但保证数据的准确性。如果总线上的带宽不足以发送整个批量包,则将数据拆分为多个包传输。批量传输数据可靠,但实时性较低。如USB硬盘、打印机等设备就采用的是批量传输方式;

img

3.3.2 等时传输

等时传输也可以传输大量数据,但数据的可靠性无法保证。采用等时传输的USB设备更加注重保持一个恒定的数据传输速度,对数据的可靠性要求不高。如USB摄像头就使用的是等时传输方式;

img

3.3.3 中断传输

USB主机请求USB设备传输数据时,中断传输以一个固定的速率传送少量的数据。中断端点的数据传输方式为中断传输,数据传输可靠,实时性高,这里的中断并不是USB设备产生中断,而是USB主机每隔一个固定的时间主动查询USB设备是否有数据要传输,以轮询的方式提高实时性。如USB鼠标采用的是中断传输;

img

3.3.4 控制传输

控制传输用于配置设备、获取设备信息、发送命令到设备、获取设备的状态。每个USB设备都有端点0的控制端点,当USB设备插入到USB主机拓扑网络中时,USB主机就通过端点0与USB设备通信,对USB设备进行配置,便于后续的数据传输。USB协议保证控制传输有足够的带宽。控制传输可靠,时间有保证,但传输的数据量不大。如USB设备的枚举过程就采用的是控制传输;

img

img

四、USB数据包分析

下面是我点击USB触摸屏通过usbmon 抓取到的数据:

root@SOM-RK3399v2:/# cat /sys/kernel/debug/usb/usbmon/1u | grep "1:004"
ffffffc0c88dab00 1925763364 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925763683 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925775309 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925775578 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925787174 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925787301 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925800237 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925800380 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925811243 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925811395 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925821178 C Ii:1:004:2 0:1 52 = 01000075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925821328 S Ii:1:004:2 -115:1 52 <
ffffffc0c88dab00 1925822163 C Ii:1:004:2 0:1 52 = 01000075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
ffffffc0c88dab00 1925822309 S Ii:1:004:2 -115:1 52 <

下面从左到右分析这些字段代表的含义:URB标签 时间戳 事件类型 地址 URB状态 数据包的长度 数据标签 数据流。

4.1 URB Tag - URB标签

该字段表示驱动中定义的struct urb结构体变量在内核空间的地址,可以使用该字段区分不同的URB数据包。

URB标签字段的长度和系统的位数相同,以64为系统为例,该字段的长度为8个字节。

4.2 Timestamp - 时间戳

该字段表示的是时间戳,单位是微秒。1微秒 = 10^(-6)秒。

该时间是由下面的mon_get_timestamp函数获取的,使用ktime_get_ts64获取的时间位与上了0xFFF,因此usbmon显示的秒数范围为0 ~ 4096s;

// drivers/usb/mon/mon_text.c
static inline unsigned int mon_get_timestamp(void)
{
        struct timespec64 now;
        unsigned int stamp;

        ktime_get_ts64(&now);  // 获取当前时间
        stamp = now.tv_sec & 0xFFF;  /* 2^32 = 4294967296. Limit to 4096s. */
        stamp = stamp * USEC_PER_SEC + now.tv_nsec / NSEC_PER_USEC;   //USEC_PER_SEC=1000000 NSEC_PER_USEC=1000
        return stamp;
}
4.3 Event Type - 事件类型

事件类型有三种:

  • S - submission,向USB Controller提交URB
  • C - callbackURB提交完成后的回调;
  • E - submission error,向usb controller提交URB发生错误;
4.4 Address word - 地址

这个字段包含4部分,各个部分之间使用分号隔开,这4部分分别是URB类型及传输方向、USB总线号、USB设备地址端点号。

Ii:1:004:2
|  |  |  |
|  |  |  |__________ 端口号
|  |  |_____________ USB设备地址  
|  |________________ USB总线号
|___________________ URB类型及传输方向

URB类型及传输方向:USB有四种传输方式,分别是控制传输、批量传输、等时传输和中断传输。USB数据的传输方向是以USB主机端为参考对象的,USB主机向USB设备发送数据那么传输方向就是OutputUSB主机读取USB设备的数据那么传输方向就是Input

Ci Co   Control input and output
    Zi Zo   Isochronous input and output
    Ii Io   Interrupt input and output
    Bi Bo   Bulk input and output

USB总线号:该字段表示USB总线号,每个USB Controller都有一条对应的USB总线,使用USB总线号区分它们,USB设备可以挂接到某条总线上;

USB设备地址:该字段表示USB设备的地址,每一个USB设备经过枚举后在USB总线都有一个唯一的地址;

端点号:表明该次数据传输是Input/Output到设备的哪个端点。上图中该字段是2,就表示这次数据传输使用的是设备的端点2。

4.5 URB Status word - URB状态

这个字段有两种表示形式: 

  • s + 一串数字:
  • 一串以分号间隔的数字(或单个数字)构成的,这串数字包含下面几个部分:URB statusintervalstart frameerror count。特别注意一点,该字段不同于 地址字段,对于不同的传输方式这几部分是可选的,并非所有部分都是必须的。

下图是不同的传输方式包含的信息;

img

下面分析不同的传输方式所包含的信息:

(1) 批量传输:只包含URB status这个字段,它对应着struct urb结构体中的status成员变量,表示URB的状态。URB status仅仅对事件类型中的Callback有意义,对于Submission是无意义的,之所以这么做是为了统一格式,方便使用脚本分析usbmon的数数据;

(2) 中断传输:URB statusIntervalURB status见前面的分析,Interval表示该URB对端点轮询的间隔时间;

(3) 等时传输:URB statusIntervalstart frameerror count。等时传输包含了所有部分,start frameerror count 是等时传输所特有的字段;

(4) 控制传输:控制传输在提交时(S:submission)这个字段是s,这里的s后面紧跟的数据是控制传输的建立过程主机发送的数据包(Setup packet),可以参考前面控制传输的示意图。控制传输在回调时(C:callback),这个字段代表的是URB status

img

该字段从左到右的格式如下,括号中的数字表示该部分占用的字节大小:

bmRequestType(1) + bRequest(1) + wvalue(2) + wIndex(2) + wLength(2)

每个字段的含义可以在USB2.0规范中找到,这部分与USB的标准请求等相关。

4.6 Data Length - 数据包的长度

对于S(Submission)Data Length字段是主机请求发送/读取的数据长度,但是设备并不一定能够接收/发送主机请求的数据长度。实际接收/发送的数据长度在C(Callback)中的Data Length字段。

ffffffc0c88dab00 1925763683 S Ii:1:004:2 -115:1 52 <
							   |__ 输入		    |____ 请求读取的长度							
ffffffc0c88dab00 1925775309 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000
                               |__ 输入           |_______ 实际读取的长度
4.7 Data tag - 数据标签

数据标签有三种:

  • =:后面紧跟数据流;

  • > :表示这是一次Output数据传输;

  • <:表示这是一次Input数据传输;

4.8 Data words follow - 数据流

这个字段就是一个事务中的数据包,我们平时分析usbmon抓取的数据包,也主要是看这个字段。注意一点,这个字段实际显示的数据 <= Data Length的值。

4.8.1 1个触摸点

1个触摸点时,我们读取到的数据:

ffffffc0c88dab00 1925775309 C Ii:1:004:2 0:1 52 = 01010075 09080930 00000000 00000000 00000000 00000000 00000000 00000000

报文格式;

01234567
0x010x010x000x750x090x080x090x30
设备ID第一个点触摸状态手指IDX坐标低8位X坐标高8位Y坐标低8位Y坐标高8位
设备ID固定值被按下手指ID为0x00X坐标为0x0975X坐标为0x0975Y坐标为0x0908Y坐标为0x0908
4.8.2 2个触摸点

2个触摸点时,我们读取到的数据:

ffffffc0c88dab00 2543284410 C Ii:1:004:2 0:1 52 = 0101003d 02d50530 000101e1 035d0330 00000000 00000000 00000000 00000000

报文格式;

01234567
0x010x010x000x3d0x020xd50x050x30
设备ID第一个点触摸状态手指IDX坐标低8位X坐标高8位Y坐标低8位Y坐标高8位
设备ID固定值被按下手指ID为0x00X坐标为0x023dX坐标为0x023dY坐标为0x05d5Y坐标为0x05d5
89101112131415
0x000x010x010xe10x030x5d0x030x30
第二个点触摸状态手指IDX坐标低8位X坐标高8位Y坐标低8位Y坐标高8位
被按下手指ID为0x01X坐标为0x03e1X坐标为0x03e1Y坐标为0x035dY坐标为0x035d

参考文章

[1] LinuxUSB抓包工具UsbMon的使用和包数据格式解析

[2] 使用usbmon抓取usb总线上的数据

[3] 详解usbmon抓取的log各字段的含义

[4] debian11 modprobe:未找到命令

[5] Ubuntu开机自动加载驱动模块

[6] Documentation/usb/usbmon.txt

[7] USB鼠标HID描述符以及数据格式

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

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

相关文章

03黑马店评-添加商户缓存和商户类型的缓存到Redis

商户查询缓存 什么是缓存 实际开发过程中数据量可以达到几千万,缓存可以作为避震器防止过高的数据访问猛冲系统,避免系统内的操作线程无法及时处理信息而瘫痪 缓存(Cache)就是数据交换的缓冲区(储存临时数据的地方),我们俗称的"缓存"实际就是缓冲区内的数据(一般从…

L14D6内核模块编译方法

一、内核模块基础代码解析 一个内核模块代码错误仍然会导致的内核崩溃。 GPL协议&#xff1a;开源规定&#xff0c;使用内核一些函数需要 1、单内核的缺点 单内核扩展性差的缺点减小内核镜像文件体积&#xff0c;一定程度上节省内存资源提高开发效率不能彻底解决稳定性低的缺…

香港鼎鑫鸿鄴:紧跟国家新能源政策,制定合理发展规划

由于全球环境的变化以及环境的需要,世界上的许多资源企业正在努力研究和开发新的、更好的绿色产品和服务,并且取得了一系列的突破和成果——其中最为瞩目的是新能源领域的发展与革新。随着我国经济的快速发展,人民生活水平的不断提高,对清洁能源的需求日益增长,同时也带动了新能…

python 定时器

需求 我想在某一时刻完成某个任务,需要一个定时计划 调研了几种方式都不是很理想. 参考,python实现定时任务的8种方式详解 选择使用 apscheduler 库吧 APScheduler简介 APScheduler是Python的一个定时任务框架&#xff0c;用于执行周期或者定时任务&#xff0c;该框架不仅可以…

【数据结构】二叉树遍历的实现(超详细解析,小白必看系列)

目录 一、前言 &#x1f34e;为何使用链式二叉树 &#x1f350;何为链式二叉树 &#x1f349;二叉树的构建 &#x1f4a6;创建二叉链结构 &#x1f4a6;手动构建一颗树 &#x1f353;二叉树的遍历 &#xff08;重点&#xff09; &#x1f4a6;前序遍历 &#x1f4a6;中…

二维码智慧门牌管理系统:提升小区管理的智能化水平

文章目录 前言一、二维码智慧门牌管理系统简介二、精准的门牌定位三、门牌编号优化三、更多特点四、未来展望 前言 随着科技的不断发展&#xff0c;智能化管理已经深入到我们生活的方方面面&#xff0c;而小区作为我们居住的重要场所&#xff0c;智能化管理更是必不可少。为了…

警惕!外贸常见的一些骗局!

随着网络技术和国际支付的普及&#xff0c;外贸汇款骗局也是时常发生&#xff0c;本文将列举外贸汇款骗局的常见套路和风险提示&#xff0c;以帮助广大外贸人更好地护好自己的“钱袋子”。 常见套路 1.钓鱼 出口商与买家的订单已经谈妥&#xff0c;把收款信息通过邮件发给买家…

【重拾C语言】七、指针(二)指针与数组(用指针标识数组、多维数组与指针、数组指针与指针数组)

目录 前言 七、指针 7.1~3 指针与变量、指针操作、指向指针的指针 7.4 指针与数组 7.4.1 用指针标识数组 7.4.2 应注意的问题 a. 数组名是指针常量 b. 指针变量的当前值 c. 数组超界 7.4.3 多维数组与指针 7.4.4 指针数组 a. 指针数组 b. 数组指针 c. 对比总结 前…

bash上下键选择选项demo脚本

效果如下&#xff1a; 废话不多说&#xff0c;上代码&#xff1a; #!/bin/bashoptions("111" "222" "333" "444") # 选项列表 options_index0 # 默认选中第一个选项 options_len${#options[]}echo "请用上下方向键进行选择&am…

【多线程案例】Java实现简单定时器(Timer)

1.定时器&#xff08;Timer&#xff09; 1.什么是定时器&#xff1f; 在日常生活中,如果我们想要在 t 时间 后去做一件重要的事情,那么为了防止忘记,我们就可以使用闹钟的计时器功能,它会在 t 时间后执行任务&#xff08;响铃&#xff09;提醒我们去执行这件事情. — 这就是J…

【数据结构-队列 二】【单调队列】滑动窗口最大值

废话不多说&#xff0c;喊一句号子鼓励自己&#xff1a;程序员永不失业&#xff0c;程序员走向架构&#xff01;本篇Blog的主题是【单调队列】&#xff0c;使用【队列】这个基本的数据结构来实现&#xff0c;这个高频题的站点是&#xff1a;CodeTop&#xff0c;筛选条件为&…

Springboot整合Druid:数据库密码加密的实现

ps:Springboot项目&#xff0c;为了防止某些人反编译看到yml里面的数据库密码&#xff0c;对密码进行加密处理&#xff0c;隐藏公钥形式。&#xff08;总有人想扒掉你的底裤看看你屁股长什么样&#xff09; 1.引入依赖&#xff08;以前有依赖就不用了&#xff09; 2.找到Druid…

你想知道的测试自动化-概览篇

测试自动化概念整理 协议 JSON Wire Protocol Specification JSON Wire 协议 现已过时的开源协议的端点和有效负载&#xff0c;它是W3C webdriver的先驱。 devtool协议 Chrome DevTools 协议允许使用工具来检测、检查、调试和分析 Chromium、Chrome 和其他基于 Blink 的浏…

轻松驾驭Hive数仓,数据分析从未如此简单!

1 前言 先通过SparkSession read API从分布式文件系统创建DataFrame 然后&#xff0c;创建临时表并使用SQL或直接使用DataFrame API&#xff0c;进行数据转换、过滤、聚合等操作 最后&#xff0c;再用SparkSession的write API把计算结果写回分布式文件系统 直接与文件系统交…

MyLife - Docker安装Redis

Docker安装Redis 个人觉得像reids之类的基础设施在线上环境直接物理机安装使用可能会好些。但是在开发测试环境用docker容器还是比较方便的。这里学习下docker安装redis使用。 1. Redis 镜像库地址 Redis 镜像库地址&#xff1a;https://hub.docker.com/_/redis/tags 这里是官方…

四向穿梭车智能机器人|HEGERLS托盘式四向穿梭车系统的换轨技术和故障恢复功能

随着物流行业的迅猛发展&#xff0c;托盘四向穿梭式立体库因其在流通仓储体系中所具有的高效密集存储功能优势、运作成本优势与系统化智能化管理优势&#xff0c;已发展为仓储物流的主流形式之一。托盘四向穿梭车立体仓库有全自动和半自动两种工作模式&#xff0c;大大提高了货…

java基础 异常

异常概述&#xff1a; try{ } catch{ }&#xff1a; package daysreplace;import com.sun.jdi.IntegerValue;import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.…

pymoo包NSGA2算法实现多目标遗传算法调参详细说明

pymoo包NSGA2算法实现多目标遗传算法调参详细说明 1.定义待求解问题1.0定义问题的参数说明1.0.0 求解问题必须设置在def _evaluate(self, x, out, *args, **kwargs)函数中1.0.1 问题必须用 out["F"] [f1, f2] 包裹起来1.0.2 约束条件也必须用 out["G"] […

Oracle 简介与 Docker Compose部署

最近&#xff0c;我翻阅了在之前公司工作时的笔记&#xff0c;偶然发现了一些有关数据库的记录。当初&#xff0c;我们的项目一开始采用的是 Oracle 数据库&#xff0c;但随着项目需求的变化&#xff0c;我们不得不转向使用 SQL Server。值得一提的是&#xff0c;公司之前采用的…

Windows保姆级安装Docker教程

1.官网下载 2.安装 3.启动Hyper-V 4.检查是否安装成功 1.下载 1.1.打开官网&#xff0c;然后点击下载 官网链接&#xff1a;https://hub.docker.com/ 2.安装 下载好之后会得到一个exe程序&#xff0c;然后启动它&#xff0c;进行安装。 去掉 WSL 不使用Hyper-V&#xff0…