重庆网站建设设计/有什么好用的搜索引擎
eBPF (扩展的伯克利数据包过滤器) 是一项强大的网络和性能分析工具,被广泛应用在 Linux 内核上。eBPF 使得开发者能够动态地加载、更新和运行用户定义的代码,而无需重启内核或更改内核源代码。这个特性使得 eBPF 能够提供极高的灵活性和性能,使其在网络和系统性能分析方面具有广泛的应用。此外,eBPF 还支持使用 USDT (用户级静态定义跟踪点) 捕获用户态的应用程序行为。
在我们的 eBPF 入门实践教程系列的这一篇,我们将介绍如何使用 eBPF 和 USDT 来捕获和分析 Java 的垃圾回收 (GC) 事件的耗时。
USDT 介绍
USDT 是一种在应用程序中插入静态跟踪点的机制,它允许开发者在程序的关键位置插入可用于调试和性能分析的探针。这些探针可以在运行时被 DTrace、SystemTap 或 eBPF 等工具动态激活,从而在不重启应用程序或更改程序代码的情况下,获取程序的内部状态和性能指标。USDT 在很多开源软件,如 MySQL、PostgreSQL、Ruby、Python 和 Node.js 等都有广泛的应用。
用户层面的追踪机制:用户级动态跟踪和 USDT
在用户层面进行动态跟踪,即用户级动态跟踪(User-Level Dynamic Tracing)允许我们对任何用户级别的代码进行插桩。比如,我们可以通过在 MySQL 服务器的 dispatch_command()
函数上进行插桩,来跟踪服务器的查询请求:
# ./uprobe 'p:cmd /opt/bin/mysqld:_Z16dispatch_command19enum_server_commandP3THDPcj +0(%dx):string'
Tracing uprobe cmd (p:cmd /opt/bin/mysqld:0x2dbd40 +0(%dx):string). Ctrl-C to end.mysqld-2855 [001] d... 19957757.590926: cmd: (0x6dbd40) arg1="show tables"mysqld-2855 [001] d... 19957759.703497: cmd: (0x6dbd40) arg1="SELECT * FROM numbers"
[...]
这里我们使用了 uprobe
工具,它利用了 Linux 的内置功能:ftrace(跟踪器)和 uprobes(用户级动态跟踪,需要较新的 Linux 版本,例如 4.0 左右)。其他的跟踪器,如 perf_events 和 SystemTap,也可以实现此功能。
许多其他的 MySQL 函数也可以被跟踪以获取更多的信息。我们可以列出和计算这些函数的数量:
# ./uprobe -l /opt/bin/mysqld | more
account_hash_get_key
add_collation
add_compiled_collation
add_plugin_noargs
adjust_time_range
[...]
# ./uprobe -l /opt/bin/mysqld | wc -l
21809
这有 21,000 个函数。我们也可以跟踪库函数,甚至是单个的指令偏移。
用户级动态跟踪的能力是非常强大的,它可以解决无数的问题。然而,使用它也有一些困难:需要确定需要跟踪的代码,处理函数参数,以及应对代码的更改。
用户级静态定义跟踪(User-level Statically Defined Tracing, USDT)则可以在某种程度上解决这些问题。USDT 探针(或者称为用户级 “marker”)是开发者在代码的关键位置插入的跟踪宏,提供稳定且已经过文档说明的 API。这使得跟踪工作变得更加简单。
使用 USDT,我们可以简单地跟踪一个名为 mysql:query__start
的探针,而不是去跟踪那个名为 _Z16dispatch_command19enum_server_commandP3THDPcj
的 C++ 符号,也就是 dispatch_command()
函数。当然,我们仍然可以在需要的时候去跟踪 dispatch_command()
以及
其他 21,000 个 mysqld 函数,但只有当 USDT 探针无法解决问题的时候我们才需要这么做。
在 Linux 中的 USDT,无论是哪种形式的静态跟踪点,其实都已经存在了几十年。它最近由于 Sun 的 DTrace 工具的流行而再次受到关注,这使得许多常见的应用程序,包括 MySQL、PostgreSQL、Node.js、Java 等都加入了 USDT。SystemTap 则开发了一种可以消费这些 DTrace 探针的方式。
你可能正在运行一个已经包含了 USDT 探针的 Linux 应用程序,或者可能需要重新编译(通常是 --enable-dtrace)。你可以使用 readelf
来进行检查,例如对于 Node.js:
# readelf -n node
[...]
Notes at offset 0x00c43058 with length 0x00000494:Owner Data size Descriptionstapsdt 0x0000003c NT_STAPSDT (SystemTap probe descriptors)Provider: nodeName: gc__startLocation: 0x0000000000bf44b4, Base: 0x0000000000f22464, Semaphore: 0x0000000001243028Arguments: 4@%esi 4@%edx 8@%rdi
[...]stapsdt 0x00000082 NT_STAPSDT (SystemTap probe descriptors)Provider: nodeName: http__client__requestLocation: 0x0000000000bf48ff, Base: 0x0000000000f22464, Semaphore: 0x0000000001243024Arguments: 8@%rax 8@%rdx 8@-136(%rbp) -4@-140(%rbp) 8@-72(%rbp) 8@-80(%rbp) -4@-144(%rbp)
[...]
这就是使用 --enable-dtrace 重新编译的 node,以及安装了提供 “dtrace” 功能来构建 USDT 支持的 systemtap-sdt-dev 包。这里显示了两个探针:node:gc__start(开始进行垃圾回收)和 node:http__client__request。
在这一点上,你可以使用 SystemTap 或者 LTTng 来跟踪这些探针。然而,内置的 Linux 跟踪器,比如 ftrace 和 perf_events,目前还无法做到这一点(尽管 perf_events 的支持正在开发中)。
Java GC 介绍
Java 作为一种高级编程语言,其自动垃圾回收(GC)是其核心特性之一。Java GC 的目标是自动地回收那些不再被程序使用的内存空间,从而减轻程序员在内存管理方面的负担。然而,GC 过程可能会引发应用程序的停顿,对程序的性能和响应时间产生影响。因此,对 Java GC 事件进行监控和分析,对于理解和优化 Java 应用的性能是非常重要的。
在接下来的教程中,我们将演示如何使用 eBPF 和 USDT 来监控和分析 Java GC 事件的耗时,希望这些内容对你在使用 eBPF 进行应用性能分析方面的工作有所帮助。
eBPF 实现机制
Java GC 的 eBPF 程序分为内核态和用户态两部分,我们会分别介绍这两部分的实现机制。
内核态程序
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
/* Copyright (c) 2022 Chen Tao */
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include <bpf/usdt.bpf.h>
#include "javagc.h"struct {__uint(type, BPF_MAP_TYPE_HASH);__uint(max_entries, 100);__type(key, uint32_t);__type(value, struct data_t);
} data_map SEC(".maps");struct {__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);__type(key, int);__type(value, int);
} perf_map SEC(".maps");__u32 time;static int gc_start(struct pt_regs *ctx)
{struct data_t data = {};data.cpu = bpf_get_smp_processor_id();data.pid = bpf_get_current_pid_tgid() >> 32;data.ts = bpf_ktime_get_ns();bpf_map_update_elem(&data_map, &data.pid, &data, 0);return 0;
}static int gc_end(struct pt_regs *ctx)
{struct data_t data = {};struct data_t *p;__u32 val;data.cpu = bpf_get_smp_processor_id();data.pid = bpf_get_current_pid_tgid() >> 32;data.ts = bpf_ktime_get_ns();p = bpf_map_lookup_elem(&data_map, &data.pid);if (!p)return 0;val = data.ts - p->ts;if (val > time) {data.ts = val;bpf_perf_event_output(ctx, &perf_map, BPF_F_CURRENT_CPU, &data, sizeof(data));}bpf_map_delete_elem(&data_map, &data.pid);return 0;
}SEC("usdt")
int handle_gc_start(struct pt_regs *ctx)
{return gc_start(ctx);
}SEC("usdt")
int handle_gc_end(struct pt_regs *ctx)
{return gc_end(ctx);
}SEC("usdt")
int handle_mem_pool_gc_start(struct pt_regs *ctx)
{return gc_start(ctx);
}SEC("usdt")
int handle_mem_pool_gc_end(struct pt_regs *ctx)
{return gc_end(ctx);
}char LICENSE[] SEC("license") = "Dual BSD/GPL";
首先,我们定义了两个映射(map):
data_map
:这个 hashmap 存储每个进程 ID 的垃圾收集开始时间。data_t
结构体包含进程 ID、CPU ID 和时间戳。perf_map
:这是一个 perf event array,用于将数据发送回用户态程序。
然后,我们有四个处理函数:gc_start
、gc_end
和两个 USDT 处理函数 handle_mem_pool_gc_start
和 handle_mem_pool_gc_end
。这些函数都用 BPF 的 SEC("usdt")
宏注解,以便在 Java 进程中捕获到与垃圾收集相关的 USDT 事件。
gc_start
函数在垃圾收集开始时被调用。它首先获取当前的 CPU ID、进程 ID 和时间戳,然后将这些数据存入 data_map
。
gc_end
函数在垃圾收集结束时被调用。它执行与 gc_start
类似的操作,但是它还从 data_map
中检索开始时间,并计算垃圾收集的持续时间。如果持续时间超过了设定的阈值(变量 time
),那么它将数据发送回用户态程序。
handle_gc_start
和 handle_gc_end
是针对垃圾收集开始和结束事件的处理函数,它们分别调用了 gc_start
和 gc_end
。
handle_mem_pool_gc_start
和 handle_mem_pool_gc_end
是针对内存池的垃圾收集开始和结束事件的处理函数,它们也分别调用了 gc_start
和 gc_end
。
最后,我们有一个 LICENSE
数组,声明了该 BPF 程序的许可证,这是加载 BPF 程序所必需的。
用户态程序
用户态程序的主要目标是加载和运行eBPF程序,以及处理来自内核态程序的数据。它是通过 libbpf 库来完成这些操作的。这里我们省略了一些通用的加载和运行 eBPF 程序的代码,只展示了与 USDT 相关的部分。
第一个函数 get_jvmso_path
被用来获取运行的Java虚拟机(JVM)的 libjvm.so
库的路径。首先,它打开了 /proc/<pid>/maps
文件,该文件包含了进程地址空间的内存映射信息。然后,它在文件中搜索包含 libjvm.so
的行,然后复制该行的路径到提供的参数中。
static int get_jvmso_path(char *path)
{char mode[16], line[128], buf[64];size_t seg_start, seg_end, seg_off;FILE *f;int i = 0;sprintf(buf, "/proc/%d/maps", env.pid);f = fopen(buf, "r");if (!f)return -1;while (fscanf(f, "%zx-%zx %s %zx %*s %*d%[^\n]\n",&seg_start, &seg_end, mode, &seg_off, line) == 5) {i = 0;while (isblank(line[i]))i++;if (strstr(line + i, "libjvm.so")) {break;}}strcpy(path, line + i);fclose(f);return 0;
}
接下来,我们看到的是将 eBPF 程序(函数 handle_gc_start
和 handle_gc_end
)附加到Java进程的相关USDT探针上。每个程序都通过调用 bpf_program__attach_usdt
函数来实现这一点,该函数的参数包括BPF程序、进程ID、二进制路径以及探针的提供者和名称。如果探针挂载成功,bpf_program__attach_usdt
将返回一个链接对象,该对象将存储在skeleton的链接成员中。如果挂载失败,程序将打印错误消息并进行清理。
skel->links.handle_mem_pool_gc_start = bpf_program__attach_usdt(skel->progs.handle_gc_start, env.pid,binary_path, "hotspot", "mem__pool__gc__begin", NULL);if (!skel->links.handle_mem_pool_gc_start) {err = errno;fprintf(stderr, "attach usdt mem__pool__gc__begin failed: %s\n", strerror(err));goto cleanup;}skel->links.handle_mem_pool_gc_end = bpf_program__attach_usdt(skel->progs.handle_gc_end, env.pid,binary_path, "hotspot", "mem__pool__gc__end", NULL);if (!skel->links.handle_mem_pool_gc_end) {err = errno;fprintf(stderr, "attach usdt mem__pool__gc__end failed: %s\n", strerror(err));goto cleanup;}skel->links.handle_gc_start = bpf_program__attach_usdt(skel->progs.handle_gc_start, env.pid,binary_path, "hotspot", "gc__begin", NULL);if (!skel->links.handle_gc_start) {err = errno;fprintf(stderr, "attach usdt gc__begin failed: %s\n", strerror(err));goto cleanup;}skel->links.handle_gc_end = bpf_program__attach_usdt(skel->progs.handle_gc_end, env.pid,binary_path, "hotspot", "gc__end", NULL);if (!skel->links.handle_gc_end) {err = errno;fprintf(stderr, "attach usdt gc__end failed: %s\n", strerror(err));goto cleanup;}
最后一个函数 handle_event
是一个回调函数,用于处理从perf event array收到的数据。这个函数会被 perf event array 触发,并在每次接收到新的事件时调用。函数首先将数据转换为 data_t
结构体,然后将当前时间格式化为字符串,并打印出事件的时间戳、CPU ID、进程 ID,以及垃圾回收的持续时间。
static void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
{struct data_t *e = (struct data_t *)data;struct tm *tm = NULL;char ts[16];time_t t;time(&t);tm = localtime(&t);strftime(ts, sizeof(ts), "%H:%M:%S", tm);printf("%-8s %-7d %-7d %-7lld\n", ts, e->cpu, e->pid, e->ts/1000);
}
安装依赖
构建示例需要 clang、libelf 和 zlib。包名在不同的发行版中可能会有所不同。
在 Ubuntu/Debian 上,你需要执行以下命令:
sudo apt install clang libelf1 libelf-dev zlib1g-dev
在 CentOS/Fedora 上,你需要执行以下命令:
sudo dnf install clang elfutils-libelf elfutils-libelf-devel zlib-devel
编译运行
在对应的目录中,运行 Make 即可编译运行上述代码:
$ make
$ sudo ./javagc -p 12345
Tracing javagc time... Hit Ctrl-C to end.
TIME CPU PID GC TIME
10:00:01 10% 12345 50ms
10:00:02 12% 12345 55ms
10:00:03 9% 12345 47ms
10:00:04 13% 12345 52ms
10:00:05 11% 12345 50ms
完整源代码:
- https://github.com/eunomia-bpf/bpf-developer-tutorial/tree/main/src/15-javagc
参考资料:
- https://www.brendangregg.com/blog/2015-07-03/hacking-linux-usdt-ftrace.html
- https://github.com/iovisor/bcc/blob/master/libbpf-tools/javagc.c
总结
通过本篇 eBPF 入门实践教程,我们学习了如何使用 eBPF 和 USDT 动态跟踪和分析 Java 的垃圾回收(GC)事件。我们了解了如何在用户态应用程序中设置 USDT 跟踪点,以及如何编写 eBPF 程序来捕获这些跟踪点的信息,从而更深入地理解和优化 Java GC 的行为和性能。
此外,我们也介绍了一些关于 Java GC、USDT 和 eBPF 的基础知识和实践技巧,这些知识和技巧对于想要在网络和系统性能分析领域深入研究的开发者来说是非常有价值的。
如果您希望学习更多关于 eBPF 的知识和实践,可以访问我们的教程代码仓库 https://github.com/eunomia-bpf/bpf-developer-tutorial 以获取更多示例和完整的教程。
相关文章:

