Perf的原理、编译以及使用

article/2025/9/26 13:23:02

​1、背景

1.1 性能分析

系统级性能优化通常包括两个阶段:性能剖析(performance profiling)和代码优化。性能剖析的目标是寻找性能瓶颈,查找引发性能问题的原因及热点代码。代码优化的目标是针对具体性能问题而优化代码或编译选项,以改善软件性能。一般在工作中比较关心的是性能瓶颈,特别是算法。

当在系统全功能启动的时候,算法一般需要将设备的性能用到极限,而在这个过程中不免出现各类性能上的瓶颈,此时需要分析自身的一些性能瓶颈在什么地方就可以用到专门的性能分析工具perf。

1.2 术语和缩写

Perf

perf是一款Linux性能分析工具。Linux性能计数器是一个新的基于内核的子系统,它提供一个性能分析框架,比如硬件(CPU、PMU(Performance Monitoring Unit))功能和软件(软件计数器、tracepoint)功能。通过perf,应用程序可以利用PMU、tracepoint和内核中的计数器来进行性能统计。它不但可以分析制定应用程序的性能问题(per thread),也可以用来分析内核的性能问题。

2、Perf工具概述

2.1 背景知识

2.1.1 tracepoints

tracepoints是散落在内核源码中的一些hook,它们可以在特定的代码被执行到时触发,这一特定可以被各种trace/debug工具所使用。

perf将tracepoint产生的时间记录下来,生成报告,通过分析这些报告,可以了解程序运行期间内核的各种细节,对性能症状做出准确的诊断。

2.1.2 硬件特性之cache

内存读写是很快的,但是还是无法和处理器指令执行速度相比。为了从内存中读取指令和数据,处理器需要等待,用处理器时间来衡量,这种等待非常漫长。cache是一种SRAM,读写速度非常快,能和处理器相匹配。因此将常用的数据保存在cache中,处理器便无需等待,从而提高性能。cache的尺寸一般都很小,充分利用cache是软件调优非常重要部分

2.2 调优方向

Hardware Event由PMU部件产生,在特定的条件下探测性能事件是否发生以及发生的次数

Software Event是内核产生的事件,分布在各个功能模块中,统计和操作系统相关性能事件。比如进程切换,ticks等。

Tracepoint Event是内核中静态tracepoint所触发的事件,这些tracepoint用来判断程序运行期间内核的行为细节。比如slab分配器的分配次数等。

2.3 火焰图

火焰图(FlameGraph)是由Linux性能优化大师BrendanGregg发明的,和所有其他的trace和profiling方法不同的是,Flame Graph以一个全局的视野来看待时间分布,它从底部往顶部,列出所有可能的调用栈。其他的呈现方法,一般只能列出单一的调用栈或者非层次化的时间分布。

以典型的分析CPU时间花费到哪个函数的on-cpu火焰图为例来展开。CPU火焰图中的每一个方框是一个函数,方框的长度,代表了它的执行时间,所以越宽的函数,执行越久。火焰图的楼层每高一层,就是更深一级的函数被调用,最顶层的函数,是叶子函数。

3、Perf检测原理

perf是利用PMU、tracepoint和内核中的计数器来进行性能统计

3.1 PMU

在这里插入图片描述

PerformanceMonitor Unit,性能监视单元,其为CPU提供的一个单元,属于硬件的范畴。通过访问相关的寄存器能读取到CPU的一些性能数据,目前大部分CPU都会提供相应的PMU。其包括各种core, offcore和uncore事件

Perf可以对程序进行函数级别的采样,从而了解程序的性能瓶颈在哪里。其基本原理是:每隔一个固定时间,就是CPU上产生一个中断,看当前是哪个进程、哪个函数,然后给对应的进程和函数加一个统计值,这样就知道CPU有多少时间在某个进程或某个函数上了。具体原作原理就是直接通过系统调用syscall/ioctl或者监听SW的event来看性能。

3.2 在PC的ubuntu系统上支持perf

在Ubuntu系统中需要安装以下2个package方可支持perf

sudo apt install linux-tools-common
以及相关的linux的内核工具

sudo apt install linux-tools-5.4.0-56-generic
此时就可以直接使用perf,用于先期的实验。

4、嵌入式设备编译perf

一般内核中的perf工具均在内核根目录下的tools目录。

