调试厉器addr2line

这篇具有很好参考价值的文章主要介绍了调试厉器addr2line。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

addr2line: 将地址转换为文件名和行号的命令行工具

在C/C++程序的调试过程中,我们通常会使用调试器(如GDB)来定位崩溃或错误的位置。但有时候,我们可能只能获得程序崩溃时的地址,而没有调试器的支持。这时候,addr2line就可以帮助我们将地址转换为对应的源代码文件和行号。

addr2line的原理

在程序编译时,编译器通常会将源代码的调试信息嵌入到可执行文件中。这些调试信息包括每个函数的起始地址、每个源代码文件的文件名和行号等。当程序崩溃或遇到错误时,我们可以根据崩溃位置的地址,通过addr2line工具将其转换为对应的源代码文件和行号。

addr2line的工作原理如下:

  1. 读取可执行文件中的调试信息。

  2. 根据地址查找对应的函数。

  3. 根据函数的调试信息查找对应的源代码文件。

  4. 根据源代码文件的调试信息查找对应的行号。

  5. 输出文件名和行号。

安装

addr2line是GNU Binutils工具集中的一部分,可以在Linux和macOS系统中进行安装。在Ubuntu系统中,可以使用以下命令进行安装:

$ sudo apt-get install binutils

在macOS系统中,可以使用Homebrew包管理器进行安装:

$ brew install binutils

使用

addr2line将地址转换为文件名和行号。给定可执行文件中的地址或可重定位对象部分中的偏移量,它会使用调试信息来确定与之相关的文件名和行数。

通过addr2line的描述可知,在使用addr2line将地址转换为函数、文件名或行号时,其有两种使用方法:即对于可执行文件,addr2line后直接跟十六进制的地址值;对于可重定位对象文件,addr2line后直接跟十六进制的地址偏移量。从而实现正确输出需要的信息,否则会导致地址无法解析,输出??:0。

注:如何区分可执行文件与可重定位对象

#输出executable,可知文件是可执行文件
[root@localhost ~]# file test
test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=6bae72d43422a7a048ecf8bce55723aff816820b            , with debug_info, not stripped
#输出relocatable,可知文件是可重定位对象
[root@localhost ~]# file helloworld.ko
helloworld.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=2f5b2cbb443e05489dcf0272dbb0b72875172411, with debug_info, not stripped

基本用法

基本用法:addr2line [选项] [地址]
选项 描述
-j –section= 读取相对于段的偏移而非地址
-p –pretty-print 让输出对人类更可读
-s –basenames 去除目录名
-f –functions 显示函数名
-C –demangle[=style] 解码函数名
-h –help 显示本帮助
-a –addresses 显示地址
-b –target= 设置二进位文件格式
-e –exe= 设置输入文件名称(默认为 a.out)
-i –inlines 解开内联函数

程序奔溃场景

1、用户态普通程序
使用方法:addr2line -e 进程名 IP指令地址 -f

用户态程序有时可能因为各种原因导致崩溃,发生段错误,比如空指针等。如果没有靠谱的工具,我们就只能靠猜哪里的代码可能存在问题,这里通过Linux自带的addr2line工具调试程序,能够快速直接帮我们准确定位到文件、异常函数名以及行号。
example.c源文件:

#include <stdio.h>
int main()
{
       int *p = NULL;
       *p = 0;

       return 0;
}

使用gcc进行编译,如下:

[root@localhost 68]# gcc example.c -o example -g
[root@localhost 68]# ls
example  example.c
[root@localhost 68]# ./example
Segmentation fault (core dumped)

dmesg查看报错信息,如下:

[root@localhost ~]# dmesg
[134563.793925] example[53791]: segfault at 0 ip 0000000000400546 sp 00007fff7956af70 error 6 in example[400000+1000]
[134563.793946] Code: 01 5d c3 90 c3 66 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 f3 0f 1e fa eb 8a 55 48 89 e5 48 c7 45 f8 00 00 00 00 48 8b 45 f8 <c7> 00 00 00 00 00 b8 00 00 00 00 5d c3 66 2e 0f 1f 84 00 00 00 00
[root@localhost 68]# addr2line -e example 0000000000400546 -f
main
/tmp/68/example.c:5

该例子说明addr2line能够直观程序发生段错误的函数以及文件和行号。

:如果编译程序时没有加上-g参数(即程序不含调试信息),就只能显示出函数名,显示不出具体所在文件的位置,如下:

[root@localhost 68]# addr2line -e example 0x0000000000400546 -f
main
??:?

提示:如何区分程序是否还有调试信息,可通过file命令,如果输出结果中显示with debug_info则表示程序含有调试信息,否则表示不含有调试信息。

#不含有调试信息
[root@localhost 68]# file example
example: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=135f8128f721d829b9318da6105cc47223b14de9, not stripped
#含有调试信息
[root@localhost 68]# file example
example: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=c94635b3ef107e7ecc19f4c1a16e16f031d9ca51, with debug_info, not stripped
2、动态库程序

在动态库中发生段错误,也可以使用addr2line进行定位。

使用方法:addr2line -e 动态库名 IP指令地址-基地址 -f

test.c源文件:

#include "foo.h"

int main(void)
{
    foo();
    return 0;
}

foo.h:

#ifndef __FOO_LIB_H__
#define __FOO_LIB_H__

int foo(void);

#endif

foo.c:

#include "foo.h"

int foo()
{
    int *p = 0;
    *p = 0;
    return 0;
}

先编译动态库, 再编译主程序, 让它链接动态库, 最后运行之:

[root@localhost 69]# gcc -O3 -g -o libfoo.so -shared -fPIC foo.c
[root@localhost 69]# gcc -O3 -g -o test test.c -L. -lfoo 
[root@localhost 69]# export LD_LIBRARY_PATH=. 
[root@localhost 69]# ./test
Segmentation fault (core dumped)

查看dmesg日志,如下:

[root@localhost ~]# dmesg
[70567.416655] test[27722]: segfault at 0 ip 00007ffa1f588580 sp 00007fffa964e698 error 6 in libfoo.so[7ffa1f588000+1000]
[70567.427374] Code: ff e8 64 ff ff ff c6 05 bd 0a 20 00 01 5d c3 0f 1f 00 c3 0f 1f 80 00 00 00 00 f3 0f 1e fa e9 77 ff ff ff 0f 1f 80 00 00 00 00 <c7> 04 25 00 00 00 00 00 00 00 00 0f 0b 00 00 00 f3 0f 1e fa 48 83

根据日志可知,段错误发生的位置是在test进程调用的libfoo.so库里,我们先使用ldd找到动态库的位置,如下:

[root@localhost 69]# ldd test
        linux-vdso.so.1 (0x00007ffd15b24000)
        libfoo.so => ./libfoo.so (0x00007f8c01879000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f8c014b4000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f8c01a7b000)

00007ffa1f588580为程序崩溃点ip指令地址,使用dmesg消息中ip指令地址减去动态库基址值(00007ffa1f588580 -7ffa1f588000=580), 差值0x580为错误点在动态库的地址,调用addr2line, 注意-e参数后文件名改为动态库名:

[root@localhost 69]# addr2line -e libfoo.so 580 -f -p
foo at /tmp/69/foo.c:6

动态库基址值(模块载入地址)即dmesg消息libfoo.so[7ffa1f588000+1000]语句中的7ffa1f588000值。

3、内核模块程序
使用方法:addr2line -e xxx.ko 地址偏移量 -f

当内核模块程序异常时,可能会导致机器直接死机重启,这种情况下定位bug可能就比较麻烦,dmesg日志在机器重新启动后,新的日志会覆盖掉原来的报错日志,从而对定位内核异常问题造成麻烦。常见

提示:Linux内核错误

  1. panic 当内核遇到严重错误的时候,内核panic,立马崩溃。死机。
  2. Oops Oops是内核遇到错误时发出的提示“声音”,Oops有时候会触发panic,有时候不会,而是直接杀死当前进程,系统可以继续运行。

比如说内核态下的段错误,当内核设置了panic_on_oops=1的时候,Oops会触发panic。【panic_on_oops的值在内核编译的时候配置,可以在/proc/sys/kernel/panic_on_oops查看值,同时可以使用sysctl修改】

当panic_on_oops=0的时候,如果错误发生在中断上下文,Oops也会触发panic。如果错误只是发生在进程上下文,这个时候只需要kill当前进程。【中断上下文包括以下情况:硬中断、软中断、NMI】。Oops的时候内核还可以运行,只是可能不稳定,这个时候,内核会调用printk打印输出内核栈的信息和寄存器的信息。