eBPF 入门实践教程十五:使用 USDT 捕获用户态 Java GC 事件耗时
eBPF (扩展的伯克利数据包过滤器) 是一项强大的网络和性能分析工具,被广泛应用在 Linux 内核上。eBPF 使得开发者能够动态地加载、更新和运行用户定义的代码,而无需重启内核或更改内核源代码。这个特性使得 eBPF 能够提供极高的灵活性和性能,…...

Linux :: vim 编辑器的初次体验:三种 vim 常用模式 及 使用:打开编辑、退出保存关闭vim
前言:本篇是 Linux 基本操作篇章的内容! 笔者使用的环境是基于腾讯云服务器:CentOS 7.6 64bit。 学习集: C 入门到入土!!!学习合集Linux 从命令到网络再到内核!学习合集 目录索引&am…...

Linux内核进程创建流程
本文代码基于Linux5.10 内容主要参考《Linux内核深度解析》余华兵 当Linux内核要创建一个新进程时, 流程大致如下 ret fork(); if (ret 0) {/* 子进程装载程序 */ret execve(filename, argv, envp); } else if (ret > 0) {/* 父进程 */ } 大致可以分为创建新…...

【03.04】大数据教程--HTTP协议和静态Web服务器
HTTP协议和静态Web服务器 HTTP(Hypertext Transfer Protocol)是一种用于传输超文本的协议,它是Web上的基础通信协议。静态Web服务器是指能够提供静态内容(如HTML、CSS、JavaScript和图像文件)的服务器。 在本教程中&am…...

