1.Linux内核调试方法总结之coredump
2.C语言调试的调b调作用C语言调试器是如何工作的
3.要成为一名专业的程序员,从零开始需要怎么一步步来比较好,试多试多要把最底层的线程线程先学精通吗?(个人认为)求学长
4.2024年选Java还是选C++,其实很简单,源码最重要的调b调是要找到工作
5.LynxOS开发信息
6.麒麟V10 SP2操作系统GDB多线程调试失败
Linux内核调试方法总结之coredump
深入理解Linux内核调试:core dump的奥秘揭示Linux内核的强大调试能力离不开core dump这一重要工具。它像是试多试多hashmap源码使用教程一份程序崩溃时的现场记录,保存了关键的线程线程内存状态,包括CPU寄存器、源码调用栈等,调b调为追踪问题提供了宝贵的试多试多线索。Linux巧妙地通过信号机制(包括SIGINT、线程线程SIGQUIT等)来触发core dump的源码生成,处理这些信号时,调b调开发者可以选择生成core dump以进行后续分析。试多试多
核心转储的线程线程触发条件当程序遭遇内存越界、多线程并发中的错误、未保护的数据访问、非法指针或栈溢出等致命错误时,Linux会自动或在用户干预下生成core dump。要有效地捕获这些信息,你可以通过调整ulimit命令来配置core dump的行为,如设置生成大小、指定默认保存路径,以及在需要时更改保存格式。
深入分析core dump实战以一个经典的示例来说,当程序试图除以零时,会触发core dump。使用gdb,你可以通过执行"test core"命令查看到信号8(浮点数异常)及其对应的代码行。接着,利用bt -n命令,调用栈将清晰地展示错误发生的路径。深入探究时,disassemble命令会揭示出出错时的汇编代码,帮助我们理解问题的根源。
解析核心转储细节例如,当程序中执行了"movl $0x0, -0x8(%ebp)",试图将0保存到内存单元,这表明被除数是0,直接导致了除以零的错误。默认情况下,gdb使用AT&T汇编语言格式,但你可以通过"set disassembly-flavor intel"将其切换为更易理解的Intel格式。然后,list命令用于查看附近的源代码,前提是gdb能找到相关的源码文件,以便进行更精准的定位和修复。
总之,core dump是Linux内核调试中的重要环节,熟练掌握其操作和分析方法,能够显著提升问题定位和故障排除的效率。通过结合实际案例和细致的调试技巧,开发者可以更加深入地理解程序的Abstractlist源码运行状态,从而优化和提升代码质量。
C语言调试的作用C语言调试器是如何工作的
C语言调试的作用,C语言调试器是如何工作的很多人还不知道,现在让我们一起来看看吧!
C语言调试器是如何工作的
当你用GDB 的时候,可以看到它完全控制了应用程序进程。当你在程序运行的时候用 Ctrl + C,程序的运行就能够终止,而GDB能展示它的当前地址、堆栈跟踪信息之类的内容。你知道C语言调试器是如何工作的吗?下面是小编为大家带来的关于C语言调试器是如何工作的的知识,欢迎阅读。
但是它们怎么不工作呢?
开始,让我们先研究它怎样才会不工作。它不能通过阅读和分析程序的二进制信息来模拟程序的运行。它其实能做,而那应该能起作用(Valgrind 内存调试器就是这样工作的),但是这样的话会很慢。Valgrind会让程序慢倍,但是GDB不会。它的工作机制与Qemu虚拟机一样。
所以到底是怎么回事?黑魔法?……不,如果那样的话就太简单了。
另一种猜想?……?破解!是的,这里正是这样的。操作系统内核也提供了一些帮助。
首先,关于Linux的进程机制需要了解一件事:父进程可以获得子进程的附加信息,也能够ptrace它们。并且你可以猜到的是,调试器是被调试的进程的父进程(或者它会变成父进程,在Linux中进程可以将一个进程变为自己子进程:-))
Linux Ptrace API
Linux Ptrace API 允许一个(调试器)进程来获取低等级的其他(被调试的)进程的信息。特别的,这个调试器可以:
读写被调试进程的内存 :PTRACE_PEEKTEXT、PTRACE_PEEKUSER、PTRACE_POKE……
读写被调试进程的CPU寄存器 PTRACE_GETREGSET、PTRACE_SETREGS
因系统活动而被提醒:PTRACE_O_TRACEEXEC, PTRACE_O_TRACECLONE, PTRACE_O_EXITKILL, PTRACE_SYSCALL(你可以通过这些标识区分exec syscall、clone、exit以及其他系统调用)
控制它的执行:PTRACE_SINGLESTEP、PTRACE_KILL、PTRACE_INTERRUPT、PTRACE_CONT (注意,CPU在这里是单步执行)
修改它的信号处理:PTRACE_GETSIGINFO、PTRACE_SETSIGINFO
Ptrace是如何实现的?
Ptrace的实现不在本文讨论的范围内,所以我不想进一步讨论,只是简单地解释它是如何工作的(我不是内核专家,如果我说错了请一定指出来,并原谅我过分简化:-))
Ptrace 是Linux内核的一部分,所以它能够获取进程所有内核级信息:
读写数据?Linux有copy_to/from_user。
获取CPU寄存器?用copy_regset_to/from_user很轻松(这里没有什么复杂的,因为CPU寄存器在进程未被调度时保存在Linux的struct task_struct *调度结构中)。
修改信号处理?smartadmin源码更新域last_siginfo
单步执行?在处理器出发执行前,设置进程task结构的right flag(ARM、x)
Ptrace是在很多计划的操作中被Hooked(搜索 ptrace_event函数),所以它可以在被询问时(PTRACE_O_TRACEEXEC选项和与它相关的),向调试器发出一个SIGTRAP信号。
没有Ptrace的系统会怎么样呢?
这个解释超出了特定的Linux本地调试,但是对于大部分其他环境是合理的。要了解GDB在不同目标平台请求的内容,你可以看一下它在目标栈里面的操作。
在这个目标接口里,你可以看到所有C调试需要的高级操作:
struct target_ops
{
struct target_ops *beneath;
/* To the target under this one. */
const char *to_shortname;
/* Name this target type */
const char *to_longname;
/* Name for printing */
const char *to_doc;
/* Documentation. Does not include trailing
newline, and starts with a one-line descrip-
tion (probably similar to to_longname). */
void (*to_attach) (struct target_ops *ops, const char *, int);
void (*to_fetch_registers) (struct target_ops *, struct regcache *, int);
void (*to_store_registers) (struct target_ops *, struct regcache *, int);
int (*to__breakpoint) (struct target_ops *, struct gdbarch *,
struct bp_target_info *);
int (*to__watchpoint) (struct target_ops *,
CORE_ADDR, int, int, struct expression *);
}
普通的GDB调用这些函数,然后目标相关的组件再实现它们。(概念上)这是一个栈,或者一个金字塔:栈顶的是非常通用的,比如:
系统特定的Linux
本地或远程调试
调试方式特定的(ptrace、ttrace)
指令集特定的(Linux ARM、Linux x)
那个远程目标很有趣,因为它通过一个连接协议(TCP/IP、串行端口)把两台“电脑”间的执行栈分离开来。
那个远程的部分可以是运行在另一台Linux机器上的'gdbserver。但是它也可以是一个硬件调试端口的界面(JTAG) 或者一个虚拟的机器管理程序(比如 Qemu),并能够代替内核和ptrace的功能。那个远程根调试器会查询管理程序的结构,或者直接地查询处理器硬件寄存器来代替对OS内核结构的查询。
想要深层次学习这个远程协议,Embecosm 写了一篇一个关于不同信息的详细指南。Gdbserver的事件处理循环在这,而也可以在这里找到Qemu gdb-server stub 。
总结一下
我们能看到ptrace的API提供了这里所有底层机制被要求实现的调试器:
获取exec系统调用并从调用的地方阻止它执行
查询CPU的寄存器来获得处理器当前指令以及栈的地址
获取clone或fork事件来检测新线程
查看并改变数据地址读取并改变内存的变量
但是这就是一个调试器的全部工作吗?不,这只是那些非常低级的部分……它还会处理符号。这是,链接源程序和二进制文件。被忽视可能也是最重要的的一件事:断点!我会首先解释一下断点是如何工作的,因为这部分内容非常有趣且需要技巧,然后回到符号处理。
断点不是Ptrace API的一部分
就像我们之前看到的那样,断点不是ptrace API的一部分。但是我们可以改动内存并获取被调试的程序信号。你看不到其中的相关之处?这是因为断点的实现比较需要技巧并且还要一点hack!让我们来检验一下如何在一个指定的地址设置一个断点。
1、这个调试器读取(ptrace追踪)存在地址里的二进制指令,并保存在它自己的数据结构中。
2、它在这个位置写入一个不合法的指令。不管这个指令是啥,只要它是不合法的。
3、当被调试的程序运行到这个不合法的指令时(或者更准确地说,处理器将内存中的内容设置好时)它不会继续运行(因为它是不合法的)。
4、在现代多任务系统中,一个不合法的指令不会使整个系统崩溃掉,但是connectbot 源码会通过引发一个中断(或错误)把控制权交回给系统内核。
5、这个中断被Linux翻译成一个SIGTRAP信号,然后被发送到处理器……或者发给它的父进程,就像调试器希望的那样。
6、调试器获得信号并查看被调试的程序指令指针的值(换言之,是陷入 trap发生的地方)。如果这个IP地址是在断点列表中,那么就是一个调试器的断点(否则就是一个进程中的错误,只需要传过信号并让它崩溃)。
7、现在,那个被调试的程序已经停在了断点,调试器可以让用户来做任何他/她想要做的事,等待时机合适继续执行。
8、为了要继续执行,这个调试器需要 1、写入正确的指令来回到被调试的程序的内存; 2、单步执行(继续执行单个CPU指令,伴随着ptrace 单步执行); 3、把非法指令写回去(使得这个执行过程下一次可以再次停止) ;4、让这个执行正常运行
很整洁,是不是?作为一个旁观的评论,你可以注意到,如果不是所有线程同时停止的话这个算法是不会工作的(因为运行的线程可能会在合法的指令出现时传出断点)。我不会详细讨论GDB是如何解决这个问题的,但在这篇论文里已经说得很详细了:使用GDB不间断调试多线程程序。简要地说,他们把指令写到内存中的其他地方,然后把那个指令的指针指向那个地址并单步执行处理器。但是问题在于一些指令是和地址相关的,比如跳转和条件跳转……
处理符号和调试信息
现在,让我们回到信号和调试信息处理。我没有详细地学习这部分,所以只是大体地说一说。
首先,我们是否可以不使用调试信息和信号地址来调试呢?答案是可以。因为正如我们看到过的那样,所有的低级指令是对CPU寄存器和内存地址来操作的,不是源程序层面的信息。因此,这个到源程序的链接只是为了方便用户。没有调试信息的时候,你看程序的方式就像是处理器(和内核)看到的一样:二进制(汇编)指令和内存字节。GDB不需要进一步的信息来把二进制信息翻译成CPU指令:
(gdb) x/x $pc # heXadecimal representation
0xc: 0x 0x 0xf 0xfd
0xc: 0xa8ec 0x8b 0x8be 0x
0xc: 0x 0x
(gdb) x/i $pc # Instruction representation
=> 0xc: push %r
0xc: push %r
0xc: push %r
0xc: push %r
0xc: mov %rsi,%r
0xc6b: push %rbp
0xc6c: mov %edi,%ebp
0xc6e: push %rbx
0xc6f: sub $0x3a8,%rsp
0xc: mov (%rsi),%rdi
现在,如果我们加上调试信息,GDB能够把符号名称和地址配对:
(gdb) $pc
$1 = (void (*)()) 0xc
你可以通过 nm -a $file 来获取ELF二进制的符号列表:
nm -a /usr/lib/debug/usr/bin/ls.debug | grep " main"
c T main
GDB还会能够展示堆栈跟踪信息(稍后会详细说),但是只有感兴趣的那部分:
(gdb) where
#0 write ()
#1 0xde3 in _IO_new_file_write ()
#2 0xde4c in new_do_write ()
#3 _IO_new_do_write ()
#4 0xd in _IO_new_file_overflow ()
#5 0xbb in print_current_files ()
#6 0xb in main ()
我们现在有了PC地址和相应的函数,就是这样。在一个函数中,你将需要对着汇编来调试!
现在让我们加入调试信息:就是uartassist源码DWARF规范下的gcc -g选项。我不是特别熟悉这个规范,但我知道它提供的:
地址到代码行和行到地址的配对
数据类型的定义,包括typedef和structure
本地变量和函数参数以及它们的类型
$ dwarfdump /usr/lib/debug/usr/bin/ls.debug | grep ce4
0xce4 [, 0] NS
$ addr2line -e /usr/lib/debug/usr/bin/ls.debug 0xce4
/usr/src/debug/coreutils-8./src/ls.c:
试一试dwarfdump来查看二进制文件里嵌入的信息。addr2line也能用到这些信息:
很多源代码层的调试命令会依赖于这些信息,比如next命令,这会在下一行的地址设置一个断点,那个print命令会依赖于变量的类型来输出(char、int、float,而不是二进制或十六进制)。
最后总结
我们已经见过调试器内部的好多方面了,所以我只会最后说几点:
这个堆栈跟踪信息也是通过当前的帧是向上“解开(unwinded)”的($sp和$bp/#fp),每个堆栈帧处理一次。函数的名称和参数以及本地变量名可以在调试信息中找到。
监视点(<code>watchpoints)是通过处理器的帮助(如果有)实现的:在寄存器里标记哪些地址应该被监控,然后它会在那内存被读写的时候引发一个异常。如果不支持这项功能,或者你请求的断点超过了处理器所支持的……那么调试器就会回到“手动”监视:一个指令一个指令地执行这个程序,并检查是否当前的操作到达了一个监视点的地址。是的,这很慢!
反向调试也可以这样进行,记录每个操作的效果,并反向执行。
条件断点是正常的断点,除非在内部,调试器在将控制权交给用户前检查当前的情况。如果当前的情况不满足,程序将会默默地继续运行。
还可以玩gdb gdb,或者更好的(好多了)gdb --pid $(pid of gdb),因为把两个调试器放到同一个终端里是疯狂的:-)。还可以调试系统:
qemu-system-i -gdb tcp::
gdb --pid $(pidof qemu-system-i)
gdb /boot/vmlinuz --exec "target remote localhost:"
要成为一名专业的程序员,从零开始需要怎么一步步来比较好,要把最底层的先学精通吗?(个人认为)求学长
前言
你是否觉得自己从学校毕业的时候只做过小玩具一样的程序?走入职场后哪怕没有什么经验也可以把以下这些课外练习走一遍(朋友的抱怨:学校课程总是从理论出发,作业项目都看不出有什么实际作用,不如从工作中的需求出发)
建议:
不要乱买书,不要乱追新技术新名词,基础的东西经过很长时间积累而且还会在未来至少年通用。
回顾一下历史,看看历史上时间线上技术的发展,你才能明白明天会是什么样。
一定要动手,例子不管多么简单,建议至少自己手敲一遍看看是否理解了里头的细枝末节。
一定要学会思考,思考为什么要这样,而不是那样。还要举一反三地思考。
注:你也许会很奇怪为什么下面的东西很偏Unix/Linux,这是因为我觉得Windows下的编程可能会在未来很没有前途,原因如下:
现在的用户界面几乎被两个东西主宰了,1)Web,2)移动设备iOS或Android。Windows的图形界面不吃香了。
越来越多的企业在用成本低性能高的Linux和各种开源技术来构架其系统,Windows的成本太高了。
微软的东西变得太快了,很不持久,他们完全是在玩弄程序员。详情参见《Windows编程革命史》
所以,我个人认为以后的趋势是前端是Web+移动,后端是Linux+开源。开发这边基本上没Windows什么事。
启蒙入门
1、 学习一门脚本语言,例如Python/Ruby
可以让你摆脱对底层语言的恐惧感,脚本语言可以让你很快开发出能用得上的小程序。实践项目:
处理文本文件,或者csv (关键词 python csv, python open, python sys) 读一个本地文件,逐行处理(例如 word count,或者处理log)
遍历本地文件系统 (sys, os, path),例如写一个程序统计一个目录下所有文件大小并按各种条件排序并保存结果
跟数据库打交道 (python sqlite),写一个小脚本统计数据库里条目数量
学会用各种print之类简单粗暴的方式进行调试
学会用Google (phrase, domain, use reader to follow tech blogs)
为什么要学脚本语言,因为他们实在是太方便了,很多时候我们需要写点小工具或是脚本来帮我们解决问题,你就会发现正规的编程语言太难用了。
2、 用熟一种程序员的编辑器(不是IDE) 和一些基本工具
Vim / Emacs / Notepad++,学会如何配置代码补全,外观,外部命令等。
Source Insight (或 ctag)
使用这些东西不是为了Cool,而是这些编辑器在查看、修改代码/配置文章/日志会更快更有效率。
3、 熟悉Unix/Linux Shell和常见的命令行
如果你用windows,至少学会用虚拟机里的linux, vmware player是免费的,装个Ubuntu吧
一定要少用少用图形界面。
学会使用man来查看帮助
文件系统结构和基本操作 ls/chmod/chown/rm/find/ln/cat/mount/mkdir/tar/gzip …
学会使用一些文本操作命令 sed/awk/grep/tail/less/more …
学会使用一些管理命令 ps/top/lsof/netstat/kill/tcpdump/iptables/dd…
了解/etc目录下的各种配置文章,学会查看/var/log下的系统日志,以及/proc下的系统运行信息
了解正则表达式,使用正则表达式来查找文件。
对于程序员来说Unix/Linux比Windows简单多了。(参看我四年前CSDN的博文《其实Unix很简单》)学会使用Unix/Linux你会发现图形界面在某些时候实在是太难用了,相当地相当地降低工作效率。
4、 学习Web基础(HTML/CSS/JS) + 服务器端技术 (LAMP)
未来必然是Web的世界,学习WEB基础的最佳网站是W3School。
学习HTML基本语法
学习CSS如何选中HTML元素并应用一些基本样式(关键词:box model)
学会用 Firefox + Firebug 或 chrome 查看你觉得很炫的网页结构,并动态修改。
学习使用Javascript操纵HTML元件。理解DOM和动态网页(Dynamic HTML: The Definitive Reference, 3rd Edition - O'Reilly Media) 网上有免费的章节,足够用了。或参看 DOM 。
学会用 Firefox + Firebug 或 chrome 调试Javascript代码(设置断点,查看变量,性能,控制台等)
在一台机器上配置Apache 或 Nginx
学习PHP,让后台PHP和前台HTML进行数据交互,对服务器相应浏览器请求形成初步认识。实现一个表单提交和反显的功能。
把PHP连接本地或者远程数据库 MySQL(MySQL 和 SQL现学现用够了)
跟完一个名校的网络编程课程(例如:(升级版为Kyoto Cabinet)、Flare、MongoDB、CouchDB、Cassandra、Voldemort等。
年选Java还是选C++,其实很简单,最重要的是要找到工作
一、选择Java还是C++?
基于个人经验,如果你是学生且有大量空闲时间,建议你深入学习C++。C++被誉为程序员的“九阳神功”,其学习过程涵盖操作系统原理,这将为后续学习其他语言和机制奠定坚实基础。然而,如果你急于寻找工作,或对编程兴趣不大,只是为了谋生,优先选择Java,甚至可以背诵Java面试答案,或许能轻松找到一份不错的工作。
二、如何学习C++?
学习C++须结合操作系统原理,掌握汇编、编译链接与运行时体系、操作系统原理、多线程、网络编程等基础知识。C++//的语法是面试重点,推荐《深度探索C++对象模型》一书,专注于面向对象程序设计的底层机制。对于C++性能优化,推荐《提高C++性能的编程技术》一书。掌握C++语言后,深入学习常用惯用法和性能编码实践。
三、C++工程实践
学习C++语言本身和惯用法后,推荐《C++ API设计》和《大规模C++程序设计》两本书,分别从API接口设计和大型C++项目组织最佳实践角度出发,提供实际工程开发的指导。
四、C++必知必会知识
学习C++还需掌握汇编、编译链接与运行时体系、操作系统原理、多线程、网络编程等。对于汇编,推荐王爽老师的《汇编(第三版)》和韩宏老师的《老码识途 从机器码到框架的系统观逆向修炼之路》。对于操作系统原理,推荐《程序员的自我修养》和《Windows核心编程》。多线程知识则需掌握线程同步原语和多线程编程实践。
五、学习体系推荐
零声教育全网独家Linux C/C++开发课程体系,通过原理技术、源码分析、案例分析和项目实战,全面解析Linux C/C++。课程包含数据结构与算法、设计模式、C++新特性、Linux工程管理、gdb调试、高性能网络设计、基础组件设计、中间件开发、开源框架、云原生、性能分析、分布式架构和上线项目实战等模块,系统学习路径清晰,帮助你全面掌握C/C++技术栈。
LynxOS开发信息
LynxOS是一款开发平台,它支持多种操作系统,如Sun Solaris、SunOS以及原生或托管的LynxOS,适应于RS、x、k、PPC、microSPARC和PA-RISC等不同处理器。在实验板方面,PowerPC是一个主要的选择。 在编译器方面,Lynx开放开发环境集成了gcc和g++,同时也支持FORTRAN /、C++、Ada、Ada、Pascal和Modula-2等第三方编程语言。此外,Lynx开放开发环境还包含了如gdb、X/Motif等开发工具,以及Lynx PosixWorks环境,包括TotalView源级调试工具、TimeScan多线程性能分析工具和LynxInsure++的静态及实时软件分析工具,如内存泄露检测和测试覆盖分析。 对于网络支持,LynxOS兼容TCP/IP、NFS、LynxSNMP工具包,能与众多网络接口卡和设备无缝对接,同时也支持第三方协议和硬件。它遵循POSIX.1/.1b/.1c标准,以及Unix BSD 4.3标准,提供本地和交互式开发环境。 LynxOS的软件供应形式包括目标代码和源代码,主要使用的编程语言是C和汇编。它具备图形界面支持,如X-Windows和Motif等。其产品有效模块涵盖了浮点运算、通讯协议、缓存、网络支持、数学库以及文件系统等多个功能。在并发处理上,支持多线程调度策略,如固定优先级、轮转调度、时间片、动态优先级调整、dead line monotonic scheduling和FIFO等,还有优先级继承机制来防止优先级倒转。 技术规格上,系统内核大小在K到4M之间,内存支持从K到4G,最小进程和线程存储区分别为字节,消息最小存储区为字节。它具有个优先级级别,线程数量无上限,线程切换和进程切换时间在4us到us之间,最大中断潜伏期为us,系统时钟分辨率低至ns。LynxOS全面支持多进程、多线程和多处理器,配备MMU,但关于集成JAVA支持的信息未详,且支持自动代码生成工具和RMA功能。扩展资料
LynxOS是由Lynx实时系统公司开发的操作系统。LynxOS是一个分布式、嵌入式、可规模扩展的实时操作系统,它遵循POSIX.1a、POSIX.1b和POSIX.1c标准。它最早开发于年。麒麟V SP2操作系统GDB多线程调试失败
在FT/4芯片及麒麟V SP2操作系统下,使用GDB进行多线程调试时,遇到了错误。
经过网络搜索,发现可能导致此问题的原因有两种。首先,libthread_db-1.0.so和libpthread.so都源自glibc库,理论上不应存在版本不一致的情况。其次,在检查版本一致时,会从libpthread.so的符号表中获取nptl_version,即版本信息。若无法获取该信息,则可能引发上述问题。
可以通过以下命令查看libpthread.so.0中是否存在nptl_version信息。在出现问题的环境下,可能会得到无符合的结果。同时,也可以尝试查看nptl_version,但由于被stripped,因此无法看到结果。
使用file命令查看libpthread-2..so文件,可以发现其被stripped。strip的作用是在动态库中移除符号表,以减小动态库占用空间。显然,我们需要用到的符号也被清除了。
为了解决这个问题,我使用了glibc 2.版本,因此需要创建一个no stripped的libpthread-2..so。最简单的方式是下载glibc2.源码编译,然后提取所需的libpthread库。
1. 从官网下载glibc-2..tar.xz。
2. 解压tar xvf glibc-2..tar.xz。
3. 创建编译目录mkdir build。
4. 进入build目录,执行../configure --prefix=/opt/glibc-2.。
5. 配置后执行make完成编译,注意我们不需要安装。有编译错误也没有关系,只要build的nptl目录下完成libpthread动态库即可。完成编译后的库名为build/nptl/libpthread.so。通过file命令可看到它是no stripped。
接下来查看其版本,可以确认是2.版。
接下来让配置vim ~/.gdbinit加入以下语句,目的是在调用GDB时优先加载我们编译的libpthread.so。修改后,gdb进行多线程调试不再提示错误。
如果你有把握,也可以使用自己编译的libpthread.so替换环境中的对应库。