本人所用主机即属于一旦发生Oops,就会触发panic,因此总是无法查看Oops时的dmesg日志,经查阅资料,发现是内核参数panic_on_oops的原因导致的,因为该参数被设置为1,所以Oops会触发panic,从而导致机器总是死机重启,无法查看Oops时的dmesg日志。下面提供两种方法修改Oops内核参数,使其不会在Oops的时候触发panic导致死机重启。

方法一:修改 /proc下内核参数文件内容,临时生效,重启后失效。

echo 0 > /proc/sys/kernel/panic_on_oops

方法二:修改/etc/sysctl.conf 文件的内核参数来永久更改。

[root@localhost ~]# vi /etc/sysctl.conf
[root@localhost ~]# cat /etc/sysctl.conf
# sysctl settings are defined through files in
# /usr/lib/sysctl.d/, /run/sysctl.d/, and /etc/sysctl.d/.
#
# Vendors settings live in /usr/lib/sysctl.d/.
# To override a whole file, create a new file with the same in
# /etc/sysctl.d/ and put new settings there. To override
# only specific settings, add a file with a lexically later
# name in /etc/sysctl.d/ and put new settings there.
#
# For more information, see sysctl.conf(5) and sysctl.d(5).
kernel.panic_on_oops=0
[root@localhost ~]# cat /proc/sys/kernel/panic_on_oops
1 
[root@localhost ~]# sysctl -p
kernel.panic_on_oops = 0
[root@localhost ~]#
[root@localhost ~]# cat /proc/sys/kernel/panic_on_oops
0

设置好Oops内核参数以后,让我们来构造生成Oops的程序,源代码如下:

oops.c源文件

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>

static noinline void do_oops(void)
{
    *(int *)0 = 0;
}

static int my_oops_init(void)
{
    pr_info("my_oops_init\n");
    do_oops();

    return 0;
}

static void my_oops_exit(void)
{
    pr_info("my_oops exit\n");
}

module_init(my_oops_init);
module_exit(my_oops_exit);
MODULE_DESCRIPTION ("Kernel OOPS Testing");
MODULE_LICENSE("GPL");

编译内核模块的Makefile文件

