学习资料
win11搭建网络映射器:https://m.comcw.cn/jc/4614.html
音视频学习:https://github.com/0voice/audio_video_streaming
阿秀的学习笔记:https://interviewguide.cn/
音视频开发的学习资源:https://github.com/xhunmon/VABlog.git
解压密码:a123654
验证码:公众号获取
飞宇压缩包的解压密码:
hfy1234或wj1234或twj123
CoderUtil:https://www.coderutil.com/
C++:https://stibel.icu/md/guide/guide-overview.html
:http://purecpp.cn/ modern c++开源社区
https://planetscale.com/blog/category/engineering io设备可视化
CSDN:https://www.cnblogs.com/liuhanxu/p/13455554.html 密码:1024
电子书:https://juejin.cn/user/2858385965070952/posts
夏槽俊qt 零声教育
课百网:https://www.kebaiwan.com/
罗剑锋的C++实战笔记学习
文档
h265白皮书:https://www.itu.int/rec/T-REC-H.265-202407-I/en
c++网络库
https://github.com/PHZ76/xop.git
Git
https://learngitbranching.js.org/?locale=zh_CN

Git的三个区域
Git 的文件有三种状态区域:
| 区域 | 命令 | 说明 |
|---|---|---|
| 工作区 (Working Directory) | 你编辑的实际文件 | 改动还没 add |
| 暂存区 (Staging Area/index) | 用 git add 后进入这里 | 等待被 commit |
| 本地仓库 (Repository) | 用 git commit 后进入这里 | 已经记录在历史中 |
对应命令区别:
| 目标 | 用法 | 说明 |
|---|---|---|
❌ 撤销刚才的 git add(把文件从暂存区移回工作区) | git restore --staged <file> | ✅ 把文件移出暂存区,但保留修改内容 |
| ❌ 丢弃修改,回到上一次提交状态 | git restore <file> | ⚠️ 彻底丢失工作区改动 |
| ❌ 既撤回暂存区,又丢弃修改 | git restore --staged <file> + git restore <file> | 等价于完全回到上一次提交的版本 |
流程
初始化
- 在本地初始化仓库并提交
git init
- 将所有当前文件添加到暂存区
git add .
- 提交到本地仓库
git commit -m "Initial commit"
- 重命名本地默认分支为
main(可选,但现在比较常见)
git branch -M main
关联本地仓库与远程仓库
添加远程仓库地址
git remote add origin <远程仓库URL>
或
git remote add origin git@192.168.10.28:pilinghui/test_project.git
推送本地分支到远程
git push -u origin main
-u选项会把本地的main与远程origin/main关联起来,之后推送可以直接用git push。- 在推送之前先拉取 git pull origin main
拉取远程仓库并允许合并无关历史
- git pull origin main –allow-unrelated-histories
代码的合并
访问不了github
修改hosts文件:
- sudo vi /etc/hosts
- 在hosts文件末尾的ip地址如下:
#github
140.82.114.4 github.com
151.101.1.6 github.global.ssl.fastly.net
151.101.65.6 github.global.ssl.fastly.net
151.101.129.6 github.global.ssl.fastly.net
151.101.193.6 github.global.ssl.fastly.net
关掉hosts文件,重启网络服务
【ubuntu20】sudo systemctl restart systemd-networkd.service
【ubuntu18】 /etc/init.d/networking restart
git add
add 命令的作用就是将工作区的文件添加到暂存区
- -A 参数会监控工作区的状态树,它会把工作区的所有变化提交到暂存区,包括修改(modified)、新文件(Untracked files)、删除的文件(deleted)。git add . 等价于git add -A
- -u
--update只更新已经被Git跟踪的文件,即只添加那些已经存在于版本库中且被修改或删除的文件,但不包括新创建的文件(未被Git跟踪的文件)
git branch
-M 参数是
--move --force的简写,它会强制将当前分支重命名为main,即使目标分支main已经存在。通常用于以下情况:你想将当前分支重命名为
main,而不是创建一个新的分支。你正在遵循社区中越来越普遍的做法,将默认分支从
master更改为main。
git branch -M main
如果你当前在 master 分支上,这将把 master 分支重命名为 main。
如果你在其他分支上,这将把该分支重命名为 main。
-r
作用: 显示你本地仓库知道的所有 远程分支 的列表
输出格式:
meian@ubuntu200406:~/work/MyProject/test_nvr/nvr_36chn$ git branch -r origin/HEAD -> origin/main origin/develop origin/mainorigin/HEAD -> origin/main: 这表示远程仓库
origin的 HEAD 指针指向origin/main分支。HEAD 指针通常表示远程仓库的默认分支(通常是main或master)。
-a
- 作用会列出所有的分支,包括本地分支和远程分支。
–contains
过滤器 查询到包含特定commitID的所有本地分支,加上–all 还会显示远程分支。
删除分支
- -d
git branch -d 新分支名- -d 是安全删除,如果新分支上有未合并到 main 的提交,Git 会报错阻止删除,防止你丢掉工作。
假设你在当前分支上运行以下命令:
一句话黄金法则
修复 bug 的分支永远基于出现 bug 的来源分支来决定。
也就是说:
- 如果 auth-server 还没合入 develop bug 修复必须在 feature/plh/auth-server 上进行
- 如果 auth-server 已经部分合入 develop bug 修复通常在 fix/plh/auth-server-xxx(新建一个 fix 分支)
- 如果 bug 出在主分支 main(线上环境) 必须从 main 开 hotfix 分支
⭐ 场景 A:auth-server 正在开发,还未完成 → bug 修复应该继续在 feature 分支做
你现在是这种情况(重构,分阶段开发)。
例如:
feature/plh/auth-server ← 你正在这里开发
突然发现一个 bug
那就继续在这个分支上修即可:
git checkout feature/plh/auth-server
# 修复代码
git commit -m "fix(auth): 修复认证流程死锁"
git push
不需要新建分支。这是最干净、最合理的方式。
⭐ 场景 B:auth-server 已经合入 develop,但你还要继续修复 → 创建 fix 分支(推荐)
此时你已经执行过:
git checkout develop
git merge feature/plh/auth-server
git push
并且 auth-server 已经部分进入 develop。 此时如果发现 bug:
🔧 必须基于 develop 创建 fix 分支:
git checkout develop
git pull
git checkout -b fix/plh/auth-server-deadlock
修复:
git add .
git commit -m "fix(auth): 修复死锁问题"
git push
修复完成后:
git checkout develop
git merge fix/plh/auth-server-deadlock
git push
最后可删除 fix 分支:
git branch -d fix/plh/auth-server-deadlock
git push origin --delete fix/plh/auth-server-deadlock
⭐ 场景 C:auth-server 已经合入 main(正式发布版本)→ bug 必须 hotfix
假设你已经发版:
v1.0.0(包含 auth-server)
用户反馈 bug 了。
🚑 必须从 main 开热修复分支:
git checkout main
git pull
git checkout -b hotfix/plh/auth-server-token-bug
修复完后必须合两个地方:
1)合回 main(用于发 v1.0.1)
git checkout main
git merge hotfix/plh/auth-server-token-bug
git push
2)合回 develop(保持一致)
git checkout develop
git merge hotfix/plh/auth-server-token-bug
git push
最后删除 hotfix 分支。
git checkout
切换到现有的分支
git checkout <branch-name>
创建并切换到新分支
git checkout -b <new-branch-name>
从指定的 commit 中取出某个文件或目录,覆盖到当前分支的工作区和暂存区
git checkout <commit> -- <文件路径>
- 只想恢复某个/某几个文件到某版本 → git checkout
– - 想切换整个工作区到某分支 → git checkout
- 只是想临时看看某个历史 commit 整体长什么样、不打算改动 → 才用 git checkout
,并且事后记得 git checkout main 切回来
git cherry-pick
对于多分支的代码库,将代码从一个分支转移到另一个分支是常见需求。 这时分两种情况。一种情况是,你需要另一个分支的所有代码变动,那么就采用合并(git merge)。另一种情况是,你只需要部分代码变动(某几个提交),这时可以采用(cherry pick)。
git commit
提交空消息
git commit -m "" --allow-empty-message
git config
- 本地仓库配置 –local:仅对当前git仓库生效
- 全局用户配置 –global:当前操作系统下用户的所有git仓库都生效
当通过git status 看到一串令人困惑的八进制转义字符,例如:"\345\237\272\346\234\254\345\212\237",只要字节值超过0x80的字符都被视为unusual字符
关闭路径转义
git config --global core.quotepath false
设置 HTTP/HTTPS 代理
git config --global http.proxy http://127.0.0.1:7897
git config --global https.proxy http://127.0.0.1:7897
git clone
浅拷贝
git clone --depth=1 <rep_url>
--shallow-submodules= 子模块也用浅克隆
--recurse-submodules
- 读取
.gitmodules - 自动 clone 每个 submodule
- checkout 到指定 commit
git diff
在git pull 完成后,Git 会在 reflog 里留下一个记录点
HEAD@{1}= 执行pull之前的 HEADHEAD= 执行pull之后的 HEAD
查看单个文件的改动
- git diff HEAD@{1} HEAD – path/to/your/file.cpp
前提:你刚刚执行的就是一次普通 git pull(没有用 --rebase),且还没再提交或切换分支,否则 HEAD@{1} 可能不是刚才的 pull 点。
git fetch
git fetch [<remote>] [<branch>]
<remote>:远程仓库的名称,默认为origin。<branch>:指定要获取的远程分支。如果不指定分支,则会获取所有分支。
常见场景与实际例子:
获取远程仓库的所有更新:
git fetch origin
这条命令会从远程仓库origin获取所有的分支信息和标签的更新信息,并更新本地的远程追踪分支(如 origin/master)。注意,这并不会修改当前的工作分支或合并任何更改。
获取特定分支的更新:
git fetch origin feature-branch
这条命令只会从远程仓库 origin 获取 feature-branch 分支的更新信息,不会获取其他分支的内容。
参数:
–all
- 获取所有远程仓库的更新信息,而不仅仅是默认的远程仓库(
origin)。
- 获取所有远程仓库的更新信息,而不仅仅是默认的远程仓库(
–depth=
- 通过指定深度来获取一个浅拷贝。此选项会限制获取的历史记录的深度,适用于只关心最近提交的情况。
-p/--prune删除本地不存在的远程分支。当远程仓库删除了某个分支,而你本地的远程追踪分支仍然存在时,使用此选项可以同步删除本地不再存在的远程分支。
--tags- 获取远程仓库的所有标签信息。
git log
git log用于查询版本的历史,命令形式如下:
git log [<options>] [<since>..<until>] [[--] <path>...]
git merge
将两个分支的更改合并到一个分支中
git merge <branch-name>
- < branch-name > :指定要合并的目标分支。当前分支会与该目标分支合并。
命令的常用选项及参数:
–no-ff 选项:强制执行合并提交
用途:即使目标分支可以快进合并,使用
--no-ff选项仍然会生成一个新的合并提交。这个选项有助于保留合并的历史记录。即使目标分支没有修改,如果你使用
--no-ff选项,Git 也会创建一个新的合并提交。
git push
提交到远程仓库,本地的代码已经add ,commit了
关联远程仓库
git remote add origin https://github.com/你的用户名/muduo.gitgit push [<remote>] <branch><remote>:远程仓库的名称,默认通常为origin,表示你与远程仓库的连接。<branch>:本地分支的名称,表示将当前分支推送到远程的哪个分支。-u 等同 –set-upstream
把当前本地分支与刚推上的远端分支建立跟踪关系(upstream)。这样以后在该分支上可以直接用
git pull/git push不带任何参数,它会默认对应到那个远端分支;git status也会显示 ahead/behind。
如果执行git push -u origin main失败:
error: 源引用规格 main 没有匹配 error: 无法推送一些引用到 ‘origin’
git branch -m 命令用于重命名 Git 分支
重命名当前所在的分支
- 如果你在某个分支上,并且希望将其名称更改为新的名字,只需执行:git branch -m 新的分支名称
重命名指定的分支
- git branch -m 旧的分支名称 新的分支名称
error:
fatal: 拒绝合并无关的历史
通常会出现这种情况的原因有
本地仓库与远程仓库独立初始化: 你可能在本地创建了一个仓库并做了一些提交,而远程仓库中也有独立的提交记录,两者没有共同的起始点。
仓库历史被重写或重新初始化: 如果远程仓库经历了历史重写(例如使用了
git rebase或git init重新初始化),就可能导致两边的历史不再匹配。
git pull
拉取远程仓库中的最新提交,并自动将其合并到当前工作分支
git pull <remote> <branch>
<remote>:远程仓库的名称,通常默认为origin。<branch>:远程分支的名称,指定要拉取并合并的远程分支。如果不指定分支,git pull会默认拉取当前跟踪的远程分支。
常见场景与实际例子:
拉取远程仓库的更新并合并到当前分支:
git pull origin master
这条命令会从远程仓库 origin 的 master 分支拉取更新,并将其自动合并到当前分支。
拉取远程分支并合并到当前分支:
如果你当前在 develop 分支上,且远程仓库中有 develop 分支的更新,执行以下命令会拉取并合并这些更新:
git pull origin develop
这条命令会从远程仓库 origin 的 develop 分支拉取更新,并将其合并到本地的 develop 分支。
只拉取更新而不合并
如果你只是想获取远程仓库的更新但不进行合并,可以使用 --no-commit 选项:
git pull --no-commit origin master
这条命令会拉取远程仓库的更新并将其合并到当前分支,但不会立即提交更改,允许你在合并之前查看差异并做进一步调整。
强制合并
如果你不介意丢失本地更改,可以使用 git reset 来放弃本地更改并强制拉取远程仓库的内容
git reset --hard <commit>
<commit>是你要重置到的目标提交,可以是:- 提交的哈希值(如
a1b2c3d4) - 分支名(如
origin/main或master) HEAD~1(上一提交)HEAD(当前提交)
- 提交的哈希值(如
git reset --hard HEAD
git pull
遇到build目录里的文件没有跟踪,但这是是临时文件并且不需要保留,所以直接删除这些未跟踪的文件。
error: The following untracked working tree files would be overwritten by merge: build/CMakeFiles/CMakeDirectoryInformation.cmake build/CMakeFiles/Makefile.cmake build/compile_commands.json
git clean -fd删除未跟踪的文件
-f是强制删除未跟踪的文件。-d是删除未跟踪的目录。
在项目的根目录下创建.gitignore,并添加内容:build/
git remote
git remote add
git stash
用于临时保存当前工作目录和暂存区的改动,使得工作区能够恢复到上一次提交的干净状态。这对于在开发过程中需要切换分支但不想提交当前修改的情况非常有用。你可以将工作进度“存放”起来,稍后再回来继续工作。
用途:
- 切换分支时保存当前工作:当你在当前分支上有未提交的更改,并且需要临时切换到其他分支处理问题时,可以使用
git stash来保存这些更改。 - 避免提交不完整的工作:当你正在开发一个功能,途中遇到需要解决的紧急问题时,可以暂时将当前工作保存在 stash 中,待解决完紧急问题后再回来继续。
- 多任务处理:在一个任务的中间,切换到另一个任务时,你可以使用
git stash临时存储工作,以便稍后恢复。
git stash [<options>]
使用场景与实际例子
查看当前所有的 stash 条目
git stash list
这会列出所有的 stash 条目,每个条目都用一个标识符(例如 stash@{0})进行区分。
应用最近保存的 stash
git stash apply
应用特定的 stash
git stash apply stash@{1}
当你有多个 stash 条目时,可以指定特定的 stash 来应用。stash@{1} 指的是第二个保存的 stash。
常用选项:
- -u 或 –include-untracked
- 除了包含已跟踪文件的改动外,还会保存未跟踪的文件。
- -a 或 –all
- 除了未跟踪的文件外,还会将忽略文件(
.gitignore文件中列出的文件)保存到 stash 中。
- 除了未跟踪的文件外,还会将忽略文件(
- –keep-index
- 保存工作目录的改动,但不保存暂存区的内容。此选项会将暂存区的更改保持不变,仅保存工作目录的改动。
git tag
git tag [选项] [标签名称] [提交哈希]
-a . 附注标签(Annotated Tag)
-m 指定附注标签的注释信息。
常用的.gitignore
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
build/
bin/
objects/
.vscode/
如果不小心把 build 目录的内容提交到了 Git 仓库中,你可以按照下面的步骤将其移除,并让 Git 后续忽略该目录:
添加忽略规则
在项目根目录下的
.gitignore文件中添加一行,指定忽略 build 目录(如果没有该文件则新建一个)从 Git 暂存区移除 build 目录
使用下面的命令将 build 目录及其内容从 Git 的版本控制中移除,但本地文件不会被删除
git rm -r --cached build
- 提交更改
提交此次移除操作
git commit -m "Remove build directory from version control"
- 推送到远程仓库
如果你有远程仓库,还需要推送更新
git push
gdb调试
问题:
arm-mol-linux-uclibcgnueabihf-gdb: loadlocale.c:130: _nl_intern_locale_data: Assertion `cnt < (sizeof (_nl_value_type_LC_TIME) / sizeof (_nl_value_type_LC_TIME[0]))' failed.
Aborted (core dumped)
在运行GDB前临时修改locale环境变量,可以有效避免此问题:
export LC_ALL=C
export LANG=C
在启动 gdb 时传递参数
使用 --args 选项启动 gdb
gdb --args ./main 10001 /home/meian/work/MyProject/study/reator/reatorHttp/readme/
在 gdb 内部设置参数
启动 gdb 后,使用 set args 命令设置参数,然后运行
(gdb) set args 10001 /home/meian/work/MyProject/study/reator/reatorHttp/readme/
(gdb) run
gdbserver 使用方法
PC 运行 gdb
mips-linux-gnu-gdb [PC 端应用程序路径]
设置动态库搜索路径
应用程序往往动态链接 libc、ld 等库,当运行于动态库中 break 时(比如 memcpy)往往无法跟踪到当前 pc 所在位置。这是因为动态库的加载地址是不固定的。因此需要在 PC 端 gdb 上指定动态库加载路径。方法是
set solib-search-path \
/home/meian/work/toolChain/mips-gcc720-glibc229-r519/mips-linux-gnu/libc/lib:\
/home/meian/work/toolChain/mips-gcc720-glibc229-r519/mips-linux-gnu/lib:\
/home/meian/work/toolChain/mips-gcc720-uclibc229/mips-linux-uclibc/libc/lib:\
/home/meian/work/toolChain/mips-gcc720-uclibc229/mips-linux-uclibc/lib
--------------------------------------------
sdk的rootfs里的库
set sysroot /home/meian/work/SDK/JZ_SDK/ISVP-A1-1.6.1-20230911/software/sdk/Ingenic-SDK-A1-1.6.1-20230911/resource/rootfs/root-uclibc-toolchain720_229
set solib-search-path /home/meian/work/SDK/JZ_SDK/ISVP-A1-1.6.1-20230911/software/sdk/Ingenic-SDK-A1-1.6.1-20230911/resource/rootfs/root-uclibc-toolchain720_229/lib
---------------------------
set sysroot /home/meian/work/SDK/JZ_SDK/ISVP-A1-1.7.0-20250407/software/zh/board/Ingenic-SDK-A1-1.7.0-20250407-zh/resource/rootfs/root-uclibc-toolchain720_229
set solib-search-path \
/home/meian/work/SDK/JZ_SDK/ISVP-A1-1.7.0-20250407/software/zh/board/Ingenic-SDK-A1-1.7.0-20250407-zh/resource/rootfs/root-uclibc-toolchain720_229/lib
多个路径之间通过“:”来隔开。注:上述例子中仅指定了 toolchain 中 glibc 的libc 系列库和 libstdc++等主要外部库。
glibc 的动态库在 toolchain 文件夹下的相对目录为:mips-linux-gnu/libc/lib 与mips-linux-gnu/lib
uclibc 的动态库在 toolchain 文件夹下的相对目录为:mips-linux-uclibc/libc/lib 与 mips-linux-uclibc/lib
开发板启动 gdbserver
gdbserver [PC 机 IP:端口,与步骤 2 中端口需一致] [应用程序路径]
./gdbserver_a1n 192.168.10.27:1234 nvr_test
链接开发板
target remote [开发板 IP:端口]
GDB 找不到带调试信息的库
你要确保运行时使用的
/tmp/nfs/lib//libmafs.so与带有 debug 信息的版本是同一个文件,或者二者在不同路径导致 GDB 只看到运行时库,却找不到符号对应的调试信息库。可以在 GDB 中设置正确的搜索路径:
set solib-search-path ~/work/MyProject/nvr_nfs/test/ set auto-solib-add on
查看当前栈帧的变量
使用 info locals 查看当前函数的局部变量
(gdb) info locals
pts = 0
tmp = "\000\000\000\000\000\000\000"
u32SetLen = 4
frmLen = 0
frame = 0xe43cf8
__FUNCTION__ = "PutOneNetAFrameToBuffer"
systime = {year = 24 '\030', month = 11 '\v', mday = 28 '\034',
hour = 16 '\020', minute = 5 '\005', second = 57 '9',
timestamp = 1732809957, week = 4 '\004', reserve = 245 '\365'}
使用info frame查看当前栈帧的所有信息
(gdb) info frame
Stack level 2, frame at 0xa8685c38:
pc = 0x7783c in FrameData::InitData (./encode/include/BufferManage.h:148);
saved pc = 0x76b80
called by frame at 0xa8685cc0, caller of frame at 0xa8685c08
source language c++.
Arglist at 0xa8685c34, args: this=0xee9da0, datalen=7492
Locals at 0xa8685c34, Previous frame's sp is 0xa8685c38
Saved registers:
r11 at 0xa8685c30, lr at 0xa8685c34
p 打印变量
- 以16进制打印内容
p/x <variable>
x (examine memory)
功能:原始字节以十六进制打出来
- 起始地址(
spsData指针) - 要看的字节数(这里是 21)
(gdb) x/21xb spsData
0x7ffff79d7234: 0x67 0x64 0x00 0x28 0xac 0xd2 0x01 0xe0
0x7ffff79d723c: 0x08 0x9f 0x96 0x10 0x00 0x00 0x3e 0x80
0x7ffff79d7244: 0x00 0x0c 0x35 0x00 0x40
x examine memory
/21 取 21 个单元
x 以十六进制显示
b 每个单元大小是 1 byte
n (next)命令:
功能:执行下一行代码。如果当前行是一个函数调用,它会把整个函数当作一条语句执行完,不会进入函数内部逐行调试,而是直接执行完函数调用后停在下一行代码处。
s (step)命令:
功能:同样也是执行下一行代码,但如果当前行是一个函数调用,它会进入函数内部,开始逐行调试函数里的代码,方便查看函数内部具体的执行细节。
跳出循环until, /wach、
gdb 应用程序与源码不在一起:
**在 gdb 中添加源码搜索路径: **
(gdb) directory /tmp/source
验证源码路径是否正确: 你可以使用 info sources 查看 gdb 当前知道的源码文件列表,确保 /tmp/source 被包含其中
(gdb) info sources
查看socket对应的ip地址
2263 root 1:28 {IdleProcess} ./FocusAppMc6830v1
2278 root 0:00 [jpegenc]
2392 root 0:00 [kworker/0:2]
2393 root 0:00 -sh
2397 root 0:00 ps
~ # ls -l /proc/2263/fd
total 0
lrwx------ 1 root root 64 Jan 14 15:25 0 -> /dev/console
lrwx------ 1 root root 64 Jan 14 15:25 1 -> /dev/console
l-wx------ 1 root root 64 Jan 14 15:25 10 -> pipe:[3630]
lrwx------ 1 root root 64 Jan 14 15:25 11 -> /dev/sda1
lr-x------ 1 root root 64 Jan 14 15:25 12 -> /var/hi_system_config.xml
lr-x------ 1 root root 64 Jan 14 15:25 13 -> pipe:[923]
l-wx------ 1 root root 64 Jan 14 15:25 14 -> pipe:[923]
lr-x------ 1 root root 64 Jan 14 15:25 15 -> pipe:[924]
l-wx------ 1 root root 64 Jan 14 15:25 16 -> pipe:[924]
~ # cat /proc/net/tcp
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
0: 00000000:1F40 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 3637 1 c92c2280 100 0 0 10 0
1: 00000000:5BA0 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 2945 1 c92c0000 100 0 0 10 0
2: 00000000:1F48 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 929 1 c8a06780 100 0 0 10 0
3: 00000000:0050 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 2946 1 c92c05c0 100 0 0 10 0
4: 00000000:0017 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 393 1 c8a00000 100 0 0 10 0
5: DE01A8C0:9CCC C801A8C0:1F40 01 00000000:00000000 00:00000000 00000000 0 0 3633 2 c92c6d40 21 4 0 10 -1
6: 0100007F:5BA0 0100007F:A2D2 08 00000000:00000000 00:00000000 00000000 0 0 798 1 c92c0b80 21 4 28 10 -1
7: 0100007F:5BA0 0100007F:A31E 08 00000000:00000000 00:00000000 00000000 0 0 886 1 c92c5640 21 4 28 10 -1
8: 0100007F:5BA0 0100007F:A2BC 08 00000000:00000000 00:00000000 00000000 0 0 779 1 c8a04500 21 4 28 10 -1
9: 0100007F:5BA0 0100007F:A340 01 00000000:00000000 00:00000000 00000000 0 0 3632 1 c8a005c0 21 4 31 10 -1
gdb不处理信号
(gdb) handle SIGINT pass nostop noprint
handle SIGABRT stop print nopass
handle SIGSEGV stop print nopass
handle SIGILL stop print nopass
handle SIGBUS stop print nopass
pass—— 将 SIGINT 转发给被调试程序nostop—— 不在 GDB 层面停下来noprint—— 不在 GDB 层面打印任何信息
推荐启动模板
在~/.gdbinit 里配置通用的gdbinit
# ========== 基本体验 ==========
set pagination off
set confirm off
set print thread-events off
set disassemble-next-line on
# 关键:共享库符号未加载时也允许设置断点(变为 pending)
set breakpoint pending on
# ========== 信号策略 ==========
# Ctrl-C:你要让程序自己处理就用 pass nostop;想随时停住就改成 stop print nopass
handle SIGINT pass nostop noprint
# 致命信号:停住并打印,不交给程序(避免直接死掉)
handle SIGSEGV stop print nopass
handle SIGABRT stop print nopass
handle SIGBUS stop print nopass
handle SIGILL stop print nopass
handle SIGFPE stop print nopass
# ========== C++ 异常/终止路径 ==========
# 所有 throw(可能很吵,必要时再开)
# break __cxa_throw
# break __cxa_rethrow
# terminate/abort(强烈建议)
break __cxa_terminate
break std::terminate
break abort
# 这条在你环境里很可能有效(你曾命中过)
break std::terminate()@plt
# 可选:如果你的 libstdc++ 有 verbose handler
break __gnu_cxx::__verbose_terminate_handler
add-auto-load-safe-path /home/meian/work/MyProject/test_nvr/nvr_36chn
- set pagination off (关闭分页显示)
- 作用:当输出内容超过一屏时,GDB 默认会暂停并提示
--Type <RET> for more, q to quit, c to continue without paging--。
- 作用:当输出内容超过一屏时,GDB 默认会暂停并提示
- set print thread-events off (关闭线程事件打印)
- 作用:默认情况下,每当程序创建一个新线程或销毁一个线程时,GDB 都会在控制台打印一条消息(例如
[New Thread 0x... (LWP ...)]或[Thread ... exited])。
- 作用:默认情况下,每当程序创建一个新线程或销毁一个线程时,GDB 都会在控制台打印一条消息(例如
- set breakpoint pending on (开启待定断点)
- 作用:允许你对**尚未加载的共享库(.so)**中的函数设置断点。
- 为什么在嵌入式开发中是必须的:
- 动态加载库:你的项目引用了 ZLToolkit 和 P2P SDK。这些库通常是以动态库形式存在的。
- 先设置后运行:在程序刚启动但还没运行到加载这些
.so文件的代码时,如果你尝试设置断点,GDB 可能会报错说“找不到函数”。开启此选项后,GDB 会先记录下这个断点,等程序运行并加载了对应的库后,再自动激活断点。
- set disassemble-next-line on
- 让 GDB 在每次程序暂停(例如停在断点或单步执行后)时,自动显示即将执行的下一行源代码对应的汇编指令
在项目的.gdbinit
echo [loaded nvr_36 project .gdbinit]\n
# ========== sysroot ==========
set sysroot /home/meian/work/SDK/JZ_SDK/ISVP-A1-1.7.0-20250407/software/zh/board/Ingenic-SDK-A1-1.7.0-20250407-zh/resource/rootfs/root-uclibc-toolchain720_229
# ========== 搜索共享库 ==========
set solib-search-path \
/home/meian/work/SDK/JZ_SDK/ISVP-A1-1.7.0-20250407/software/zh/board/Ingenic-SDK-A1-1.7.0-20250407-zh/resource/rootfs/root-uclibc-toolchain720_229/lib:\
/home/meian/work/SDK/JZ_SDK/ISVP-A1-1.7.0-20250407/software/zh/board/Ingenic-SDK-A1-1.7.0-20250407-zh/resource/rootfs/root-uclibc-toolchain720_229/usr/lib
开启coredump
#确保内核中有开启CONFIG_COREDUMP,开启了这个目录就存在
ls /proc/sys/kernel/core_pattern
# 1. 打开 core
ulimit -c unlimited
# 2. 设置路径
echo "/tmp/core.%e.%p.%t" > /proc/sys/kernel/core_pattern
# 3. 运行程序
./nvr_app
内存泄露
asan
g++ -fsanitize=address -o test_asan memory.cpp -g
plh@plh-virtual-machine:~/work/MyProject/c++/plh/DataStructrues/1.linear_List$ ./test_asan
=================================================================
==12293==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000018 at pc 0x5578e1a712ff bp 0x7ffe0f40ead0 sp 0x7ffe0f40eac0
WRITE of size 4 at 0x602000000018 thread T0
#0 0x5578e1a712fe in func() /home/plh/work/MyProject/c++/plh/DataStructrues/1.linear_List/memory.cpp:6
#1 0x5578e1a71314 in main /home/plh/work/MyProject/c++/plh/DataStructrues/1.linear_List/memory.cpp:11
#2 0x7fdbb00f2082 in __libc_start_main ../csu/libc-start.c:308
#3 0x5578e1a711ad in _start (/home/plh/work/MyProject/c++/plh/DataStructrues/1.linear_List/test_asan+0x11ad)
0x602000000018 is located 4 bytes to the right of 4-byte region [0x602000000010,0x602000000014)
allocated by thread T0 here:
#0 0x7fdbb071b587 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cc:104
#1 0x5578e1a7127e in func() /home/plh/work/MyProject/c++/plh/DataStructrues/1.linear_List/memory.cpp:5
#2 0x5578e1a71314 in main /home/plh/work/MyProject/c++/plh/DataStructrues/1.linear_List/memory.cpp:11
#3 0x7fdbb00f2082 in __libc_start_main ../csu/libc-start.c:308
SUMMARY: AddressSanitizer: heap-buffer-overflow /home/plh/work/MyProject/c++/plh/DataStructrues/1.linear_List/memory.cpp:6 in func()
Shadow bytes around the buggy address:
0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c047fff8000: fa fa 04[fa]fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==12293==ABORTING