使用`LD_PRELOAD`和`jemalloc`实现C/C++信号的内存堆栈信息收集
文章目录
- 0. 概要
- 1. 编译jemalloc
- 2. 编译钩子共享库liballoc_hook.so
- 3. 使用`LD_PRELOAD`加载钩子库liballoc_hook.so测试
- 3.1 设置环境变量
- 3.2 使用`LD_PRELOAD`加载钩子库并运行程序
- 3.3 发送`SIGUSR1`信号以触发堆栈信息打印
- 3.4 使用jeprof解析heap堆栈信息文件
- 4. 示例程序example.cpp代码
- 5. 注意事项
- 6. jemalloc的限制
0. 概要
本文介绍如何结合LD_PRELOAD
与jemalloc
,在接收到SIGUSR1
信号时打印程序的堆栈信息。详细步骤包括编译和配置jemalloc
,编写信号处理程序,并通过LD_PRELOAD
加载共享库的方法。
1. 编译jemalloc
编译并安装启用prof
功能的jemalloc
。以下是Ubuntu 18.04上的编译步骤:
git clone https://github.com/jemalloc/jemalloc.git # 本文测试的版本是jemalloc-5.3.0
cd jemalloc
./configure --prefix=/usr/local --enable-prof CFLAGS="-fPIC"
make -j10
sudo make install
确保编译 libjemalloc.a
时使用了 -fPIC
选项。
2. 编译钩子共享库liballoc_hook.so
创建一个名为alloc_hook.c
的文件,并实现信号处理函数:
/*gcc -o liballoc_hook.so -shared -fPIC alloc_hook.c -Wl,-Bstatic -ljemalloc -Wl,-Bdynamic
*/
#include <jemalloc/jemalloc.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>// 信号处理函数
void handle_signal(int signum) {if (signum == SIGUSR1) {// 触发 jemalloc 的 heap profiling dumpmallctl("prof.dump", NULL, NULL, NULL, 0);printf("Heap profile dump generated.\n");}
}// 初始化函数
void __attribute__((constructor)) init_hook() {// 设置信号处理函数signal(SIGUSR1, handle_signal);printf("Signal handler for SIGUSR1 is set.\n");
}
使用以下命令编译liballoc_hook.so
并静态链接libjemalloc.a
:
gcc -o liballoc_hook.so -shared -fPIC alloc_hook.c -Wl,-Bstatic -ljemalloc -Wl,-Bdynamic -lpthread
3. 使用LD_PRELOAD
加载钩子库liballoc_hook.so测试
假设你的目标程序是example
,通过LD_PRELOAD
加载liballoc_hook.so钩子库,按照以下步骤运行和测试:
3.1 设置环境变量
export MALLOC_CONF="prof:true,prof_active:true,lg_prof_sample:0,tcache:false,prof_prefix:jeprof.out"
prof:true
:启用配置文件。prof_active:true
:启用性能分析。lg_prof_sample:0
:设置采样率为最高。tcache:false
:禁用线程缓存,可能影响性能,但在进行性能分析时,可以提供更准确的内存分配数据。prof_prefix:jeprof.out
:指定性能分析输出文件前缀。
3.2 使用LD_PRELOAD
加载钩子库并运行程序
LD_PRELOAD="/path/to/liballoc_hook.so" ./example
3.3 发送SIGUSR1
信号以触发堆栈信息打印
killall -10 example
# 或者
killall -SIGUSR1 example
通过以上步骤,你可以在接收到SIGUSR1
信号时打印jemalloc
的堆栈信息,并将其输出到本地目录。本文得到的堆栈信息文件名为jeprof.out.60571.0.m0.heap
。
3.4 使用jeprof解析heap堆栈信息文件
通过如下命令分析该堆栈信息文件:
jeprof --show_bytes --text --lines ./example ./jeprof.out.60571.0.m0.heap
解析结果示例如下:
$ jeprof --show_bytes --text --lines ./example jeprof.out.60571.0.m0.heap
Using local file ./example.
Using local file jeprof.out.60571.0.m0.heap.
Total: 83512 B82944 99.3% 99.3% 82944 99.3% prof_backtrace_impl /tmp/jemalloc-5.3.0/src/prof_sys.c:103448 0.5% 99.9% 448 0.5% allocateIntArray /home/test/jemalloc_test/example.cpp:1380 0.1% 100.0% 80 0.1% allocateDynamicArray /home/test/jemalloc_test/example.cpp:32 (discriminator 1)32 0.0% 100.0% 32 0.0% allocateString /home/test/jemalloc_test/example.cpp:258 0.0% 100.0% 8 0.0% allocateDouble /home/test/jemalloc_test/example.cpp:190 0.0% 100.0% 1024 1.2% _IO_new_file_overflow /build/glibc-2ORdQG/glibc-2.27/libio/fileops.c:7590 0.0% 100.0% 1024 1.2% _IO_new_file_xsputn /build/glibc-2ORdQG/glibc-2.27/libio/fileops.c:12660 0.0% 100.0% 1024 1.2% _IO_puts /build/glibc-2ORdQG/glibc-2.27/libio/ioputs.c:400 0.0% 100.0% 1024 1.2% __GI__IO_doallocbuf /build/glibc-2ORdQG/glibc-2.27/libio/genops.c:3650 0.0% 100.0% 1024 1.2% __GI__IO_file_doallocate /build/glibc-2ORdQG/glibc-2.27/libio/filedoalloc.c:1010 0.0% 100.0% 568 0.7% __libc_start_main /build/glibc-2ORdQG/glibc-2.27/csu/../csu/libc-start.c:3100 0.0% 100.0% 82944 99.3% _dl_start_user :?0 0.0% 100.0% 568 0.7% _start ??:?0 0.0% 100.0% 448 0.5% allocateMemory /home/test/jemalloc_test/example.cpp:510 0.0% 100.0% 8 0.0% allocateMemory /home/test/jemalloc_test/example.cpp:520 0.0% 100.0% 32 0.0% allocateMemory /home/test/jemalloc_test/example.cpp:530 0.0% 100.0% 80 0.1% allocateMemory /home/test/jemalloc_test/example.cpp:540 0.0% 100.0% 82944 99.3% call_init /build/glibc-2ORdQG/glibc-2.27/elf/dl-init.c:720 0.0% 100.0% 82944 99.3% imalloc (inline) /tmp/jemalloc-5.3.0/src/jemalloc.c:26940 0.0% 100.0% 82944 99.3% imalloc_body (inline) /tmp/jemalloc-5.3.0/src/jemalloc.c:25500 0.0% 100.0% 1024 1.2% init_hook ??:?0 0.0% 100.0% 82944 99.3% je_malloc_default /tmp/jemalloc-5.3.0/src/jemalloc.c:27220 0.0% 100.0% 82944 99.3%je_prof_backtrace /tmp/jemalloc-5.3.0/src/prof_sys.c:2840 0.0% 100.0% 82944 99.3% je_prof_tctx_create /tmp/jemalloc-5.3.0/src/prof.c:1950 0.0% 100.0% 568 0.7% main /home/test/jemalloc_test/example.cpp:600 0.0% 100.0% 82944 99.3% prof_alloc_prep (inline) /tmp/jemalloc-5.3.0/include/jemalloc/internal/prof_inlines.h:1410 0.0% 100.0% 81920 98.1% std::__once_callable ??:0
4. 示例程序example.cpp代码
以下是完整的example.cpp
代码,编译方法: g++ -g -o example example.cpp
:
#include <sys/mman.h> // mmap, munmap
#include <unistd.h> // usleep
#include <csignal> // signal, sigaction
#include <cstdlib> // rand()和srand()
#include <ctime> // time()
#include <iostream>
#include <string>
#include <vector>// 分配int数组
void allocateIntArray() {const int* intPtr = new int[100];std::cout << "Allocated int array at: " << intPtr << std::endl;
}// 分配double
void allocateDouble() {const double* doublePtr = new double(3.14);std::cout << "Allocated double at: " << doublePtr << ", value: " << *doublePtr << std::endl;
}// 分配字符串
void allocateString() {const std::string* strPtr = new std::string("Hello, World!");std::cout << "Allocated string at: " << strPtr << ", value: " << *strPtr << std::endl;
}// 分配动态数组
void allocateDynamicArray() {size_t arraySize = 10;size_t* const arrayPtr = new size_t[arraySize];std::cout << "Allocated array of " << arraySize << " ints at: " << arrayPtr << std::endl;for (size_t i = 0; i < arraySize; ++i) {arrayPtr[i] = i;}
}// 使用mmap分配内存
void allocateMmap() {size_t mmapSize = 4096; // 4KBconst void* mmapPtr = mmap(nullptr, mmapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);if (mmapPtr == MAP_FAILED) {perror("mmap failed");} else {std::cout << "Allocated mmap at: " << mmapPtr << ", size: " << mmapSize << " bytes" << std::endl;}
}void allocateMemory() {allocateIntArray();allocateDouble();allocateString();allocateDynamicArray();allocateMmap();
}int main() {usleep(100000); // 100msallocateMemory();while (true) {usleep(100000); // 100ms}return 0;
}
5. 注意事项
-
编译
libjemalloc.a
时请记得添加CFLAGS="-fPIC"
:./configure --prefix=/usr/local --enable-prof CFLAGS="-fPIC"
-
liballoc_hook.so
必须是静态链接libjemalloc.a
。 -
liballoc_hook.so
需要动态链接libpthread.so
,编译时记得切回动态链接方式:gcc -o liballoc_hook.so -shared -fPIC alloc_hook.c -Wl,-Bstatic -ljemalloc -Wl,-Bdynamic -lpthread
-
请勿使用动态加载
libjemalloc.so
。如果使用如下命令:LD_PRELOAD="/path/to/liballoc_hook.so /usr/local/lib/libjemalloc.so" ./example
jeprof
解析heap的结果会显示为:
$ jeprof --show_bytes --text --lines ./example ./jeprof.out.60571.0.m0.heap
Using local file ./example.
Using local file ./jeprof.out.60571.0.m0.heap.
Total: 83512 B83512 100.0% 100.0% 83512 100.0% prof_backtrace_impl /tmp/jemalloc-5.3.0/src/prof_sys.c:1030 0.0% 100.0% 568 0.7% 0x00005610af62de49 ??:00 0.0% 100.0% 448 0.5% 0x00005610af62df3b ??:00 0.0% 100.0% 8 0.0% 0x00005610af62df8e ??:00 0.0% 100.0% 32 0.0% 0x00005610af62e039 ??:00 0.0% 100.0% 80 0.1% 0x00005610af62e137 ??:00 0.0% 100.0% 448 0.5% 0x00005610af62e299 ??:00 0.0% 100.0% 8 0.0% 0x00005610af62e29e ??:00 0.0% 100.0% 32 0.0% 0x00005610af62e2a3 ??:00 0.0% 100.0% 80 0.1% 0x00005610af62e2a8 ??:00 0.0% 100.0% 568 0.7% 0x00005610af62e2c3 ??:0
可以看到example.cpp
部分的信息无法显示,因此不可使用LD_PRELOAD
同时加载liballoc_hook.so
和libjemalloc.so
。
6. jemalloc的限制
尽管jemalloc
在内存管理和性能分析方面具有强大的功能,但它也存在一些限制:
-
无法hook mmap
jemalloc
无法hook通过mmap
或munmap
进行的内存分配。这意味着如果程序中大量使用mmap
进行内存分配,这部分内存不会被jemalloc
监控和管理,也不会包含在jemalloc
的内存分析报告中。因此,对于需要分析这种内存分配行为的程序,jemalloc
可能不是最佳选择。 -
无法hook线程相关信息
jemalloc
无法直接监控线程的创建和销毁。这对于某些需要详细分析线程行为的应用程序来说是一个限制。尽管jemalloc
可以通过配置和编译选项优化内存分配以适应多线程环境,但它不能提供与线程操作相关的详细信息。 -
无法hook直接系统调用的内存分配
如果程序通过直接系统调用(如brk
或其他系统级内存分配调用)分配内存,这些调用将绕过jemalloc
的内存管理机制。因此,jemalloc
无法跟踪这些内存分配行为,导致分析结果不完整。 -
高采样率对性能的影响
开启高采样率(如lg_prof_sample:0
)会显著影响程序的性能。虽然高采样率能够提供更详细和频繁的内存分配数据,但它也会导致程序运行速度变慢。因此,在生产环境中需要权衡采样率和性能之间的关系。 -
配置和使用复杂度
正确配置和使用jemalloc
需要一定的专业知识和经验。对于不熟悉内存管理和性能分析的开发者来说,jemalloc
的配置选项和参数可能显得复杂,容易出错。因此,在使用jemalloc
进行内存分析之前,建议详细阅读官方文档并进行充分测试。 -
与其他内存管理库的兼容性问题
在某些情况下,jemalloc
可能与其他内存管理库或工具产生兼容性问题。这可能导致程序在链接和运行时遇到问题。因此,在将jemalloc
集成到现有项目时,需要进行全面的测试以确保兼容性。
总的来说,尽管jemalloc
是一款功能强大的内存管理库,但在使用过程中需要注意其自身的限制,并根据具体需求进行权衡和取舍。
相关文章:
使用`LD_PRELOAD`和`jemalloc`实现C/C++信号的内存堆栈信息收集
文章目录 0. 概要1. 编译jemalloc2. 编译钩子共享库liballoc_hook.so3. 使用LD_PRELOAD加载钩子库liballoc_hook.so测试3.1 设置环境变量3.2 使用LD_PRELOAD加载钩子库并运行程序3.3 发送SIGUSR1信号以触发堆栈信息打印3.4 使用jeprof解析heap堆栈信息文件 4. 示例程序example.…...
计算机组成原理(四)Cache存储器
文章目录 Cache存储器的基本原理cache命中率、平均访问时间、效率地址映射全相联映射直接映射组相联映射 查找算法cache 存储器替换策略cache 存储器-写操作策略习题 Cache存储器的基本原理 Cache是一种高速缓冲寄存器,是为了解决CPU和主存之间速度不匹配而采用的一…...
怎么做成的文件二维码?扫阅览文件的制作方法
现在用二维码来分享或者查看文件是一种很常用的方式,比如常见的文件内容有简历、资料、作品、压缩包等等。通过将文件生成二维码能够在提升文件传输速度的同时还有利于用户体验的提升,那么如何制作可以长期提供文件预览或者下载的二维码呢? …...
js 前端 Function.prototype.call.call(0[‘toString‘], *, 16)
这个函数将 数组转任意进制 Function.prototype.call.call(0[toString], *, 16)...
李沐:用随机梯度下降来优化人生!
大侠幸会,在下全网同名「算法金」 0 基础转 AI 上岸,多个算法赛 Top 「日更万日,让更多人享受智能乐趣」 今天我们来聊聊达叔 6 大核心算法之 —— 优化 算法。吴恩达:机器学习的六个核心算法! 梯度下降优化算法是机器…...
Linux 环境.Net程序堆栈查询
# 安装 dotnet tool install --global dotnet-dump# 收集信息, 产生 core_XXX 文件 dotnet-dump collect -p pid# 分析 core_XXX 文件 dotnet dump analyze core_XXX# 列出 大于 XXX 字节的 对象 dumpheap -stat -min XXX# 查看对象具体信息 dumpobj address_XXX# 查看对应引用…...
志愿服务管理系统的设计
管理员账户功能包括:系统首页,个人中心,管理员管理,基础数据管理,广场论坛管理,志愿活动管理,活动报名管理 前台账户功能包括:系统首页,个人中心,志愿活动&a…...
微信小游戏5月畅销榜,新老产品更替显著,亿级爆款频出
小游戏由于微信的平台扶持,被视为可以大力发掘的蓝海,成为国内游戏最大的增长机会之一,随着越来越多的大厂和中小厂转向了小游戏赛道,每个月的小游戏畅销榜单都有不同变化。 5月的小游戏畅销榜显示,小游戏市场正经历显…...
自己想要公开自己的学习方法,但是自己很害怕自己的学习方法是一个错误的,因为对于自己而言,专升本的机会只有一次
分享自己的学习方法可能需要一定的勇气,特别是当你担心可能会受到批评或是不被理解时。以下是一些建议,可以帮助你克服这种恐惧:(kimi编辑器自己对于这些内容的基础批注) 自我肯定:首先,认识到你…...
linux centos consul1.15.2一键安装部署
consul原理、作用、安装相关内容 一、理论部分二、安装下载版本地址三、安装consul服务 一、理论部分 1、consul的原理 Consul的原理及作用可以归纳为以下几点: ①、基于Gossip协议的通信:Consul使用了基于Gossip协议的Serf实现来进行通信。 Gossip协议…...
速盾:dns和cdn区别?
DNS(Domain Name System)和CDN(Content Delivery Network)是互联网中两个不同但相互关联的服务。下面将详细解释DNS和CDN的区别。 功能和作用: DNS:DNS是将域名转换为IP地址的服务,它充当着互联…...
多目标跟踪中用到的求解线性分配问题(Linear Assignment Problem,LAP)C++
多目标跟踪中用到的求解线性分配问题(Linear Assignment Problem,LAP)C flyfish python实现,说的比这里详细 lapjv.h和lapjv.cpp代码在https://github.com/shaoshengsong/DeepSORT C代码调用 #include <iostream> #include <ve…...
Unity | Shader基础知识(第十四集:简单效果练习)
目录 前言 一、效果预览 1.弧形边缘光 二、效果制作 1. 制作弧形边缘光 2.弧形边缘光进阶 3.弧形边缘光调节渐变范围 4.边缘光突变 5.同心圆 三、加入世界坐标做效果 1.绘制结界 2.斑马球 3.效果合并 四、作者的碎碎念 前言 有粉丝建议说,让我继续更新…...
Vue48-ref属性
一、需求:操作DOM元素 1-1、使用原生的id属性 不太好! 1-2、使用 ref属性 原生HTML中,用id属性给元素打标识,vue里面用ref属性。 给哪个元素加了ref属性,vc实例对象就收集哪个元素!!࿰…...
【SpringCloud学习笔记】RabbitMQ(中)
1. 交换机概述 前面《RabbitMQ上篇》我们使用SpringAMQP来演示如何用Java代码操作RabbitMQ,当时采用的是生产者直接将消息发布给队列,但是实际开发中不建议这么做,更加推荐生产者将消息发布到交换机(exchange),然后由exchange路由…...
【C++】类和对象的引入
文章目录 前言一、类的定义二、类的访问控制与封装三、类的作用域四、类的实例化五、类的存储方式及大小计算六、隐藏的this指针 前言 C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。 C是基于面向对象的&…...
11.5.k8s中pod的调度-cordon,drain,delete
目录 一、概念 二、使用 1.cordon 停止调度 1.1.停止调度 1.2.解除恢复 2.drain 驱逐节点 2.1.驱逐节点 2.2.参数介绍 2.3.解除恢复 3.delete 删除节点 一、概念 cordon节点,drain驱逐节点,delete 节点,在对k8s集群节点执行维护&am…...
Java中线程的创建方式
一、继承Thread类,重写run方法 public class MyThread{public static void main(String[] args) {Thread threadDome new ThreadDome();threadDome.start();} }class ThreadDome extends Thread{Overridepublic void run() {for (int i 0; i < 5; i) {try {Th…...
猫头虎推荐20个值得体验的通用大模型
猫头虎推荐20个值得体验的通用大模型 🚀 大家好,我是猫头虎,一名专注于科技领域的自媒体博主。今天是周一,新的开始,我们来深入探讨一下当前最值得体验的通用大模型。这些AI模型不仅功能强大,而且在各自领…...
Novartis诺华制药社招综合能力性格动机问卷入职测评笔试题库答案及包过助攻
【华东同舟求职】由资深各行业从业者建立的一站式人才服务网络平台,现阶段目标是“提升全市场各行业岗位信息的流动性和透明度”。我们接受众多行业机构的直接委托发布,并尽力通过各种方法搜寻高价值岗位信息。事实上,我们以发现不为人知的优…...
Adam优化算法
Adam优化算法 Adam(Adaptive Moment Estimation)是一种用于训练深度学习模型的优化算法,由Diederik P. Kingma和Jimmy Ba在2014年提出。Adam结合了动量和自适应学习率的方法,具有高效、稳定和适应性强的特点,被广泛应…...
MYSQL 三、mysql基础知识 7(MySQL8其它新特性)
一、mysql8新特性概述 MySQL从5.7版本直接跳跃发布了8.0版本 ,可见这是一个令人兴奋的里程碑版本。MySQL 8版本在功能上 做了显著的改进与增强,开发者对MySQL的源代码进行了重构,最突出的一点是多MySQL Optimizer优化器进行了改进。不仅在速度…...
git error: does not have a commit checked out fatal: adding files failed
git add net error: net/ does not have a commit checked out fatal: adding files failed这个错误是因为尝试将一个尚未被提交的文件夹添加到Git中。解决这个问题的方法是先将文件夹中的文件提交到Git仓库中,然后再将文件夹添加到Git中。 首先,需要进…...
Java Websocket分片发送
一、分片发送和接收(复杂) 如果数据量太大,需要分多次发送, 需要考虑数据划分和重组的问题。 二、具体思路 每次发送和接收用一个布尔值变量指定是否为最后一个分片。 三、具体使用 (一)字符串分片发送: sendText(文本, 布尔值)…...
vivado NODE、PACKAGE_PIN
节点是Xilinx部件上用于路由连接或网络的设备对象。它是一个 WIRE集合,跨越多个瓦片,物理和电气 连接在一起。节点可以连接到单个SITE_, 而是简单地将NETs携带进、携带出或携带穿过站点。节点可以连接到 任何数量的PIP,并且也可以…...
JavaEE、SSM基础框架、JavaWeb、MVC(认识)
目录 一、引言 (0)简要介绍 (1)主要涉及的学习内容 (2)学习的必要性 (3)适用学习的人群(最好有这个部分的知识基础) (4)这个基础…...
【漏洞复现】飞企互联-FE企业运营管理平台 treeXml.jsp SQL注入漏洞
0x01 产品简介 飞企互联-FE企业运营管理平台是一个基于云计算、智能化、大数据、物联网、移动互联网等技术支撑的云工作台。这个平台可以连接人、链接端、联通内外,支持企业B2B、C2B与020等核心需求,为不同行业客户的互联网转型提供支持。其特色在于提供…...
Android基础-运行时权限
一、引言 随着智能手机和移动互联网的普及,Android操作系统作为其中的佼佼者,其安全性问题日益受到关注。为了保障用户数据的安全和隐私,Android系统引入了权限机制来管理和控制应用程序对系统资源和用户数据的访问。特别是在Android 6.0&am…...
postman断言及变量及参数化
1:postman断言 断言:判断接口是否执行成功的过程 针对接口请求完成之后,针对他的响应状态码及响应信息进行判断,代码如下: //判断响应信息状态码是否正确 pm.test("Status code is 200", function () { pm.response.…...
安装和使用TrinityCore NPCBot
安装TrinityCore NPCBot 官网:GitHub - trickerer/Trinity-Bots: NPCBots for TrinityCore and AzerothCore 3.3.5 基本安装方法 Follow TrinityCore Installation Guide (https://TrinityCore.info/) to install the server firstDownload NPCBots.patch and put …...
营销型网站./新媒体
基于MQTT协议的消息传输为什么MQTT?之前刚写了一篇关于socket的文章,为什么又来一个MQTT?因为MQTT协议就是socket接口实现的啊(socket和MQTT只是层级不一样而已),并且感觉发布/订阅模式非常棒,于…...
win10建站wordpress/今日热点新闻事件摘抄
点击“运行”,在子菜单中选择“运行”。1、在运行窗口中输入:cmd /k javac "$(FULL_CURRENT_PATH)" & echo 编译成功! & PAUSE & EXIT点击保存后,在弹出的窗口中设置名称:取名为JAVA编译2、重复…...
自己做书画交易网站/电销外包团队在哪找
【现象】 原因是{}导致的 【解决方法】 logger.info(f接口地址:/user/dashboard/getBaseCount ,请求参数:{},返回结果:{result}) 修改为: logger.info(f接口地址:/user/dashboard/getBaseCount ,请求参数:,返回结果:{result})...
连云港网站建设公司/成都seo网络优化公司
1、%s:字符型 %s:正常输出字符 %6s:输出6位字符,不足6位字符的左边用空格符补充,超过6位字符的输出全部字符(%-6d为右边用空格补充) %.2s:截取前2位字符,不足2位的左边…...
网站拓扑图怎么做/制作网站免费
昨天茂妈在我们手机摄影修图11号课后群,看见有新手妈妈说:宝宝的皮肤怎么调,看起来都不通透呢。即使手机品牌不断升级硬件,但手机和单反在对光线的需求上还是差距很大。了解单反的同学应该知道,可以通过调整感光度&…...
wordpress禁用主题字体/推广网站大全
案例:电脑清理怎么做? 【求一个电脑清理的好方法!电脑垃圾文件太多了又不敢随意删除,怕误删重要的文件!哪位友友可以帮我出出主意呀?到底应该怎么清理电脑呢?】 电脑使用的时间长了都会慢慢变…...