ifneq ($(KERNELRELEASE),)
EXTRA_CFLAGS = -Wall -g
obj-m := oops.o
else
PWD  := $(shell pwd)
KVER := $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
all:
        $(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
        rm -rf .*.cmd *.o *.order *.symvers *.mod.c *.ko .tmp_versions
endif

编译以及安装oops.ko模块,出现killed,由于错误未发生在中断上下文,未导致Kernel panic死机重启。

[root@localhost oops]# make
make -C /lib/modules/4.18.0-394.el8.x86_64/build M=/tmp/oops modules
make[1]: Entering directory '/usr/src/kernels/4.18.0-394.el8.x86_64'
  CC [M]  /tmp/oops/oops.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /tmp/oops/oops.mod.o
  LD [M]  /tmp/oops/oops.ko
make[1]: Leaving directory '/usr/src/kernels/4.18.0-394.el8.x86_64'
[root@localhost oops]#
[root@localhost oops]# ls
dump.log  Makefile  modules.order  Module.symvers  oops.c  oops.ko  oops.mod.c  oops.mod.o  oops.o
[root@localhost oops]# insmod ./oops.ko
Killed 
[root@localhost oops]# lsmod | grep oops
oops                   16384  1

加载模块将生成以下Kernel Oops消息,dmesg查看信息如下:

[root@localhost ~]# dmesg
[ 1039.918606] my_oops_init
[ 1039.918616] BUG: unable to handle kernel NULL pointer dereference at 0000000000000000
[ 1039.926442] PGD 0 P4D 0
[ 1039.928979] Oops: 0002 [#1] SMP NOPTI
[ 1039.932637] CPU: 34 PID: 3843 Comm: insmod Kdump: loaded Tainted: G           OE    --------- -  - 4.18.0-394.el8.x86_64 #1
[ 1039.943756] Hardware name: New H3C Technologies Co., Ltd. H3C UniServer R4950 G5/RS45M2C9SB, BIOS 5.37 09/30/2021
[ 1039.954000] RIP: 0010:do_oops+0x5/0x11 [oops]
[ 1039.958364] Code: Unable to access opcode bytes at RIP 0xffffffffc02e6fdb.
[ 1039.965231] RSP: 0018:ffffb9d40a8c7cb0 EFLAGS: 00010246
[ 1039.970449] RAX: 000000000000000c RBX: 0000000000000000 RCX: 0000000000000000
[ 1039.977573] RDX: 0000000000000000 RSI: ffff98942ee96758 RDI: ffff98942ee96758
[ 1039.984697] RBP: ffffffffc02e7011 R08: 0000000000000000 R09: c0000000ffff7fff
[ 1039.991822] R10: 0000000000000001 R11: ffffb9d40a8c7ad8 R12: ffffffffc02e9000
[ 1039.998944] R13: ffffffffc02e9018 R14: ffffffffc02e91d0 R15: 0000000000000000
[ 1040.006069] FS:  00007f1b8d93b740(0000) GS:ffff98942ee80000(0000) knlGS:0000000000000000
[ 1040.014145] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 1040.019884] CR2: ffffffffc02e6fdb CR3: 0000000145c02000 CR4: 0000000000350ee0
[ 1040.027008] Call Trace:
[ 1040.029454]  my_oops_init+0x16/0x19 [oops]
[ 1040.033550]  do_one_initcall+0x46/0x1d0
[ 1040.037390]  ? do_init_module+0x22/0x220
[ 1040.041318]  ? kmem_cache_alloc_trace+0x142/0x280
[ 1040.046023]  do_init_module+0x5a/0x220
[ 1040.049777]  load_module+0x14ba/0x17f0
[ 1040.053530]  ? __do_sys_finit_module+0xb1/0x110
[ 1040.058059]  __do_sys_finit_module+0xb1/0x110
[ 1040.062411]  do_syscall_64+0x5b/0x1a0
[ 1040.066077]  entry_SYSCALL_64_after_hwframe+0x65/0xca
[ 1040.071130] RIP: 0033:0x7f1b8c8509bd
[ 1040.074701] Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 9b 54 38 00 f7 d8 64 89 01 48
[ 1040.093446] RSP: 002b:00007ffc4df0a968 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 1040.101004] RAX: ffffffffffffffda RBX: 00005653fb1997d0 RCX: 00007f1b8c8509bd
[ 1040.108126] RDX: 0000000000000000 RSI: 00005653f980c8b6 RDI: 0000000000000003
[ 1040.115251] RBP: 00005653f980c8b6 R08: 0000000000000000 R09: 00007f1b8cbd9760
[ 1040.122375] R10: 0000000000000003 R11: 0000000000000246 R12: 0000000000000000
[ 1040.129498] R13: 00005653fb1997b0 R14: 0000000000000000 R15: 0000000000000000
[ 1040.136623] Modules linked in: oops(OE+) binfmt_misc xt_CHECKSUM ipt_MASQUERADE xt_conntrack ipt_REJECT nf_reject_ipv4 nft_compat nft_counter nft_chain_nat nf_nat nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 nf_tables nfnetlink rpcsec_gss_krb5 auth_rpcgss nfsv4 dns_resolver nfs lockd grace fscache bridge stp llc intel_rapl_msr intel_rapl_common amd64_edac_mod edac_mce_amd amd_energy kvm_amd kvm irqbypass ipmi_ssif pcspkr crct10dif_pclmul crc32_pclmul ghash_clmulni_intel rapl joydev ccp sp5100_tco i2c_piix4 k10temp ptdma acpi_ipmi ipmi_si sunrpc vfat fat xfs libcrc32c sd_mod t10_pi sg crc32c_intel ast drm_vram_helper drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops drm_ttm_helper ttm ahci drm libahci nfp(OE) igb libata dca i2c_algo_bit dm_mirror dm_region_hash dm_log dm_mod ipmi_devintf ipmi_msghandler
[ 1040.208357] CR2: 0000000000000000
[ 1040.211668] ---[ end trace b69c1e8998070273 ]---
[ 1040.230185] RIP: 0010:do_oops+0x5/0x11 [oops]
[ 1040.234540] Code: Unable to access opcode bytes at RIP 0xffffffffc02e6fdb.
[ 1040.241409] RSP: 0018:ffffb9d40a8c7cb0 EFLAGS: 00010246
[ 1040.246626] RAX: 000000000000000c RBX: 0000000000000000 RCX: 0000000000000000
[ 1040.253750] RDX: 0000000000000000 RSI: ffff98942ee96758 RDI: ffff98942ee96758
[ 1040.260876] RBP: ffffffffc02e7011 R08: 0000000000000000 R09: c0000000ffff7fff
[ 1040.267998] R10: 0000000000000001 R11: ffffb9d40a8c7ad8 R12: ffffffffc02e9000
[ 1040.275124] R13: ffffffffc02e9018 R14: ffffffffc02e91d0 R15: 0000000000000000
[ 1040.282247] FS:  00007f1b8d93b740(0000) GS:ffff98942ee80000(0000) knlGS:0000000000000000
[ 1040.290323] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 1040.296061] CR2: ffffffffc02e6fdb CR3: 0000000145c02000 CR4: 0000000000350ee0

