Linux | c&cpp | Email | github | QQ群:425043908 关注本站

itarticle.cc

01动态库so被覆盖导致coredump

一、为何cp覆盖进程的动态库(so)会导致coredump?

1.应用程序通过dlopen打开so的时候,kernel通过mmap把so加载到进程地址空间,对应于vma里的几个page.

2.在这个过程中loader会把so里面引用的外部符号例如malloc printf等解析成真正的虚存地址。

3.当so被cp覆盖时,确切地说是被trunc时,kernel会把so文件在虚拟内存的页清除掉。

4.当运行到so里面的代码时,因为虚拟内存的页已经清除掉了,这时会产生一次缺页中断。

5.缺页中断会导致Kernel从so文件中拷贝对应的页到内存中去:

a) 如果so里面依赖了外部符号,但是这时的外部符号并没有经过重新解析,当调用到时就产生segment fault

b) 如果需要的文件偏移大于新的so的地址范围,就会产生bus error.

02linux内核缺页中断处理

* 如果页表项页框存在标志为0,说明页框不存在,此时分为两种情况,第一种情况是页表项为 * 空,说明此页表项是第一次进行映射,并且还会分为是匿名映射还是文件映射。第二种情况是 * 页表项不为null,说明此页表项映射过页框,只不过被换到了磁盘,所以也分为匿名映射和文件 * 映射两种情况,其中匿名映射从swap区加载数据,文件映射从对应的文件加载数据

缺页中断处理一般流程:

1.硬件陷入内核,在堆栈中保存程序计数器,大多数当前指令的各种状态信息保存在特殊的cpu寄存器中。

2.启动一个汇编例程保存通用寄存器和其他易丢失信息,以免被操作系统破坏。

3.当操作系统发现缺页中断时,尝试发现需要哪个虚拟页面。通常一个硬件寄存器包含了这些信息,如果没有的话操作系统必须检索程序计数器,取出当前指令,分析当前指令正在做什么。

4.一旦知道了发生缺页中断的虚拟地址,操作系统会检查地址是否有效,并检查读写是否与保护权限一致,不过不一致,则向进程发一个信号或者杀死该进程。如果是有效地址并且没有保护错误发生则系统检查是否有空闲页框。如果没有,则执行页面置换算法淘汰页面。

5.如果选择的页框脏了,则将该页写回磁盘,并发生一次上下文切换,挂起产生缺页中断的进程让其他进程运行直到写入磁盘结束。且回写的页框必须标记为忙,以免其他原因被其他进程占用。

6.一旦页框干净后,操作系统查找所需页面在磁盘上的地址,通过磁盘操作将其装入,当页面被装入后,产生缺页中断的进程仍然被挂起,并且如果有其他可运行的用户进程,则选择另一用户进程运行。

7.当磁盘中断发生时,表明该页已经被装入,页表已经更新可以反映他的位置,页框也标记位正常状态。

8.恢复发生缺页中断指令以前的状态,程序计数器重新指向这条指令。

9.调度引发缺页中断的进程,操作系统返回调用他的汇编例程

10.该例程恢复寄存器和其他状态信息,返回到用户空间继续执行,就好像缺页中断没有发生过。

03VisualStudio代码格式化的几个方法

1)、选中需要格式化的代码,然后按Alt+F8

2)、选中需要格式化的代码,先按Ctrl+K 变成下面的状态,然后再按Ctrl+F

3)、选中代码,编辑->高级->设置选定内容的格式

04vim块操作:列删除、列插入

1、vim中块删除:

第一步:按下组合键“CTRL+v” 进入“可视 块”模式,选取这一列操作多少行

第二步:按下d 即可删除被选中的整块

2、vim中块插入

第一步:按下组合键“CTRL+v” 进入“可视 块”模式,选取这一列操作多少行

第二步:按下shift+i(或者大写的字母"i")

第三步:输入要插入的内容

第四步:按ESC,之后就会看到插入的效果。

05gcc 编译参数 -fPIC 的详解和一些问题

-fPIC 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code),则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。

06C语言函数调用栈(一)

程序的执行过程可看作连续的函数调用。当一个函数执行完毕时,程序要回到调用指令的下一条指令(紧接call指令)处继续执行。函数调用过程通常使用堆栈实现,每个用户态进程对应一个调用栈结构(call

stack)。编译器使用堆栈传递函数参数、保存返回地址、临时保存寄存器原有值(即函数调用的上下文)以备恢复以及存储本地局部变量。

不同处理器和编译器的堆栈布局、函数调用方法都可能不同,但堆栈的基本概念是一样的。

07C语言函数调用栈(二)

程序的执行过程可看作连续的函数调用。当一个函数执行完毕时,程序要回到调用指令的下一条指令(紧接call指令)处继续执行。函数调用过程通常使用堆栈实现,每个用户态进程对应一个调用栈结构(call

stack)。编译器使用堆栈传递函数参数、保存返回地址、临时保存寄存器原有值(即函数调用的上下文)以备恢复以及存储本地局部变量。

不同处理器和编译器的堆栈布局、函数调用方法都可能不同,但堆栈的基本概念是一样的。

08X86-64寄存器和栈帧

