万字解析Linux内核调试之动态追踪
文章介绍几种常用的内核动态追踪技术,对 ftrace、perf 及 eBPF 的使用方法进行案例说明。
1、什么是动态追踪
动态追踪技术,是通过探针机制来采集内核或者应用程序的运行信息,从而可以不用修改内核或应用程序的代码,就获得调试信息,对问题进行分析、定位。
通常在排查和调试异常问题时,我们首先想到的是使用 GDB 在程序运行路径上设置断点,然后结合命令进行分析定位;或者,在程序源码中增加一系列的日志,从日志输出中寻找线索。不过,断点往往会中断程序的正常运行;而增加新的日志,往往需要重新编译和部署。在面对偶现问题以及对时延要求严格的场景下,GDB 和增加日志的方式就不能满足需求了。
动态追踪为这些问题提供了完美的方案:它既不需要停止服务,也不需要修改程序的代码;程序还按照原来的方式正常运行时,就可以分析出问题的根源。同时,相比以往的进程级跟踪方法(比如 ptrace),动态追踪往往只会带来很小的性能损耗。
2、动态追踪技术分类
动态追踪的工具很多,systemtap、perf、ftrace、sysdig、eBPF 等。动态追踪的事件源根据事件类型不同,主要分为三类:静态探针, 动态探针以及硬件事件。
- 硬件事件:通常由性能监控计数器 PMC(Performance Monitoring Counter)产生,包括了各类硬件的性能情况,比如 CPU 的缓存、指令周期、分支预测等。
- 静态探针:事先在代码中定义好,并编译到应用程序或者内核中的探针。这些探针只有在开启探测功能时,才会被执行到;未开启时并不会执行。常见的静态探针包括内核中的跟踪点(tracepoints)和 USDT(Userland Statically Defined Tracing)探针。
- 动态探针:没有事先在代码中定义,但却可以在运行时动态添加的探针,比如函数的调用和返回等。动态探针支持按需在内核或者应用程序中添加探测点,具有更高的灵活性。常见的动态探针有两种,即用于内核态的 kprobes 和用于用户态的 uprobes。需要注意的是 kprobes 需要内核编译时开启 CONFIG_KPROBE_EVENTS,uprobes 则需要内核编译时开启 CONFIG_UPROBE_EVENTS。
3、动态追踪之ftrace
ftrace 最早用于函数跟踪,后来扩展支持了各种事件跟踪功能。ftrace 通过 debugfs 以普通文件的形式,向用户空间提供访问接口,这样不需要额外的工具,就可以通过挂载点(通常为 /sys/kernel/debug/tracing 目录)的文件读写,来跟 ftrace 交互,跟踪内核或者应用程序的运行事件。
在使用 ftrace 之前,首先要确定当前系统是否已经挂载了 debugfs,可以使用如下方式进行确认。
#方式一:查看挂载信息
mount | grep debugfs
#方式二:查看挂载点
ls /sys/kernel/debug/tracing
如果在上面的查找结果中有输出,说明当前系统已经挂载了 debugfs,如果系统未挂载 debugfs,则使用如下的命令进行挂载。
mount -t debugfs nodev /sys/kernel/debug
在 /sys/kernel/debug/tracing 目录下提供了各种跟踪器(tracer)和事件(event),一些常用的选项如下。
- available_tracers:当前系统支持的跟踪器;
- available_events:当前系统支持的事件;
- current_tracer:当前正在使用的跟踪器;默认为 nop,表示不做任何跟踪操作,使用 echo 命令把跟踪器的名字写入该文件,即可切换到不同的跟踪器;
- trace:当前的跟踪信息;
- tracing_on:用于开始或暂停跟踪;
- trace_options:设置 ftrace 的一些相关选项;
ftrace 提供了多个跟踪器,用于跟踪不同类型的信息,比如函数调用、中断关闭、进程调度等。具体支持的跟踪器取决于系统配置,使用如下的命令来查询当前系统支持的跟踪器。
root@ubuntu:/sys/kernel/debug/tracing# cat available_tracers
hwlat blk mmiotrace function_graph wakeup_dl wakeup_rt wakeup function nop
以上是系统支持的所有跟踪器,function 表示跟踪函数的执行,function_graph 则是跟踪函数的调用关系,也就是生成直观的调用关系图,这是最常用的两种跟踪器。也可以通过配置内核,使系统支持更多类型的跟踪器。
在使用跟踪器前,需要确定好跟踪目标,包括内核函数和事件,函数是指内核中的函数名,事件是指内核中预先定义的跟踪点。可以使用如下方式查看内核支持跟踪的函数和事件。
#查看内核支持追踪的函数
cat available_filter_functions
#查看内核支持追踪的事件
cat available_events
接下来以一个简单的示例说明 ftrace 的基本用法,比如我们需要跟踪 open 的系统调用,open 在内核中的实现接口为 do_sys_open,所以需要将跟踪的函数设置为 do_sys_open,以下是跟踪过程的步骤。
#设置函数跟踪点为 do_sys_open
echo do_sys_open > set_graph_function
#设置当前跟踪器为 function_graph
echo function_graph > current_tracer
#配置 trace 属性:显示当前的进程
echo funcgraph-proc > trace_options
#清除 trace 缓存
echo > trace
#开启跟踪
echo 1 > tracing_on
#产生 do_sys_open 调用
ls
#关闭跟踪
echo 0 > tracing_on
在关闭跟踪后,使用 cat 命令查看跟踪结果,如下所示,在 trace 文件中保存了跟踪到的信息,第一列表示接口执行的 CPU,第二列表示任务名称和进程 PID,第三列是函数执行延迟,最后一列是函数调用关系图。
root@ubuntu:/sys/kernel/debug/tracing# cat trace
# tracer: function_graph
#
# CPU TASK/PID DURATION FUNCTION CALLS
# | | | | | | | | |2) ls-46073 | | do_sys_open() {2) ls-46073 | | getname() {2) ls-46073 | | getname_flags() {2) ls-46073 | | kmem_cache_alloc() {2) ls-46073 | | _cond_resched() {2) ls-46073 | 0.119 us | rcu_all_qs();2) ls-46073 | 0.416 us | }2) ls-46073 | 0.095 us | should_failslab();2) ls-46073 | 0.112 us | memcg_kmem_put_cache();2) ls-46073 | 1.041 us | }2) ls-46073 | | __check_object_size() {2) ls-46073 | 0.097 us | check_stack_object();2) ls-46073 | 0.100 us | __virt_addr_valid();2) ls-46073 | 0.097 us | __check_heap_object();2) ls-46073 | 0.662 us | }2) ls-46073 | 2.109 us | }2) ls-46073 | 2.414 us | }
ftrace 的输出通过不同级别的缩进,直观展示了各函数间的调用关系。但是 ftrace 的使用需要好几个步骤,用起来并不方便,不过,trace-cmd 已经把这些步骤给包装了起来,这样,就可以通过一行命令,完成上述所有过程。trace-cmd 的安装方式如下。
# Ubuntu
apt install trace-cmd
# CentOS
yum install trace-cmd
trace-cmd安装好之后,可以通过执行如下的命令输出类似的结果,值得注意的是 trace-cmd 的执行不能在 /sys/kernel/debug/tracing 路径,否则执行出出错,提示信息为“trace-cmd: Permission denied”。
trace-cmd record -p function_graph -g do_sys_open -O funcgraph-proc ls
trace-cmd report
ftrace 的追踪功能不止于此,它不仅能追踪到接口的调用关系,还能抓取接口调用的时间戳,用于性能分析;还可根据需要追踪的接口进行模糊过滤,众多的功能不在这里详细介绍,如果项目中需要用到再进行具体了解和总结。
资料直通车:Linux内核源码技术学习路线+视频教程内核源码
学习直通车:Linux内核源码内存调优文件系统进程管理设备驱动/网络协议栈
4、动态追踪之perf
perf 的功能强大,可以统计分析出应用程序或者内核中的热点函数,从而用于程序性能分析;也可以用来分析 CPU cache、CPU 迁移、分支预测、指令周期等各种硬件事件;还可以对感兴趣的事件进行动态追踪。下面以 do_sys_open 为例,设置目标函数追踪。执行如下命令,可以查询所有支持的事件。
perf list
在 perf 的各个子命令中添加 --event 选项,设置追踪感兴趣的事件。如果这些预定义的事件不满足实际需要,可以使用 perf probe 来动态添加。而且,除了追踪内核事件外,perf 还可以用来跟踪用户空间的函数。执行如下代码添加 do_sys_open 探针。
#执行指令
perf probe --add do_sys_open
#输出
Added new event:probe:do_sys_open (on do_sys_open)
You can now use it in all perf tools, such as:perf record -e probe:do_sys_open -aR sleep 1
探针添加成功后,就可以在所有的 perf 子命令中使用。比如,上述输出就是一个 perf record 的示例,执行它就可以对 1s 内的 do_sys_open 进行采样,如下所示。
#执行命令
perf record -e probe:do_sys_open -aR sleep 1
#输出
[ perf record: Woken up 1 times to write data ][ perf record: Captured and wrote 0.810 MB perf.data (18 samples) ]
执行如下命令显示采样结果,输出结果中列出了调用 do_sys_open 的任务名称、进程 PID 以及运行的 CPU 等信息。
#执行命令
perf script
#输出
perf 3676 [003] 7619.618568: probe:do_sys_open: (ffffffffa92e78e0)
sleep 3677 [000] 7619.621118: probe:do_sys_open: (ffffffffa92e78e0)
sleep 3677 [000] 7619.621130: probe:do_sys_open: (ffffffffa92e78e0)
vminfo 1403 [001] 7619.864117: probe:do_sys_open: (ffffffffa92e78e0)
dbus-daemon 749 [001] 7619.864222: probe:do_sys_open: (ffffffffa92e78e0)
dbus-daemon 749 [001] 7619.864310: probe:do_sys_open: (ffffffffa92e78e0)
irqbalance 743 [000] 7620.013548: probe:do_sys_open: (ffffffffa92e78e0)
irqbalance 743 [000] 7620.013687: probe:do_sys_open: (ffffffffa92e78e0)
在使用结束后,使用如下命令删除探针。
#删除 do_sys_open 探针
perf probe --del probe:do_sys_open
5、动态追踪之eBPF
eBPF 相对于 ftrace 和 perf 更加灵活,它可以通过 C 语言自由扩展,这些扩展通过 LLVM (Low Level Virtual Machine) 转换为 BPF 字节码后,加载到内核中执行。
5.1 搭建 eBPF 开发环境
虽然 Linux 内核很早就已经支持了 eBPF,但很多新特性都是在 4.x 版本中逐步增加的。所以,想要稳定运行 eBPF 程序,内核至少需要 4.9 或者更新的版本。而在开发和学习 eBPF 时,为了体验和掌握最新的 eBPF 特性,推荐使用更新的 5.x 内核,接下来的案例是基于 Ubuntu20.04 系统,内核版本为 5.15.0-56-generic,eBPF 开发和运行需要相关的开发工具如下。
- 将 eBPF 程序编译成字节码的 LLVM;
- C 语言程序编译工具 make;
- 流行的 eBPF 工具集 BCC 和它依赖的内核头文件;
- 内核代码仓库实时同步的 libbpf;
- 内核提供的 eBPF 程序管理工具 bpftool。
可运行如下命令进行相关工具的安装。
apt install -y make clang llvm libelf-dev libbpf-dev bpfcc-tools libbpfcc-dev linux-tools-$(uname -r) linux-headers-$(uname -r)
5.2 开发 eBPF 程序的步骤
一般来说, eBPF 程序的开发分为如下 5 个步骤。
- 第一步,使用 C 语言开发一个 eBPF 程序;
- 第二步,借助 LLVM 把 eBPF 程序编译成 BPF 字节码;
- 第三步,通过 bpf 系统调用,把 BPF 字节码提交给内核;
- 第四步,内核验证并运行 BPF 字节码,并把相应的状态保存到 BPF 映射中;
- 第五步,用户程序通过 BPF 映射查询 BPF 字节码的运行状态。
eBPF 程序执行过程
以上的每一步,都可以自己动手去完成。但为了方便,推荐从 BCC(BPF Compiler Collection)开始学起。BCC 是一个 BPF 编译器集合,包含了用于构建 BPF 程序的编程框架和库,并提供了大量可以直接使用的工具。使用 BCC 的好处是,它把上述的 eBPF 执行过程通过内置框架抽象了起来,并提供了 Python、C++ 等编程语言接口。这样,就可以直接通过 Python 语言去跟 eBPF 的各种事件和数据进行交互。
5.3 借助 BCC 开发 eBPF 程序案例
接下来,就以跟踪 openat()(即打开文件)这个系统调用为例,说明如何开发并运行第一个 eBPF 程序。使用 BCC 开发 eBPF 程序,可以把上面的五步简化为下面的三步。
5.3.1 使用 C 开发一个 eBPF 程序
新建一个 trace_open.c 文件,输入如下内容。
/* 包含头文件 */
#include <uapi/linux/openat2.h>
#include <linux/sched.h>/* 定义数据结构 */
struct data_t {u32 pid;u64 ts;char comm[TASK_COMM_LEN];char fname[NAME_MAX];
};/* 定义性能事件映射 */
BPF_PERF_OUTPUT(events);/* 定义 kprobe 处理函数 */
int trace_open(struct pt_regs *ctx, int dfd, const char __user *filename, struct open_how *how)
{struct data_t data = {};/* 获取 PID 和时间 */data.pid = bpf_get_current_pid_tgid();data.ts = bpf_ktime_get_ns();/* 获取进程名 */if (bpf_get_current_comm(&data.comm, sizeof(data.comm)) == 0){bpf_probe_read(&data.fname, sizeof(data.fname), (void *)filename);}/* 提交性能事件 */events.perf_submit(ctx, &data, sizeof(data));return 0;
}
BPF 程序可以利用 BPF 映射(map)进行数据存储,而用户程序也需要通过 BPF 映射,同运行在内核中的 BPF 程序进行交互。用户层为了获取内核打开文件名称时,就要引入 BPF 映射。为了简化 BPF 映射的交互,BCC 定义了一系列的库函数和辅助宏定义。
如下是对上述代码的说明。
- data_t 是用户自定义的数据结构,用于保存进程 PID、时间、执行命令及文件名称;
- 使用 BPF_PERF_OUTPUT 来定义一个 Perf 事件类型的 BPF 映射;
- bpf_get_current_pid_tgid 用于获取进程的 TGID 和 PID。定义的 data.pid 数据类型为 u32,所以高 32 位舍弃掉后就是进程的 PID;
- bpf_ktime_get_ns 用于获取系统自启动以来的时间,单位是纳秒;
- bpf_get_current_comm 用于获取进程名,并把进程名复制到预定义的缓冲区中;
- bpf_probe_read 用于从指定指针处读取固定大小的数据,这里则用于读取进程打开的文件名。
- perf_submit() 接口将填充好的 data 数据提交到定义的 BPF 映射中;
5.3.2 使用 Python 开发用户态程序
创建一个 trace_open.py 文件,并输入下面的内容。
#!/usr/bin/env python3# 1) import bcc library
from bcc import BPF# 2) load BPF program
b = BPF(src_file="trace_open.c")# 3) attach kprobe
b.attach_kprobe(event="do_sys_openat2", fn_name="trace_open")# 4) print header
print("%-18s %-16s %-6s %-16s" % ("TIME(s)", "COMM", "PID", "FILE"))# 5) define the callback for perf event
start = 0
def print_event(cpu, data, size):global startevent = b["events"].event(data)if start == 0:start = event.tstime_s = (float(event.ts - start)) / 1000000000print("%-18.9f %-16s %-6d %-16s" % (time_s, event.comm, event.pid, event.fname))# 6) loop with callback to print_event
b["events"].open_perf_buffer(print_event)
while 1:try:b.perf_buffer_poll()except KeyboardInterrupt:exit()
如下是对上述代码的说明。
- 第 1) 处导入了 BCC 库的 BPF 模块,以便接下来调用;
- 第 2) 处调用 BPF() 加载第一步开发的 BPF 源代码;
- 第 3) 处将 BPF 程序挂载到内核探针(简称 kprobe),其中 do_sys_openat2() 是系统调用 openat() 在内核中的实现;
- 第 4) 处输出显示标题;
- 第 5) 处的 print_event 定义一个数据处理的回调函数,打印进程的名字、PID 以及它调用 openat 时打开的文件;
- 第 6) 处的 open_perf_buffer 定义了名为 "events" 的 Perf 事件映射,而后通过一个循环调用 perf_buffer_poll 读取映射的内容,并执行回调函数输出进程信息。
5.3.3 执行 eBPF 程序
用户态程序开发完成之后,最后一步就是执行它了。需要注意的是,eBPF 程序需要以 root 用户来运行,非 root 用户需要加上 sudo 来执行,输入如下命令执行程序。
python3 trace_open.py
命令执行后,可以在另一个终端中输入 cat 或 ls 命令(执行文件打开命令),然后回到运行 trace_open.py 脚本的终端,结果如下。
TIME(s) COMM PID FILE
0.000000000 b'ls' 5171 b'/etc/ld.so.cache'
0.000021937 b'ls' 5171 b'/lib/x86_64-linux-gnu/libselinux.so.1'
......
2.088971702 b'gsd-housekeepin' 1803 b'/etc/fstab'
2.089056747 b'gsd-housekeepin' 1803 b'/proc/self/mountinfo'
......
2.741662512 b'cat' 5172 b'/etc/ld.so.cache'
2.741681539 b'cat' 5172 b'/lib/x86_64-linux-gnu/libc.so.6'
......
5.4 eBPF 开发方式简介
除了 BCC 之外,eBPF 还有可以使用其他的方式进行辅助开发,比如 bpftrace 和 libbpf,每种方法都有自己的优点和适用范围,以下是这三种方式的对比。
- bpftrace 通常用在快速排查和定位系统上,它支持用单行脚本的方式来快速开发并执行一个 eBPF 程序。不过,bpftrace 的功能有限,不支持特别复杂的 eBPF 程序,也依赖于 BCC 和 LLVM 动态编译执行。
- BCC 通常用在开发复杂的 eBPF 程序中,其内置的各种小工具也是目前应用最为广泛的 eBPF 小程序。不过,BCC 也不是完美的,它依赖于 LLVM 和内核头文件才可以动态编译和加载 eBPF 程序。
- libbpf 是从内核中抽离出来的标准库,用它开发的 eBPF 程序可以直接分发执行,这样就不需要每台机器都安装 LLVM 和内核头文件了。不过,它要求内核开启 BTF 特性,需要非常新的发行版才会默认开启(如 RHEL 8.2+ 和 Ubuntu 20.10+ 等)。
在实际应用中,可以根据内核版本、内核配置、eBPF 程序复杂度,以及是否允许安装内核头文件和 LLVM 编译工具等,来选择最合适的方案。
相关文章:

万字解析Linux内核调试之动态追踪
文章介绍几种常用的内核动态追踪技术,对 ftrace、perf 及 eBPF 的使用方法进行案例说明。 1、什么是动态追踪 动态追踪技术,是通过探针机制来采集内核或者应用程序的运行信息,从而可以不用修改内核或应用程序的代码,就获得调试信…...

Spring Boot 各层作用与联系
目录 1 Entity 层 2 DAO 层 3 Service 层 4 Controller 层 Spring Boot 各层之间的联系: controller 层-----> service 层(接口->接口实现类) -----> dao 层的.mapper 文件 -----> 和 mapper 层里的.xml 文件对应 1 Entity 层 实体层,…...

苦中作乐---竞赛刷题(15分-20分题库)
(一)概述 (Ⅰ)彩票是幸运的 (Ⅱ)AI 英文问答程序 ( Ⅲ ) 胎压检测 (二)题目 Ⅰ 彩票的号码有 6 位数字,若一张彩票的前 3 位上的数之和等于后 3 …...

超详细,多图,PVE安装以及简单设置教程(个人记录)
前言: - 写这个的目的是因为本人健忘所以做个记录以便日后再折腾时查阅,。 - 本人笔拙如有选词,错字,语法,标点错误请忽视,大概率知道了也不会修改,本人能看懂就好。 - 内容仅适用于本人的使用环境,不同…...