消息解析:

[ 1039.928979] Oops: 0002 [#1] SMP NOPTI
[ 1039.932637] CPU: 34 PID: 3843 Comm: insmod Kdump: loaded Tainted: G           OE    --------- -  - 4.18.0-394.el8.x86_64 #1 

Oops: 0002 – 错误码

Oops: [#1] – Oops发生的次数

CPU: 34 – 表示Oops是发生在CPU34上

关键信息如下,这里提示在操作函数do_oops的时候出现异常,地址偏移量0x5:
[ 1039.954000] RIP: 0010:do_oops+0x5/0x11 [oops]

为什么这条信息关键,因为其含有指令指针RIP;指令指针IP/EIP/RIP的基本功能是指向要执行的下一条地址。在8080 8位微处理器上的寄存器名称是PC(program counter,程序计数器),从8086起,被称为IP(instruction pointer,指令指针)。主要区别在与PC指向正在执行的指令,而IP指向下一条指令。在64位模式下,指令指针是RIP寄存器。这个寄存器保存着下一条要执行的指令的64位地址偏移量。64位模式支持一种新的寻址模式,被称为RIP相对寻址。使用这个模式,有效地址的计算方式变为RIP(指向下一条指令)加上位移量。

由此可以看出内核执行到do_oops+0x5/0x11这个地址的时候出现异常,我们只需要找到这个地址对应的代码即可。

打印格式:do_oops+0x5/0x11 [oops] 即:symbol+offset/size [module] symbol: 符号 offset:地址偏移量 size:函数的长度 module: 所属内核模块

do_oops指示了是在do_oops函数中出现的异常, 0x5表示出错的地址偏移量, 0x11表示do_oops函数的大小。使用file查看内核模块文件类型:

[root@localhost oops]# file oops.ko
oops.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=4b5b58c4aaf63d65d338be17399bfd3c480504c3, with debug_info, not stripped

上述结果显示该内核模块文件为可重定位对象文件,因此使用addr2line直接跟十六进制的地址偏移量定位文件名、行号和行数,如下:

[root@localhost oops]# addr2line -e oops.ko 0x5 -f -p
do_oops at /tmp/oops/oops.c:7

可以看到异常代码在oops.c文件第7行,根据源代码,可知该行代码访问非法内存地址

注意事项

在使用addr2line时,需要注意以下几点:

  1. 可执行文件必须包含调试信息才能使用addr2line进行转换。如果编译时没有开启调试信息选项(如-g),则可能无法转换。

  2. 转换结果可能不一定准确。因为在优化编译时,编译器会对代码进行一些优化,可能会导致源代码和可执行代码之间存在一些差异。这时候,转换结果可能会不准确。

  3. 在转换地址时,要注意地址的字节序。如果程序是在大端模式或小端模式下编译的,而addr2line使用的字节序不同,则转换结果也会不正确。

总结

Addr2line 工具(它是标准的 GNU Binutils 中的一部分)是一个可以将指令的地址和可执行映像转换成文件名、函数名和源代码行数的工具。这在应用程序和内核程序执行过程中出现崩溃时,可用于快速定位出出错的位置,进而找出代码的bug。一般适用于 debug 版本或带有 symbol 信息的库。

参考文章来源地址https://www.toymoban.com/news/detail-487883.html

到了这里,关于调试厉器addr2line的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • linux怎么查看ip地址类型(查看静态ip、查看固定ip、查看动态ip)(ip addr show、nmcli dev show)

    如果网络接口已配置为静态IP地址,则在输出中会看到以下内容: 如果网络接口已配置为动态IP地址,则在输出中会看到以下内容: 如果网络接口已配置为静态IP地址,则在输出中会看到以下内容: 如果网络接口已配置为动态IP地址,则在输出中会看到以下内容: 请注意,这

    2024年02月12日
    浏览(74)
  • nacos服务注册地址配置(server-addr)failed to req API:/nacos/v1/ns/instance after all servers([xxxxxx])

    先贴报错 java.lang.IllegalStateException: failed to req API:/nacos/v1/ns/instance after all servers([http://xxxxxx.viphk.91tunnel.co]) tried: failed to req API:http://xxxxxx.viphk.91tunnel.co/nacos/v1/ns/instance. code:500 msg: java.net.UnknownHostException: xxxxxx.viphk.91tunnel.co 因为遇到上面的报错,所以才深究了一下nacos服务注册的

    2024年02月15日
    浏览(39)
  • Java支付SDK接口远程调试 - 支付宝沙箱环境【公网地址调试】

    转发自CSDN远程穿透的文章:Java支付宝沙箱环境支付,SDK接口远程调试【内网穿透】 Maven Spring boot Jdk 1.8 获取支付宝支付Java SDK,maven项目可以选择maven版本,普通java项目可以在GitHub下载,这里以maven为例 SDK下载地址:https://doc.open.alipay.com/docs/doc.htm?treeId=193articleId=103419docType=1 选择

    2023年04月18日
    浏览(52)
  • 串口调试助手 安卓版 附下载地址

    平时工作中和硬件同事对接的比较多,软件和硬件的通讯,串口用的也比较多的。在网上找了很多串口调试工具,大都年代久远,没有继续更新维护的了。 于是,自己抽空写了一个:串口调试助手。基于Android-SerialPort-Api修改, 最低支持安卓5.x, 支持armeabi-v7a, arm64-v8a, x86, x

    2024年02月05日
    浏览(43)
  • 支付宝SDK接口调试- cpolar内网穿透工具实现公网地址调试

    转发自cpolar内网穿透的文章:Java支付宝沙箱环境支付,SDK接口远程调试【内网穿透】 Maven Spring boot Jdk 1.8 获取支付宝支付Java SDK,maven项目可以选择maven版本,普通java项目可以在GitHub下载,这里以maven为例 SDK下载地址:https://doc.open.alipay.com/docs/doc.htm?treeId=193articleId=103419docType=1 选择

    2024年02月06日
    浏览(49)
  • 《操作系统》逻辑地址如何转换为物理地址

    (1)十六进制 逻辑地址=页号+页内地址 物理地址=块号+页内地址 (2)非十六进制 物理地址=块号*页内大小+页内地址 页号=逻辑地址/页面大小字节=(取整数) 页内地址=逻辑地址%页面大小字节=(取余数) (1)十六进制 例题 一分页存储管理系统中逻辑地址长度为16位,页面

    2024年02月04日
    浏览(48)
  • 【CSS3】CSS3 3D 转换 ③ ( 3D 透视视图 | translateZ 转换分析 | 网页调试工具调试 translateZ 属性值 | 代码示例 )

    \\\" 透视 \\\" 是 模拟人的眼镜 , 观察 物体 在 平面上的成像 , translateZ 转换 , 是 物体 沿着 Z 轴 移动 , 也就是下图中的 Z 距离 ; Z 轴的 Z 数值 是 物体 和 成像平面 之间的距离 , 默认为 0 ; 如果 Z 增加 , 说明 物体 越靠近 眼睛 , 在平面上成像范围更大 ; 如果 Z 减小 , 说明 物体 越远离

    2024年02月10日
    浏览(51)
  • Scala 如何调试隐式转换--隐式转换代码的显示展示

    在需要隐式转换的地方,把需要的参数显示的写出。 在terminal中 利用 scalac -Xprint:typer xxx.scala 方法打印添加了隐式值的代码示例。 对于复杂的工程来说,直接跑到terminal执行 scalac -Xprint:typer xxx.scala 会报很多库不存在的错误。 可在idea的设置中,找到如下配置,写入 -Xprint:ty

    2024年02月12日
    浏览(43)
  • NAT网络地址转换

    网络地址转换 NAT(Network Address Translation)是一种网络技术,用于将一个网络的IP地址转换为另一个网络的IP地址。 NAT技术的主要作用是将私有IP地址转换为公有IP地址,以便在互联网上进行通信。这样,私有网络中的计算机就可以使用单个公有IP地址与互联网通信,而不需要为

    2024年02月08日
    浏览(36)
  • 【NAT】网络地址转换

    NAT(Network Address Translation)又称为 网络地址转换 ,用于实现 私有网络和公有网络之间的互访 。 私有网络地址和公有网络地址 公有网络地址 (以下简称公网地址)是指在互联网上 全球唯一 的IP地址。2019年11月26日,是人类互联网时代值得纪念的一天,全球近43亿个IPv4地址已

    2024年01月17日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包