程序员的自我修养读书笔记

1.进程直接使用物理内存的坏处:第一、地址空间不隔离,一个进程可能改写另一个进程的数据,从而导致系统崩溃。第二、内存使用效率低,频繁的数据换入换出,效率低。第三、程序运行地址不稳定,每次重新装载的空闲区域位置不确定。

2.虚拟地址和物理地址是为了隔离进程。

3.分段使用一段虚拟空间和物理空间的映射解决了地址空间隔离和程序运行地址稳定的问题。但是内存使用效率因为内存不足时换入换出到磁盘的还是整个程序,因此会有大量的磁盘访问操作,粒度比较大。程序在运行时候,往往只会访问部分数据,大部分数据是不会被频繁的使用的,因此将粒度更小化,局部性原理得到利用,就有了分页机制。

4.Linux中的缺页异常机制,其实这本质上是一种动态分配内存的机制,然后每个页有自己的属性,利用MMU来进行页映射。

5.线程是轻量级的进程,一个标准的线程是由线程ID、当前指令指针PC、寄存器集合和堆栈组成,线程共享程序的内存空间,包括代码段,数据段,堆等。

6.多线程的好处:多线程可以有效利用一个线程等待的时候去做其他的事情;长时任务,一个线程负责计算,一个线程负责交互;适应多核或者多cpu计算机;共享数据效率高。

7.线程私有数据为局部变量、TLS局部存储、函数参数,共享的为全局变量、堆数据、函数静态变量、程序代码和打开的文件。

8.线程的状态有运行、就绪和等待。

9.频繁计算的线程叫CPU密集型线程,频繁等待的线程叫I/O密集型线程,后者相对来说更加受欢迎。

10.fork和execl,写时复制机制,LDDR3讲的很清楚。

11.i++这个指令转化成汇编的时候不只是一条指令,因此有可能在执行到一半的时候被打断,单指令操作叫做原子操作。

12.临界区的作用范围仅限于本进程,其他的进程无法获取该锁。其余性质和互斥量基本相同。

13.一个函数可重入,表示这个函数没有执行完成,由于外部因素或者内部调用又一次进入函数执行,一个函数要被重入,有两种情况:第一、多个线程同时执行这个函数。第二、函数自身(可能是经过多层调用之后)调用自身。

14.可重入的特点。。。,保障并发安全。

15.volatile关键字可以制图组织过度优化,这个在很多寄存器操作中用到过。

16.linux内核代码中使用了众多的if语句,这里得到解释,原来是可以将lock的调用开销降低到最小,具体为什么,后期揣摩。

17.barrier指令是用于阻止cpu将该指令之前的指令交换到之后,linux内核源码中也见到过。

18.一对一的线程模型让多线程程序在多处理器的系统上具有更好的表现,但是内核限制内核线程的数量,一对一线程会让用户的线程数量受到限制,另外上下文切换开销较大,

19.多对一的线程模型可以拥有无上限的线程,上下文切换效率高,但是多处理器性能不会有明显提升,并且当一个阻塞,其余的都会阻塞。


1.gcc是后台程序的包装,不同的语言调用不用的预编译和编译工具。

2.重新计算各个目标地址的过程叫做重定位。

3.符号被用来表示表示一段子程序(函数)或者变量的起始地址。汇编语言中jmp等指令可以理解为符号?

4.预编译、编译、汇编和链接。

5.动态链接库和静态链接库都按照可执行文件进行存储。

6.程序源代码编译后的机器指令存放在代码段,.test或者.code,全局变量、局部静态变量存放在.data段,未初始化的全局变量、局部静态变量存放在.bss段(预留),目标文件的开头包含段属性,文件内的偏移地址,还有每个段的信息。

7.指令和数据分离的好处:第一、数据和指令的权限不同。第二、支持数据缓存和指令缓存。第三、节省内存。

8.readelf和obkdump工具的使用。

9.编译器的优化屏蔽了一些细节,对了解系统工作机制和调试有一定的影响。

10..bss段存放的是未初始化的全局变量和局部静态变量,.data存放了初始化的全局静态变量和局部静态变量。

11.有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,即其占用的存储单元不释放,在下一次该函数调用时,该变量保留上一次函数调用结束时的值。这时就应该指定该局部变量为静态局部变量(static local variable)。 把局部变量改变为静态变量后是改变了它的存储方式,即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。

12.马屁股决定航天飞机的宽度的故事,遗留问题。

13.链接的过程实际上是对地址的引用,即对函数和变量地址的引用,所谓符号就是函数和变量的名称,符号值就是他们的地址。可以使用nm来查看符号。

14.ELF文件中的符号表是一个段,名称为.symtab。

15.readelf –s xxxx.o查看符号在符号表中的状态。

16.使用ld来链接的时候,会自动定义一些特殊符号,例如程序起始地址,代码段结束地址,数据段结束地址等。