数据共享传输:台式机和笔记本同步文件!
为什么要在台式机和笔记本同步文件? “我想在台式机和笔记本同步文件。因为我工作时使用笔记本,在家里使用安装了Windows 10系统的台式机,我想要在笔记本和台式机之间同步应用程序、游戏、文档等。有没有一种可以在台式机和笔记本同步文件的…...

java设计模式(十二)代理模式
目录 定义模式结构角色职责代码实现静态代理动态代理jdk动态代理cglib代理 适用场景优缺点 定义 代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。说简单点,代理模式就是设置一个中间代理来控制访问原目标对象,以达到…...

Umi微前端水印踩坑以及解决方案
最近公司需要在管理后台加一个水印方案~ 项目用的umi方案,以为就是改一个配置的问题,后来发现坑点还蛮多~ 希望此稳定能帮助到用umi 的你们. 一. 先来说说心路历程 坑点1 umi的水印适配只能在layout中进行配置,也就是路由配置中layout为false的页面无法配置水印,比如说登录页…...

Android RK3588-12 hdmi-in Camera方式支持NV24格式
hdmi-in Camera方式支持NV24格式 modified: hardware/interfaces/camera/device/3.4/default/ExternalCameraDevice.cpp modified: hardware/interfaces/camera/device/3.4/default/ExternalCameraDeviceSession.cpp diff --git a/hardware/interfaces/camera/device/3.4…...