茴子的写法:关于JAVA中的函数传递语法糖:lambda
解决的问题:Java中实现函数传递。 在Java编程的实践过程中,有一些场景,我们希望能够将函数传递进去,不同的函数实现代表着不同的策略,这在JDK8以前,需要定义一个接口,这个接口中定义这个函数方…...

动态规划刷题记录(2)
今天的三个题目属于模板题,可能将来会遇见它们的变形应用。 1、最长上升子序列问题 这道题目的关键就在于我们的状态定义,我们定义:f(i)表示长度为i的子序列的末尾最大值。意思就是,比如一个子序列为:1,4,5࿰…...

2023年广东省网络安全竞赛——Web 渗透测试解析(超级详细)
任务一:Web 渗透测试 任务环境说明: √ 服务器场景:Server03 √ 服务器场景操作系统:未知(关闭连接) 通过本地 PC 中的渗透测试平台 Kali 对靶机进行 WEB 渗透,找到页面内的文件上传漏洞并且尝试进行上传攻击,将文件上传成功后的页面回显字符串作为Flag 提交(如:…...

MI-SegNet阅读笔记
MI-SegNet: Mutual Information-Based US Segmentation for Unseen Domain Generalization 摘要 解决医学成像泛化能力提出了一种新的基于互信息(MI)的框架MI- segnet分离解剖结构和领域特征采用两个编码器提取相关特征:两个特征映射中出现的任何MI都将受到惩罚&a…...

十、MyBatis分页插件
1.分页插件实现的步骤 ①在pom.xml添加依赖 <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.2.0</version> </dependency>②配置分页插件 mybatis-config.xml 在MyB…...

EasyCVR平台国标GB28181协议设备接入时,可支持过滤通道类型
EasyCVR基于云边端智能协同架构,能支持海量视频的轻量化接入与集中汇聚管理,平台可支持多协议接入,包括市场主流标准协议与厂家私有协议及SDK,如:国标GB28181、RTMP、RTSP/Onvif、海康Ehome、海康SDK、宇视SDK等&#…...

玩转git的第1章节:git的理论以及操作规则
一 git原理 1.1 git的操作原理 上图是Git与提交有关的三个命令对应的操作: Add命令是把文件从IDE的工作目录添加到本地仓库的stage区, Commit命令把stage区的暂存文件提交到当前分支的仓库,并清空stage区。 Push命令把本地仓库的提交同步…...

【新2023Q2模拟题JAVA】华为OD机试 - 二叉树层次遍历
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:二叉树层次遍历 题目 有一棵…...

轻松拿结果-第三部分 同欲 -第六章 有凝聚力才有战斗力
第三部分 同欲 “上下同欲者胜”,同结果、共承担,不仅是打造销售铁军军魂的必要条件,也能让成员们对每个结果负责,更好更快实现目标。 第六章 有凝聚力才有战斗力 管理者有担当才能上下齐心 苦劳是自己的,功劳是团队的 做管理者,就要做好承受苦劳奉献功劳的心理准备 学…...

chatGPT 会给程序员带来失业潮吗?
AIChatGPT根本不是取代普通人工作,让很多人失业那么简单。他现在直接革的是世界级巨头的命,你从他们的反应就能看出来这个人工智能将掀起一场怎样规模的战争,什么腾讯百度纷纷研发自己的版本,谷歌是直接拉响红色警报,那…...

Vue项目proxyTable跨域配置
Vue项目proxyTable跨域配置文章说明proxyTable跨域配置config / dev.env.jsconfig / prod.env.jsconfig / index.jsutils / request.js接口api.js路径转换解析文章说明 学习连接 - 重要❤ - 一文详解vue-cli2.0与vue-cli3.0之间的区别 1. vue cli 2.0项目2. 本地运行时&#x…...

ubuntu16.04搭建gitlab
ubuntu16.04搭建gitlab 目录ubuntu16.04搭建gitlab一、在虚拟机ubuntu16.04安装gitlab二、配置gitlab三、使用gitlab四、踩坑记录工作中遇到需要在远端服务器搭建gitlab,耗时4天,踩坑无数,特此开个虚拟机再次搭建一次gitlab并记录供以后参考&…...

SSMP综合案例
案例实现方案分析 实体类开发————使用Lombok快速制作实体类 Dao开发————整合MyBatisPlus,制作数据层测试类 Service开发————基于MyBatisPlus进行增量开发,制作业务层测试类 Controller开发————基于Restful开发,使用PostM…...

让你的作品更出色——词云Word Cloud的制作方法(基于python,WordCloud,stylecloud)
让你的作品更出色—— 词云Word Cloud的制作方法(基于python) 本文目录: 一、词云的简介 二、 实现原理和流程 1、制作词云流程图 2、词云实现原理 三、 实现词云的方式 1、安装词云相关模块库 2、WordCloud库 3、stylecloud库 四、总结 一、词…...

axios请求拦截器
在vue项目中,通常使用axios与后台进行数据交互,axios是一款基于promise封装的库, axios特性: 1、axios 是一个基于promise的HTTP库,支持promise所有的API 2、浏览器端/node端(服务器端)都可以…...

四个常见的Linux技术面问题
刚毕业要找工作了,只要是你找工作就会有面试这个环节,那么在面试环节中,有哪些注意事项值得我的关注呢?特别是专业技术岗位,这样的岗位询问一般都是在职的工程师,如何在面试环节更好地理解面试官的问题&…...

有什么适合程序员查资料的网站
当今信息爆炸的时代,程序员每天需要花费大量的时间查找相关技术文档、知识和工具。但是,因为互联网上的内容如此之多,选择合适的网站可以成为一项艰巨的任务。在本文中,我们将介绍几个适合程序员查资料的网站,并详细阐…...

(七)手把手带你搭建精美简洁的个人时间管理网站—实现登录与注册的前端代码【源码】
🌟所属专栏:献给榕榕 🐔作者简介:rchjr——五带信管菜只因一枚 😮前言:该专栏系为女友准备的,里面会不定时发一些讨好她的技术作品,感兴趣的小伙伴可以关注一下~👉文章简…...

Day933.如何将设计最终落地到代码 -系统重构实战
如何将设计最终落地到代码 Hi,我是阿昌,今天学习记录的是关于如何将设计最终落地到代码的内容。 这要将各个组件拆分到独立的模块工程中,最终将架构设计落地到代码中。 组件化架构重构 5 个关键的步骤分别是: 设计守护解耦移动…...

209. 长度最小的子数组
209. 长度最小的子数组 力扣题目链接(opens new window) 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。 示例: 输入…...

【数据结构与算法】查找(Search)【详解】
文章目录查找查找概论一、查找的基本概念顺序表查找一、定义二、算法有序表查找一、折半查找二、插值查找三、斐波那契查找线性索引查找一、稠密索引二、分块索引三、倒排索引二叉树排序与平衡二叉树一、二叉排序树1、定义2、二叉排序树的常见操作3、性能分析二、平衡二叉树1、…...

一文学会 Spring MVC 表单标签
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...

如何在 Windows10 下运行 Tensorflow 的目标检测?
前言 看过很多博主通过 Object Detection 实现了一些皮卡丘捕捉,二维码检测等诸多特定项的目标检测。而我跟着他们的案例来运行的时候,不是 Tensorflow 版本冲突,就是缺少什么包,还有是运行官方 object_detection_tutorial 不展示…...

【jvm系列-04】精通运行时数据区共享区域---堆
JVM系列整体栏目 内容链接地址【一】初识虚拟机与java虚拟机https://blog.csdn.net/zhenghuishengq/article/details/129544460【二】jvm的类加载子系统以及jclasslib的基本使用https://blog.csdn.net/zhenghuishengq/article/details/129610963【三】运行时私有区域之虚拟机栈…...

ctfshow愚人杯 re easy_pyc wp
一、反编译题目pyc文件 题目下载解压后是一个.pyc文件,那就去反编译看看呗,因为之前用过uncompyle6,直接去命令行执行 uncompyle6 -o ez_re.py ez_re.pyc 得到ez_re.py源码一份~ 但是这里我用uncompyle6反编译的结果不知道为啥就出来很多奇…...

Ubuntu18.04 系统中本地代码上传至Gitlab库
主要步骤如下: 设置SSH Key 上传项目 1.创建SSH Key 每次上传可重新设置一个SSH Key或者使用已有SSH Key (1)创建SSH Key 创建一个新的SSH Key,终端输入以下指令,其中 “xxxxxx163.com” 是邮箱账号: s…...