17.编译后的符号前后加上_是为了解决符号名冲突的问题,后来使用“签名机制”解决该问题。

18.extern “C”是表示编译器按照C规则进行编译。

19.强符号、弱符号,强引用和弱引用概念。

20.gcc –g添加调试信息。

21.链接器采用两步链接方法,即将各个目标文件中相同的段进行合并,再链接成一个大的目标文件。第一步:空间与地址分配(虚拟地址,注意BSS段)。第二步:符号解析与重定位。重定位过程最重要!

22.ld a.o b.o –e main –o ab,-e指定程序的入口为main函数,ld默认的为_start。

23.符号解析的确定是通过分配得各个段的起始虚拟地址加上偏移量来的,(操作系统中,偏移量的使用无处不在啊)

24.重定位表被用来保存与重定位相关的信息,.rel.text,也叫重定位段,链接器根据这个重定位符号。

25.符号的解析占据了链接过程的大部分。

26.undefined reference xxxx一般都是因为链接时候缺少了某个库,或者输入目标文件路径不正确或者符号的声明与定义不一致。

27.指令修正,绝对地址和相对地址,分别为S+A和S+A-P,绝对地址为该符号的实际地址,相对地址为与符号与被修正地址的地址差。

28.common块机制处理弱符号解析的问题。

29.API是源码级别的应用程序接口,例如POSIX。ABI是二进制文件层面的接口,规范更为严格。

30.静态库可以简单地看做一组目标文件的集合。

动态链接

31.GCC手册内嵌汇编知识

32.Ld连接器的语句分为连接语句和赋值语句。

33.BFD库将编译器和链接器与目标文件格式隔离开。

34.Runtimelib运行时动态库。

35.程序的局部性原理,将运行的部分装载。

36.覆盖载入在早期内存不够用的时候用的较多,现在已经很少使用,但是在DSP中也许还有用武之地。关键词:禁止树间调用和明确调用路劲。

37.为什么叫映像文件,因为可执行文件在装载的时候是被映射到虚拟地址空间的。

38.Linux缺页异常机制。do_page_fault(),建立可执行文件头文件和操作系统进程虚存之间的关系。当访问该内存时候,发现为空,则产生缺页异常。

39.对于相同权限的段,把他们合并到一起当做一个段进行映射可以有效地减少系统内存的碎片。

40.从链接角度,按照section划分,链接视角;从装载角度,按照segment划分,执行视角。

41.Linux装载过程,./可执行文件之后,工作台调用fork,然后fork调用execve,这时候工作台等待fork返回并接受用户输入的参数。:fork()->execve()->sys_execve()->do_execve()->search_binary_handler...。利用魔术确定文件的格式和类型.

42.静态链接的缺点:浪费内存和磁盘空间,因为每个一个进程都有自己用到的库的拷贝副本。第二是影响程序的跟新和发布,一个lib模块的更新,整个程序都要更新发布。

43.动态链接的优势:减少内存,减少物理页的换入换出,增加CPU缓存的命中率,升级容易,降低耦合率,增加程序的可扩展性和兼容性。缺点在于需要完善的共享库管理机制。linux下为.so,windows下为.dll

44.静态库为链接时重定位,动态库为装载时重定位。

45.地址无关代码技术。。不怎么看得懂。

46.如果一个lib.so中定义了一个全局变量a,进程A和进程B都使用了这个全局变量,一方的修改不会影响另一方,因为他们有各自的数据段副本,是数据段副本。。。但是同一个进程的两个线程,任何一边的修改另一个是会受到影响的。

47.通过延迟绑定优化动态链接的速度。ELF通过PLT来实现,具体实现函数为_dl_runtime_reslove。

48.动态链接器其实也是一个共享库,系统通过映射方式将其加载到进程的地址空间中,并将控制权交给他。动态链接库进行一些列链接之后,将控制权交给可执行文件的入口地址。

49..interp段指定动态链接器的路径。一般是一个软链接。.dynamic段保存了动态链接器所需要的各种信息,比如哪些共享对象,动态链接符号表的位置,动态链接重定位表的位置,共享对象初始化代码等。.symtab为静态链接符号表,动态链接符号表为.dynsym,其中之保存于动态链接相关的符号。231页

50.rel.dyn是动态库对数据引用的修正(数据段),.rel.plt是对函数引用的修正(代码段)。

51.elf.h包含了动态链接时候的辅助信息。

52.动态链接的步骤:启动动态链接器本身,装载所需要的共享对象,重定位和初始化。

53.动态链接器自举后,首先找到自己的GOT段,第一个存放的是.dynamic段,找到自己的重定位表和符号表等。