Hive窗口函数详细介绍
文章目录 Hive窗口函数概述样本数据表结构表数据 窗口函数窗口聚合函数count()SQL演示 sum()SQL演示 avg()SQL演示 min()SQL演示 max()SQL演示 窗口分析函数first_value() 取开窗第一个值应用场景SQL演示 last_value()取开窗最后一个值应用场景SQL演示 lag(col, n, default_val…...

牛客网【c语言练习】
单选题 下面代码段的输出是(-12 ) int main() {int a3; printf("%d\n",(aa-a*a)); } aa-9,此时还是等于3,因为a*a只是运算,并没有赋值;之后再算a-9,运算之前a等于3,运算…...

C++类和对象(上)
文章目录 🦍1. 面向过程和面向对象🦧2. 类的引入🐶3. 类的定义🦮4. 类的访问控制和封装🍖4.1 访问限定符🍖4.2 封装 🐩5. 类的作用域🐅6. 类的实例化🐄7. 类的大小计算&a…...

JavaScript 数据透视表 DHTMLX Pivot Crack
DHTMLX Pivot JavaScript 数据透视表 - 强大的数据汇总和报告 使用我们的高速 JavaScript/HTML5 Pivot 组件可视化您的复杂数据,从而提高您的商业智能。 它可以帮助您以方便的方式汇总大型数据集。 主要特征 纯 JavaScript 库,可轻松与任何服务器端集成…...

QT链接库设置
以windows 平台为例,在.pro 文件中: 1 增加 INCLUDEPATH <头文件路径> DEPENDPATH <头文件路径> 2 LIBS -L<库目录路径> -l<库得名字> 3 设置MT、MTD、MD、MDD运行时库 win32:CONFIG(debug, debug|release): { QMAKE_CFLAGS_…...

零点起飞学Android——期末考试课本复习重点
目录 第一章 认识Android第二章 Android常见界面布局第三章 Android常用基本控件第四章 Android 高级控件第五章 Android菜单和对话框 第一章 认识Android 1. Android 界面设计被称为______。 答案:布局 2. Android中常见的布局包括______、______ 、______ 、____…...

Redis为什么快?
目录 Redis为什么快?渐进式ReHash全局哈希表渐进式ReHash 缓存时间戳 Redis为什么快? 纯内存访问; 单线程避免上下文切换; 渐进式ReHash、缓存时间戳; 前面两个都比较好理解,下面我们主要来说下 渐进式…...

Zabbix从入门到精通以及案例实操系列
1、Zabbix入门 1.1、Zabbix概述 Zabbix是一款能够监控各种网络参数以及服务器健康性和完整性的软件。Zabbix使用灵活的通知机制,允许用户为几乎任何事件配置基于邮件的告警。这样可以快速反馈服务器的问题。基于已存储的数据,Zabbix提供了出色的报告和…...

水声声波频率如何划分?水声功率放大器可将频率放大到20MHz吗?
水声声波频率如何划分?水声功率放大器可将频率放大到20MHz吗? 现如今我们可以在地球任意地区实现通信,是因为电磁波的作用。但是我们都知道海洋占了全球十分之七面积,电磁波在水下衰减速度太快,无法做到远距离传输&am…...

网络攻防技术--论文阅读--《基于自动数据分割和注意力LSTM-CNN的准周期时间序列异常检测》
英文题目:Anomaly Detection in Quasi-Periodic Time Series based on Automatic Data Segmentation and Attentional LSTM-CNN 论文地址:Anomaly Detection in Quasi-Periodic Time Series Based on Automatic Data Segmentation and Attentional LST…...

C++ 学习 ::【基础篇:08】:C++ 中 struct 结构体的认识【面试考点:C 与 C++ 中结构体的区别】
本系列 C 相关文章 仅为笔者学习笔记记录,用自己的理解记录学习!C 学习系列将分为三个阶段:基础篇、STL 篇、高阶数据结构与算法篇,相关重点内容如下: 基础篇:类与对象(涉及C的三大特性等&#…...

Electron开发:打包和发布 Electron 应用
https://start.spring.io/ 在线数据分析网站 https://tj.aldwx.com/ https://www.spsspro.com/ win10如何分屏 拖到边缘 Electron 环境搭建 https://www.electronjs.org/zh/docs/latest/tutorial/%E6%89%93%E5%8C%85%E6%95%99%E7%A8%8B electron 隐藏菜单 electron 标题栏 设…...

【每日一题Day222】LC1110删点成林 | dfs后序
删点成林【LC1110】 给出二叉树的根节点 root,树上每个节点都有一个不同的值。 如果节点值在 to_delete 中出现,我们就把该节点从树上删去,最后得到一个森林(一些不相交的树构成的集合)。 返回森林中的每棵树。你可以按…...

[ChatGPT] 从 GPT-3.5 到 GPT-5 的进化之路 | ChatGPT和程序员 : 协作 or 取代
⭐作者介绍:大二本科网络工程专业在读,持续学习Java,努力输出优质文章 ⭐作者主页:逐梦苍穹 ⭐如果觉得文章写的不错,欢迎点个关注一键三连😉有写的不好的地方也欢迎指正,一同进步😁…...

6.4 GDP调试多进程程序
目录 GDB调试多进程程序 安装gdb gdb编译 运行gdb 单步运行 从头到尾运行 下一步 运行子进程 同时运行父进程 查看运行的进程 切换进程 退出 GDB调试多进程程序 set follow-fork-mode child 设置GDB调试子进程 set follow-fork-mode parent 设置GDB调试父进…...

TDengine 时序数据的保留策略
“TDengine除vnode分片之外,还对时序数据按照时间段进行分区。每个数据文件只包含一个时间段的时序数据,时间段的长度由DB的配置参数days决定。这种按时间段分区的方法还便于高效实现数据的保留策略,只要数据文件超过规定的天数(系…...

Java-多线程解析1
一、线程的描述: 1、线程是一个应用程序进程中不同的执行路径比例如:一个WEB服务器,能够为多个用户同时提供请求服务;而 -> 进程是操作系统中正在执行的不同的应用程序,比如:我们可以同时打开系统的word和游戏 2、多…...

PHP 判断用户当前坐标是否在电子围栏内
可以使用射线法判断用户当前坐标点是否在电子围栏内。 具体步骤如下: 1. 将电子围栏的四个角坐标按顺序连接成一个封闭多边形。 2. 从用户当前坐标点向外发射一条射线,判断这条射线与多边形的交点个数。 3. 如果交点个数为奇数,则用户当前…...

Java版本工程管理系统源码企业工程项目管理系统简介
一、立项管理 1、招标立项申请 功能点:招标类项目立项申请入口,用户可以保存为草稿,提交。 2、非招标立项申请 功能点:非招标立项申请入口、用户可以保存为草稿、提交。 3、采购立项列表 功能点:对草稿进行编辑&#x…...

高速缓存(cache)的原理: 了解计算机架构与性能优化
计基之存储器层次结构 Author: Once Day Date: 2023年5月9日 长路漫漫,而今才刚刚启程! 本内容收集整理于《深入理解计算机系统》一书。 参看文档: 捋一捋Cache - 知乎 (zhihu.com)iCache和dCache一致性 - 知乎 (zhihu.com)C…...

【Vue3+TS项目】硅谷甄选day04--顶部组件搭建+面包屑+路由鉴权
顶部组件搭建 顶部左侧折叠和面包屑实现 左侧菜单刷新折叠的问题解决---属性default-active 折叠之后图标不见:icon放在插槽外面----element的menu属性:collapse project\src\layout\index.vue // 获取路由对象 import { useRoute } from vue-route…...

某oa 11.10 未授权任意文件上传
漏洞简介 之前也对通达 oa 做过比较具体的分析和漏洞挖掘,前几天看到通达 oa 11.10 存在未授权任意文件上传漏洞,于是也打算对此进行复现和分析。 环境搭建 https://www.tongda2000.com/download/p2019.php 下载地址 :https://cdndown.tongda…...