c r o s s c o m p i l e = a r m − l i n u x − g n x x x x x − m a k e C R O S S C O M P I L E = cross_compile = arm-linux-gnxxxxx- make CROSS_COMPILE= crosscompile=armlinuxgnxxxxxmakeCROSSCOMPILE=cross_compile ARCH=arm tools/perf clean
make CROSS_COMPILE=$cross_compile ARCH=arm tools/perf
完成编译后会产生相关的工具perf
注意的是要使用该工具必须要一些库,主要有elfutils这个一般在交叉编译器中可以找到 zlib也可以在交叉编译中找到 libunwind

当然,必须在内核中打开perf的events功能:

5、perf的使用

在本文中我们介绍几个最常用的命令,而更多的命令请到参考网址中的wiki官方网址上查阅

5.1 perf lsit

列出当前系统支持的所有性能事件。包括硬件性能事件、软件性能事件以及检查点

List of pre-defined events (to be used in -e):branch-instructions OR branches                    [Hardware event]branch-misses                                      [Hardware event]cache-misses                                       [Hardware event]cache-references                                   [Hardware event]cpu-cycles OR cycles                               [Hardware event]instructions                                       [Hardware event]stalled-cycles-backend OR idle-cycles-backend      [Hardware event]stalled-cycles-frontend OR idle-cycles-fronten     [Hardware event]alignment-faults                                   [Software event]bpf-output                                         [Software event]context-switches OR cs                             [Software event]cpu-clock                                          [Software event]cpu-migrations OR migrations                       [Software event]dummy                                              [Software event]emulation-faults                                   [Software event]major-faults                                       [Software event]minor-faults                                       [Software event]page-faults OR faults                              [Software event]task-clock                                         [Software event]L1-dcache-load-misses                              [Hardware cache event]L1-dcache-loads                                    [Hardware cache event]L1-dcache-store-misses                             [Hardware cache event]L1-dcache-stores                                   [Hardware cache event]L1-icache-load-misses                              [Hardware cache event]branch-load-misses                                 [Hardware cache event]branch-loads                                       [Hardware cache event]dTLB-load-misses                                   [Hardware cache event]dTLB-store-misses                                  [Hardware cache event]iTLB-load-misses                                   [Hardware cache event]armv7_cortex_a9/br_immed_retired/                  [Kernel PMU event]armv7_cortex_a9/br_mis_pred/                       [Kernel PMU event]armv7_cortex_a9/br_pred/                           [Kernel PMU event]armv7_cortex_a9/br_return_retired/                 [Kernel PMU event]armv7_cortex_a9/cid_write_retired/                 [Kernel PMU event]armv7_cortex_a9/cpu_cycles/                        [Kernel PMU event]armv7_cortex_a9/exc_return/                        [Kernel PMU event]armv7_cortex_a9/exc_taken/                         [Kernel PMU event]armv7_cortex_a9/inst_retired/                      [Kernel PMU event]armv7_cortex_a9/l1d_cache/                         [Kernel PMU event]armv7_cortex_a9/l1d_cache_refill/                  [Kernel PMU event]armv7_cortex_a9/l1d_tlb_refill/                    [Kernel PMU event]armv7_cortex_a9/l1i_cache_refill/                  [Kernel PMU event]armv7_cortex_a9/l1i_tlb_refill/                    [Kernel PMU event]armv7_cortex_a9/ld_retired/                        [Kernel PMU event]armv7_cortex_a9/pc_write_retired/                  [Kernel PMU event]armv7_cortex_a9/st_retired/                        [Kernel PMU event]armv7_cortex_a9/sw_incr/                           [Kernel PMU event]armv7_cortex_a9/unaligned_ldst_retired/            [Kernel PMU event]rNNN                                               [Raw hardware event descriptor]cpu/t1=v1[,t2=v2,t3 ...]/modifier                  [Raw hardware event descriptor]

解释:
HW event 这个是与硬件相关的事件

SW event 这个是跟内核相关的事件

HW cache event 这个是跟cache相关的

5.2 perfbench all

 perf中内置的benchmark,主要是对系统性能进行摸底,目前包括两套针对调度器和内存管理子系统的benchmark。