说到x86-64,总不免要说说AMD的牛逼,x86-64是x86系列中集大成者,继承了向后兼容的优良传统,最早由AMD公司提出,代号AMD64;正是由于能向后兼容,AMD公司打了一场漂亮翻身战。导致Intel不得不转而生产兼容AMD64的CPU。这是IT行业以弱胜强的经典战役。不过,大家为了名称延续性,更习惯称这种系统结构为x86-64。

X86-64在向后兼容的同时,更主要的是注入了全新的特性,特别的:x86-64有两种工作模式,32位OS既可以跑在传统模式中,把CPU当成i386来用;又可以跑在64位的兼容模式中,更加神奇的是,可以在32位的OS上跑64位的应用程序。有这种好事,用户肯定买账啦。

值得一提的是,X86-64开创了编译器的新纪元,在之前的时代里,Intel CPU的晶体管数量一直以摩尔定律在指数发展,各种新奇功能层出不穷,比如:条件数据传送指令cmovg,SSE指令等。但是GCC只能保守地假设目标机器的CPU是1985年的i386,额。。。这样编译出来的代码效率可想而知,虽然GCC额外提供了大量优化选项,但是这对应用程序开发者提出了很高的要求,会者寥寥。X86-64的出现,给GCC提供了一个绝好的机会,在新的x86-64机器上,放弃保守的假设,进而充分利用x86-64的各种特性,比如:在过程调用中,通过寄存器来传递参数,而不是传统的堆栈。又如:尽量使用条件传送指令,而不是控制跳转指令。

09Linux系统下x86和ARM的区别有哪些?

由于定位的不同,ARM处理器基于精简指令集(RISC)架构。指令集数量少就可以简化硬件逻辑的设计,减少晶体管数量,也就意味着低功耗。而且由于移动平台应用通常简单,程序的控制流不复杂,执行效率没有必要很高,所以流水线、分支预测等硬件逻辑都比较简单。这些都降低了晶体管总量。同时因为移动设备有电池的能源限制,ARM的电源管理是作为重要部分特别设计了的。比如移动设备的处理器在待机时通常只以极低的主频在运行,甚至可以暂时关闭闲置的核心、协处理器来降低功耗。

x86就截然不同。x86是复杂指令集(CISC)架构,存在很多机器指令,只为了高效地完成一项专门任务(比如MMX,

SSE中的指令)。这就使得硬件的逻辑很复杂,晶体管数量庞大。为了高效地进行运算,x86架构有较长的流水线以达到指令级并行(ILP)。长流水线带来的一个弊端,就是当遇到分支时,如果预载入分支指令不是未来真实的分支,那么要清空整个流水,代价较高。所以x86为此还必须有复杂的分支预测机构,确保流水线的效率。再加上多级cache,支持超线程、虚拟化等等,x86的复杂度其实相当高。

10gcc -D选项 编译时添加宏定义

使用 gcc -D选项 可以在编译时添加宏定义选择性的编译代码


例:gcc debugtest.c -o debugtest.exe -D DEBUG

即在编译时加入 DEBUG 宏

11gcc的编译参数-fPIC

non-PIC 与 PIC 在代码的区别主要在于 access global data, jump label 的不同。

比如一条 access global data 的指令,

non-PIC 的形式是:ld r3,var1

PIC 的形式则是:ld r3,var1-offset@GOT

意思是从 GOT 表的 index 为 var1-offset的地方处指示的地址处装载一个值,即var1-offset@GOT处的4个 byte 其实就是 var1的地址。这个地址只有在运行的时候才知道,是由 dynamic-loader(ld-linux.so)填进去的。


再比如 jump label 指令

non-PIC 的形式是:jump printf ,意思是调用 printf。

PIC 的形式则是:jump printf-offset@GOT,

意思是跳到 GOT 表的 index 为 printf-offset 的地方处指示的地址去执行,

这个地址处的代码摆放在 .plt section,


每个外部函数对应一段这样的代码,其功能是呼叫dynamic-loader(ld-linux.so)来查找函数的地址(本例中是

printf),然后将其地址写到 GOT 表的 index 为 printf-offset的地方,同时执行这个函数。这样,第2次呼叫 printf 的时候,就会直接跳到 printf 的地址,而不必再查找了。

12TCMalloc的使用与源码剖析(安装和使用)

TcMalloc是一个由Google开发的,比glibc的malloc更快的内存管理库。通常情况下ptmalloc2能在300ns执行一个malloc和free对,而TcMalloc能在50ns内执行一个malloc和free对。

TcMalloc可以减少多线程程序之间的锁争用问题,在小对象上能达到零争用。

TcMalloc为每个线程单独分配一个线程本地的Cache,少量的地址分配就直接从Cache中分配,并且定期做垃圾回收,将线程本地Cache中的空闲内存返回给全局控制堆。

TcMalloc认为小于(<=)32K为小对象,大对象直接从全局控制堆上以页(4K)为单位进行分配,也就是说大对象总是页对齐的。

TcMalloc中一个页可以存入一些相同大小的小对象,小对象从本地内存链表中分配,大对象从中心内存堆中分配。

我的名片

网名:丰果 | Ranger

职业:游戏开发

现居:上海市

Email:86668082@qq.com




站点信息

  • 建站时间:2016-04-01
  • 文章统计:728条
  • 文章评论:82条
  • QQ群二维码:扫描二维码,互相交流