54.共享对象全局符号介入指的是在两个模块定义了同一个符号的时候,其中一个将另外一个覆盖的现象,即他们之间存在优先级的顺序。Linux的LD采用的是,如果即将加入的符号和已经存在的符号同名的话,就忽略即将加入的这个符号。

55.动态链接器本身是静态链接的。也是PIC的(地址无关代码)。

56.运行时加载,显示模块加载,linux驱动insmod和rmmod。

57.对动态库的操作分为四个函数:打开(dlopen),查找符号(dlsym),错误处理(dlerror),关闭动态库(dlclose)

58.Libname.x.y.z.so,x主版本号,y次版本号,z发布版本号

59.SO-NAME机制,去掉次版本号和发布版本号,建立软连接指向次版本号和发布版本号最新的库。

60.程序的运行环境包括,内存、运行库和系统调用。

61.栈一般分配在高地址内存区域,用来保存程序的上下文,它会向下生长。堆一般分配在低地址的内存区域,程序malloc和new出来的内存就在此分配,它是向上生长。

62.指针在初始化为NULL后,使用前一定要给他指向一个地址。栈上的指针也需要初始化好,不然随机指针会出现错误。

63.栈保存了:函数返回的地址和参数;临时变量包括局部的非静态的局部变量以及编译器生成的临时变量;保存的上下文包括函数调用前后需要保存不变的寄存器。

64.Ebp寄存器成为帧指针,固定在函数活动的位置,调用函数的时候首先将参数压入栈中,然后将下一条指令压入栈中,跳转到函数体执行。

65.函数的调用惯例:1.函数参数的传递方式和顺序,例如从左到右还有利用寄存器传递参数提高性能。2.栈的维护方式,主要是弹出的时候由栈本身完成还是有函数体自己完成。3.名字修饰策略。默认的在C语言中使用的是_cdec1。(318页)

31.eax用来传递函数的返回值。

32.堆空间由运行库先向操作系统批发,再向各个程序零售,当零售光了的时候运行库再向系统批发。运行库有维护堆空间的算法。分别为mmapbrk

33.在Linux2.6内核版本里,malloc可以分配的内存空间多达2.9G

34.Malloc申请的内存,在进程结束以后就不存在了,因为进程结束以后,进程的资源包括内存,打开的文件,网络接口等都会被操作系统回收。

35.堆的管理算法:简单的方法有:空闲链表,位图。针对固定分配大小的内存,可以使用对象池。一般会采用多种方法的综合。

36.Main函数之前会运行入口函数或者叫入口点,操作系统在创建进程后,将系统的的控制权交给了程序的入口,入口函数都程序运行环境和运行库进行初始化,包括堆、I/O、线程、全局变量等等。

37.入口函数初始化结束之后,调用Main函数,main函数执行结束返回到入口函数,然后回收资源,进行清理工作。

38.环境变量存储的是一些系统的公开信心,例如路劲,版本等。

39._start传给main的参数除了argcargv里面的参数意外,还包括initmain调用前的初始化工作;fini:main结束后的收尾工作。Rtld_fini:和动态加载有关的收尾工作。

40.其实main函数的退出也隐式的调用了exit函数,在exit后面还有个hlt是为了防止exit没有调用,强制的把进程给干掉。

41.程序的I/O包括文件、管道、信号、网络、命令等。

42.I/O对文件的操作,初始化主要包括:建立打开文件表,如果能够继承自父进程,那么从父进程获取继承的句柄,初始化标准输入输出。

43.C语言的运行库大致包含以下功能:1.启动与退出。2.标准函数。3.I/O4.堆的封装和实现。5.语言实现。6.调试功能

C语言的标准库由美国国家标准协会制定,最后一次扩充是1999年。。


66.(374)

67.线程私有数据包括:局部变量,函数的参数,线程局部存储的数据。线程共享的数据包括:全局变量,堆上的数据,函数里面的静态变量,程序代码和打开的文件。

68.printf/fprintf在被多线程共享使用时,也会造成混乱。C库当中一些函数做成可重入的。

69.可重入函数主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误。

70.由于glibc在malloc和new中支持了多线程,所以在使用前后不用加锁。但是一些例如memcopy,还是需要做保护。

71.缓冲机制的目的是为了避免减少系统调用或者读盘的次数,减少切换开销,framebuffer,还有等等都是这样。

72.linux下fread的实现:

73.linux下使用int 0x80来出发所有的系统调用。例如fork之后,会产生int 0x80的中断,然后去查找中断向量表,会发现是sys_call,这时候fork的系统调用好会存放在eax中,再去比对系统调用号的表,找到sys_fork,执行后返回到用户态。

74.ESP是栈指针寄存器,指向系统栈最上面一个栈的栈顶。

75.EBP是基址指针寄存器,指向系统栈最上面一个栈的底部。

76.EAX一般用来保存函数的返回值,EB/C/DX等一般用来传递参数。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值