# Executed 1000000 pipe operations between two processesTotal time: 41.530 [sec]41.530077 usecs/op24078 ops/sec# Running mem/memcpy benchmark...
# function 'default' (Default memcpy() provided by glibc)
# Copying 1MB bytes ...1.511707 GB/sec# Running mem/memset benchmark...
# function 'default' (Default memset() provided by glibc)
# Copying 1MB bytes ...1.603551 GB/sec# Running futex/hash benchmark...
Run summary [PID 1217]: 2 threads, each operating on 1024 [private] futexes for 10 secs.Averaged 635903 operations/sec (+- 4.24%), total secs = 10# Running futex/wake benchmark...
Run summary [PID 1217]: blocking on 0 threads (at [private] futex 0x222574), waking up 1 at a time.
[Run 1]: Wokeup 0 of 0 threads in 0.0010 ms
[Run 2]: Wokeup 0 of 0 threads in 0.0020 ms
[Run 3]: Wokeup 0 of 0 threads in 0.0010 ms
[Run 4]: Wokeup 0 of 0 threads in 0.0020 ms
[Run 5]: Wokeup 0 of 0 threads in 0.0020 ms
[Run 6]: Wokeup 0 of 0 threads in 0.0010 ms
[Run 7]: Wokeup 0 of 0 threads in 0.0010 ms
[Run 8]: Wokeup 0 of 0 threads in 0.0020 ms
[Run 9]: Wokeup 0 of 0 threads in 0.0020 ms
[Run 10]: Wokeup 0 of 0 threads in 0.0010 ms
Wokeup 0 of 0 threads in 0.0015 ms (+-11.11%)
# Running futex/wake-parallel benchmark...
Run summary [PID 1217]: blocking on 2 threads (at [private] futex 0x22265c), 2 threads waking up 1 at a time.[Run 1]: Avg per-thread latency (waking 1/2 threads) in 0.0105 ms (+-14.29%)
[Run 2]: Avg per-thread latency (waking 1/2 threads) in 0.1815 ms (+-95.04%)
[Run 3]: Avg per-thread latency (waking 1/2 threads) in 0.0075 ms (+-6.67%)
[Run 4]: Avg per-thread latency (waking 1/2 threads) in 0.2210 ms (+-8.60%)
[Run 5]: Avg per-thread latency (waking 1/2 threads) in 0.0090 ms (+-11.11%)
[Run 6]: Avg per-thread latency (waking 1/2 threads) in 0.0755 ms (+-89.40%)
[Run 7]: Avg per-thread latency (waking 1/2 threads) in 0.0080 ms (+-12.50%)
[Run 8]: Avg per-thread latency (waking 1/2 threads) in 0.0870 ms (+-87.36%)
[Run 9]: Avg per-thread latency (waking 1/2 threads) in 0.1920 ms (+-95.31%)
[Run 10]: Avg per-thread latency (waking 1/2 threads) in 0.0090 ms (+-22.22%)
Avg per-thread latency (waking 1/2 threads) in 0.0801 ms (+-34.14%)# Running futex/requeue benchmark...
Run summary [PID 1217]: Requeuing 2 threads (from [private] 0x222764 to 0x222774), 1 at a time.[Run 1]: Requeued 2 of 2 threads in 0.0130 ms
[Run 2]: Requeued 2 of 2 threads in 0.0150 ms
[Run 3]: Requeued 2 of 2 threads in 0.0150 ms
[Run 4]: Requeued 2 of 2 threads in 0.0130 ms
[Run 5]: Requeued 2 of 2 threads in 0.0130 ms
[Run 6]: Requeued 2 of 2 threads in 0.0130 ms
[Run 7]: Requeued 2 of 2 threads in 0.0130 ms
[Run 8]: Requeued 2 of 2 threads in 0.0120 ms
[Run 9]: Requeued 2 of 2 threads in 0.0140 ms
[Run 10]: Requeued 2 of 2 threads in 0.0140 ms
Requeued 2 of 2 threads in 0.0135 ms (+-2.28%)# Running futex/lock-pi benchmark...
Run summary [PID 1217]: 2 threads doing pi lock/unlock pairing for 10 secs.Averaged 73 operations/sec (+- 0.00%), total secs = 10

5.3 perf top

可以实时查看当前系统进程函数占用率情况

