重要性
作为一个程序员,软件开发工程师,编程是一个程序员最基础的工作,也是安身立命之本,基础却不简单,也可以说尤其重要,它可以不断巩固提升你的技术壁垒,在不断实现需求、解决问题中积累经验,让你明白程序的设计实现是如何影响实际应用的,让你对软件功能从编码实现到投入运行整个过程有更底层的、更清晰的认知。
技术难题
编程的重要性不言而喻,但编程过程中可能会遇到各种各样的问题,比如设计实现问题,内存泄漏问题、多线程死锁问题、性能调优问题等,如何解决这些问题,是一个程序员工作经历中的必修课,也是程序员充实自己、提升自己的必经之路,这些问题每个都值得单独拎出来讨论,由于篇幅限制,也担心不能面面俱到,这里只针对这其中的个别问题如何解决,在此简单讨论,由于本人也处于不断学习的过程当中,存在误解或描述不准的地方,还望谅解并给出意见,希望能共同交流学习。
解决方法
设计实现
当我们面对一个项目需求,如何通过编码的形式满足需求是实现问题,如何实现、如何更好的实现则是设计问题,由于本人编码经验有限,不敢妄谈设计,在此只是描述自己的一点浅陋认知;一旦涉及到设计,这就对程序员的编码经验,项目经历、认知有了一定的要求,如何做好设计实现,可以从以下几个方面考虑:
1)充分了解项目背景,分析并提炼项目需求。
2)对现有软件框架有一个充分的认知,并基于此考虑有几种实现方案
3)实现方案对比分析,满足需求是基础,此外,实现的复杂度,模块间是否高内聚、低耦合,说白了就是在满足功能实现的基础上尽可能降低模块间的依赖关系,前后版本的兼容性,性能、内存消耗等是衡量一个实现是否合理的重要依据,最终目的只有一个在多种方案中,寻找一个最优解,这个最优解不一定最好但一定是最适合的。
内存问题
内存问题是项目开发过程中时常会遇到但解决起来却比较棘手的问题,常见的内存问题有:内存越界,野指针,内存泄漏等,又可分为应用层进程导致的内存问题和内核层导致的内存问题,了解内存分配背后的原理对定位内存问题有一定帮助;在此举一个我曾遇到的一个内核拷贝越界导致设备挂死的问题。
问题描述:
在wifi驱动开发中,存在一个扫描的功能,扫描即通过捕获、解析空口中的管理报文,解析并获取无线信号的ssid(无线信号名)、bssid(无线mac地址)、带宽、发送速率、信号强度、信道等并将这些信息汇总,感知周围存在多少个信号,信号信息,从而判断是否存在恶意信号,是否存在干扰等。
在无线报文解析,拷贝ssid时存在如下语句:memcpy(rs->ssid, ssid, ssidlen);测试过程中设备挂死,通过串口可以看出打印如下死机堆栈:
在介绍定位过程之前,有必要先了解下Linux内存管理和分配的相关知识;
Linux内核中以页为单位维护并管理物理内存,以32位操作系统为例,页大小为4KB,但实际使用过程,通常申请的内存大小多为几十字节或几百字节,这必然会产生内存碎片并导致内存空间的浪费,为此Linux内核设计了Slab分配器。
Slab分配器的工作原理大致如下:预先申请一个或几个物理页大小的内存,作为slab内存池,每个slab维护多个相同大小的结构体对象,根据对象大小的不同,slab分为32字节、64字节、128字节等,具体可通过cat /proc/slabinfo查看slab内存池的分配及使用情况;
当申请内存时,根据申请内存的大小选择合适的slab内存池分配适当内存,因此对象数据也维护在slab内存区域中,slab中对象的数据分布有固定格式,具体如下:
slab的每个区域会填充固定的魔数,从确定对象的边界,同时提供一种防止并检测slab内存改写的保护机制,常见的魔数如下:
#define SLUB_RED_INACTIVE 0xbb
#define SLUB_RED_ACTIVE 0xcc
/* ...and for poisoning */
#define POISON_INUSE 0x5a /* for use-uninitialised poisoning */
#define POISON_FREE 0x6b /* for use-after-free poisoning */
#define POISON_END 0xa5 /* end-byte of poisoning */
各区域填充值如下:
red_left_pad和Red zone填充了SLUB_RED_INACTIVE(0xbb);
object填充了POISON_FREE(0x6b),但是最后一个byte填充POISON_END(0xa5)
padding在allocate_slab的时候就已经被填充POISON_INUSE(0x5a)
当区域的值被改写,根据改写区域的不同产生不同的问题,有如下几种:
回归BUG 堆栈信息,这里对应Redzone overwritten,发生了内存越界访问问题;下一步需要进一步分析log信息,在明确信息不多时要尽可能理解各个log信息的含义,如下所示:
根据以上信息,结合代码定位分析,由于篇幅问题,在此不再展开。
性能调优
性能调优是一个复杂的问题,涉及到各个方面,比较容易想到且常见的一般从两方面去考虑:1)内存管理 2)CPU利用率是否过高。
内存管理
通过free命令可以查看Linux 系统的使用情况,如下图所示(仅为free命令示例,非内存使用不足情况):
重点查看free、swap内存使用统计;
free表示当前剩余可用的内存空间;
swap是一种虚拟内存技术,用于在物理内不足时提供额外的内存空间。Swap空间通常是磁盘上的块专门分配的区域,用于存储暂时不活动的或不常用的内存数据。当物理内存不足时,系统可以将一些不活动的内存页面移动到swap空间,从而释放物理内存以供活动进程使用;swap虽然提供了一种内存不足时的备用方案,但实际使用中最好还是尽量避免频繁的使用swap,因为磁盘I/O相对缓慢。
当free数值趋近于0,且swap较大时,说明内存空间不足存在内存页大量换出到磁盘的操作,正常情况下设备的物理内存都是事先估计好留有使用空间的,此时,首先查看是否存在内存泄漏,
通过top命令,可以查看linux系统中各个进程的内存使用情况,shift + M使之以内存使用情况排序,从而可以看出是否存在内存使用率较高的进程;类似如下情况(仅为top命令示例,非MEM使用过高情况):
如果存在某个进程内存使用特别高,进一步的通过pmap -x pdi 或cat /proc/x/maps 可以查看进程的内存分布情况,若发现进程堆空间比较大且持续增长,则说明存在内存泄露问题,通过代码分析+gdb调试定位解决。
若未发生内存泄漏,则可能是新的功能或设计实现导致,则需要代码优化或人为增加物理内存。
CPU
cpu利用率代表cpu的使用情况、负载情况,当cpu利用率过高或将近100%时,说明cpu负载过重,可能影响设备性能。
top命令同样可以查看cpu使用率,以及通过shift+P 查看是否存在cpu使用率过高的进程,如下所示(仅为top命令示例,非cpu使用过高情况):
top命令后 按1可以看到各个cpu的使用情况,首先确认下cpu的负载是否均衡,若单个cpu过载,而另外的cpu比较空闲,则可通过线程绑核或设置cpu亲和性的方式,改变cpu负载不均的情况。
若cpu利用率均衡,且存在各个进程使用率过高,则通过perf命令进一步查看具体的cpu利用率使用过高的函数,有针对性的对代码进行优化。
其他
此外,缺页次数是否过高,是否频繁发生进程切换、缓冲大小等也会影响到性能问题,因此选择性的使用巨页,编码时刻意保持缓冲行对齐等对性能也会有一定的改善;总之性能是一个复杂的、综合性的问题,不能以偏概全,具体问题应具体分析。