PerfTop:      43 irqs/sec  kernel:100.0%  exact:  0.0% [4000Hz cpu-clock:pppH],  (all, 2 CPUs)
-------------------------------------------------------------------------------95.50%  [kernel]                               [k] arch_cpu_idle1.29%  [kernel]                               [k] read_current_ti0.58%  [kernel]                               [k] gt_read_long0.58%  [kernel]                               [k] _raw_spin_unloc0.48%  [kernel]                               [k] console_unlock0.45%  [kernel]                               [k] __timer_delay0.25%  [kernel]                               [k] _raw_spin_unlock_irqres0.09%  [kernel]                               [k] do_raw_spin_lock0.09%  doris (deleted)  pa:14004000~14725000  [.] 0x000cce080.07%  [kernel]                               [k] hrtimer_nanosleep0.07%  [kernel]                               [k] walk_stackframe0.07%  libc.so.6  pa:04f7b000~050a8000        [.] 0x00076abc0.05%  [kernel]                               [k] memset0.04%  [kernel]                               [k] __fget0.04%  doris (deleted)  pa:14004000~14725000  [.] 0x000539f80.04%  [kernel]                               [k] n_tty_poll0.04%  [kflow_videoprocess]                   [k] $a0.03%  [kernel]                               [k] _raw_write_unlock_irq0.03%  [kernel]                               [k] do_select0.03%  [kernel]                               [k] put_timespec64Mapped keys:[d]     display refresh delay.                  (2)[e]     display entries (lines).                (20)[f]     profile display filter (count).         (5)[F]     annotate display filter (percent).      (5%)[s]     annotate symbol.                        (NULL)[S]     stop annotation.[K]     hide kernel symbols.                    (no)[U]     hide user symbols.                      (no)[z]     toggle sample zeroing.                  (0)[qQ]    quit.Enter selection, or unmapped key to continue

第一行是CPU占用比 第二行是属性 第三行是运行的函数名 [k]是指内核空间 [.]是指用户空间

第一列:符号引发的性能事件的比例,指占用的cpu周期比例。

第二列:符号所在的DSO(Dynamic Shared Object),可以是应用程序、内核、动态链接库、模块。

第三列:DSO的类型。[.]表示此符号属于用户态的ELF文件,包括可执行文件与动态链接库;[k]表述此符号属于内核或模块。

第四列:符号名。有些符号不能解析为函数名,只能用地址表示。

除了上面的还有以下几个命令也被常常用到,在此不一一运行查阅了。

perf kmem针对slab子系统性能分析

perf kvm针对kvm虚拟化分析

perf lock分析锁性能

perf mem分析内存slab性能

perf sched分析内核调度器性能

perf trace记录系统调用轨迹

5.4 perfrecord与perf report

record会将数据保存到perf.data中。随后,可以使用perf report进行分析。

perf record和perf report可以更精确的分析一个应用,perf record可以精确到函数级别。并且在函数里面混合显示汇编语言和代码。

这里要注意如果是要分析应用程序,请务必不要strip并加上CFLAG+=-g

分析整个系统的

perf record -F 99 -ag --call-graph dwarf -o /mnt/mmc01/perf.data – sleep 30
perf report --stdio --no-children -g graph,0.5,callee -i perf.data > perfreport.txt

record 进行记录 然后report进行报告

报告截取部分如下:

65.10%  swapper          [kernel.kallsyms]                          [k] arch_cpu_idle|---arch_cpu_idledefault_idle_calldo_idlecpu_startup_entry|         |--36.12%--secondary_start_kernel|          0x10248c|         --28.97%--rest_initstart_kernel03.78%  ALG              [kernel.kallsyms]                          [k] _raw_spin_unlock_irqrestore|---_raw_spin_unlock_irqrestore|         --3.77%--grph_platform_spin_unlockgraph_enqueuekdrv_grph_triggergfx_dma_copy$aproc_ioctlproc_reg_unlocked_ioctlvfs_ioctldo_vfs_ioctlksys_ioctl__se_sys_ioctl__hyp_idmap_text_start2.25%  ctl_ipp_buf_tsk  [kernel.kallsyms]                          [k] _raw_spin_unlock_irqrestore|---_raw_spin_unlock_irqrestore|         --1.86%--vk_spin_unlock_irqrestore|         --0.51%--hwclock_get_longcounter1.39%  NMR_VdoTrig_D2D  [kernel.kallsyms]                          [k] _raw_spin_unlock_irqrestore|---_raw_spin_unlock_irqrestore|         --1.15%--vk_spin_unlock_irqrestore1.32%  kdrv_ise_proc_t  [kernel.kallsyms]                          [k] v7_dma_clean_range|---v7_dma_clean_rangefmem_dcache_syncvos_cpu_dcache_sync$akdrv_ise_job_process_llkdrv_ise_proc_taskkthreadret_from_fork
......

可以看到占用CPU最高的程序是什么,运行在哪一个函数了

解释:
perf record -F 99 -p 13204 -g – sleep 30
perf record表示记录,-F 99表示每秒99次,-p 13204是进程号,即对哪个进程进行分析,-g表示记录调用栈,sleep 30则是持续30秒

6、如何生成火焰图

必须使用相关的工具,该工具为FlameGraph,可以在https://github.com/brendangregg/FlameGraph上下载到该工具

6.1 recode数据

使用perf recode记录采样数据

6.2 解析recode的数据

perf script -i perf.data &> perf.unfold

6.3 将perf.unfold中的符号进行折叠

stackcollapse-perf.pl perf.unfold &> perf.folded

6.4 最后生成svg图

flamegraph.pl perf.folded > perf.svg
这样就能总览整个性能了
在这里插入图片描述

y 轴表示调用栈,每一层都是一个函数。调用栈越深,火焰就越高,顶部就是正在执行的函数,下方都是它的父函数。

x 轴表示抽样数,如果一个函数在 x 轴占据的宽度越宽,就表示它被抽到的次数多,即执行的时间长。注意,x 轴不代表时间,而是所有的调用栈合并后,按字母顺序排列的。

火焰图就是看顶层的哪个函数占据的宽度最大。只要有"平顶"(plateaus),就表示该函数可能存在性能问题。

颜色没有特殊含义,因为火焰图表示的是 CPU 的繁忙程度,所以一般选择暖色调。


http://chatgpt.dhexx.cn/article/VeiX6Ug7.shtml

相关文章

学会使用perf性能分析工具--这一篇就够了

在功能上,perf很强大,可以对众多的软硬件事件采样,还能采集出跟踪点(trace points)的信息(比如系统调用、TCP/IP事件和文件系统操作。perf的代码和Linux内核代码放在一起,是内核级的工具。perf是…

perf的基本使用方法

目录 前言perf的基本使用方法perf listperf statperf topperf record/perf reportperf -eperf probeperf schedperf sched mapperf sched scriptperf sched replay perf lockperf kmemperf timechartperf script 参考文档 前言 声明:本文主要参考几位老师的博文&…

Linux常用命令——ldconfig命令

在线Linux命令查询工具 ldconfig 动态链接库管理命令 补充说明 ldconfig命令的用途主要是在默认搜寻目录/lib和/usr/lib以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库(格式如lib*.so*),进而创建出动态装入程…

[JavaScript] 理解 e.clientX,e.clientY e.pageX e.pageY e.offsetX e.offsetY

event.clientX、event.clientY 鼠标相对于浏览器窗口可视区域的X,Y坐标(窗口坐标),可视区域不包括工具栏和滚动条。IE事件和标准事件都定义了这2个属性 event.pageX、event.pageY 类似于event.clientX、event.clientY&#xff…

clientX,clientY,offsetY,offsetX,screenX,screenY区分

在js中clientX,clientY,offsetX,offsetY,screenX,screenY都是鼠标事件的几个对象: 如下图可知: clientX:在鼠标事件发生时,鼠标相对于浏览器位置的X轴位置(…

pageX/pageY、clientX/clientY、screenX/screenY的区别

在触发鼠标事件的时候会传一个event参数,在这个参数中我们可以获得pageX/pageY、clientX/clientY、screenX/screenY,但是这几个属性有啥区别呢?看下图 screenX/screenY:其实就是你的鼠标指针距离你电脑屏幕左侧边缘和上侧边缘的距…

event事件坐标详解(clientx, offsetx, screenX等)

鼠标事件(ee||window.event) event.clientX、event.clientY (相对浏览器窗口) //鼠标相对于浏览器窗口可视区域的X,Y坐标(窗口坐标),可视区域不包括工具栏和滚动条。IE事件和标准事件都定义了这2个属性…

JS 中clientX clientY offsetX offsetY 等属性说明

JS中clientX clientY offsetX offsetY 等属性说明 示意图 event.clientX, event.clientY : 鼠标点击的位置距离 body 的大小event.screenX, event.screenY : 鼠标点击的位置距离 屏幕 的大小event.offsetX, event.offsetY 鼠标点击的位置距离 div盒子 的大小 没有滚动条的情…

clientX与offsetX、clientWidth与offsetWidth的区别

相关属性介绍 都要结合鼠标事件监听使用 1、client相关属性 获取鼠标位置 鼠标相对于视口的位置 e.clientX e.clientY (e是鼠标对象) 获取节点尺寸 节点.clientWidth; 节点.clientHeight;(返回值为数值) clientWidth cont…

图解事件坐标screenX、clientX、pageX, offsetX的区别

一、图解 如图所示,假设页面中灰色圆点是鼠标点击处,黄色区域是鼠标触发事件对象 二、概念 1、screenX 和screenY 参照点:电脑屏幕左上角screenX:鼠标点击位置相对于电脑屏幕左上角的水平偏移量screenY:鼠标点击位置…

clientX、pageX、screenX以及offsetX区别

clientX、pageX、clientX以及offsetX区别 client系列(不包含滚动条) clientX:鼠标相对于浏览器窗口可视区域x方向坐标 clientY:鼠标相对于浏览器窗口可视区域y方向坐标 page系列(包含滚动条) pageX&…

Client/Service

目录 客户--服务器方式 客户程序: 服务程序 TCP连接的建立采用客户服务器方式。主动发起连接建立的应用进程叫做客户(client),而被动等待连接建立的应用进程叫做服务器(server)。 客户--服务器方式 客户(client)和服务器(server)都是指通信中所涉及的两个应用进程。客户-服务…

js鼠标事件 clientX、clientY、offsetX、offsetY、layerX、layerY、pageX、 pageY、screenX、screenY

MouseEvent的类别有以下: mousedown 鼠标按下mouseup 鼠标释放click 左键单击dblclick 左键双击mousemove 鼠标移动mouseover 鼠标经过mouseout 鼠标滑出mouseenter 鼠标进入mouseleave 鼠标离开contextmenu 右键菜单 注意: 执行顺序:mous…

图解offsetX、offsetY和clientX、clientY和pageX、pageY和screenX、screenY的区别

offsetX、offsetY: 鼠标相对于事件源元素的X,Y坐标。比如说,我给黄色的盒子定义一个点击事件,则这个offset的坐标原点就在这个黄色盒子的左上角 clientX、clientY: 鼠标相对于浏览器窗口可视区域的X,Y坐标(窗口坐标)&…

clientX, clientY | offsetX, offsetY | pageX, pageY的区别:

clientX, clientY | offsetX, offsetY | pageX, pageY的区别: ✍ clientX, clientY:指的是鼠标点击位置距离可视窗口的坐标; 图示:在不拉滚动条的情况下 在拉滚动条的情况下: 还是点击位置距离可视窗口左上角的距离 …

clientX和clientY

####需求: 做项目中遇到一个这样的需求,点击属性菜单节点,出现显示框,显示框的位置随着鼠标点击的位置距离而改变。 ###教程: clientX 事件属性 http://www.runoob.com/jsref/event-clientx.html clientY 事件属性 h…

pageX和clientX的区别

目录 定义: 代码求证 结论: 定义: e.page:返回鼠标相对于文档页面的左上角的坐标(滚轮滑动时改变) page的大小页面滚动部分大小距离窗口可视区距离的大小(距离整个文档左上角距离的大小) e.client:返回鼠标相对于浏…

HTML 页面中的位置:clientX、screenX、offsetX、pageX

事件中鼠标的 (x,y) 位置 clientX 鼠标相对于浏览器左上角x轴的坐标; 不随滚动条滚动而改变;clientY 鼠标相对于浏览器左上角y轴的坐标; 不随滚动条滚动而改变;pageX 鼠标相对于浏览器左上角x轴的坐标; 随滚动条滚动而…

JavaScript 中一些概念理解 :clientX、clientY、offsetX、offsetY、screenX、screenY

clientX: 设置或获取鼠标指针位置相对于窗口客户区域的 x 坐标,其中客户区域不包括窗口自身的控件和滚动条。 clientY: 设置或获取鼠标指针位置相对于窗口客户区域的 y 坐标,其中客户区域不包括窗口自身的控件和滚动条。 offset…

JS之clientX,clientY,screenX,screenY,offsetX,offsetY区别测试

首先需要知道clientX,clientY,screenX,screenY,offsetX,offsetY 是鼠标事件对象下的几个属性. 之前也一直对这些属性搞的稀里糊涂,看文档上说的也是不太理解,反正看完一头雾水,所以自己动手亲自测试了一下;说一下自己的理解&…