daily reading

来源:互联网 发布:mac pdf转word 扫描版 编辑:程序博客网 时间:2024/05/16 05:16

操作系统: 让CPU满载,设备驱动,内存管理,线程调度

内存直接存取,不足点:(地址空间不隔离,内存效率低--内存不够要经常换入换出, 程序运行地址不确定), 解决,用虚拟内存方式 ,CPU->虚拟地址->mmu->物理地址 ,(段页式内存模型)

线程: 程序执行流最小单元,cpu调度的最小单元.  运行-》就绪-》等待 (调度算法,轮转法为基础,加上优先级调度)

线程私有:线程局部存储(容量有限,__thread xxxx  在tls段,复制多个副本到堆里面保证独立),寄存器,栈(栈可以被同进程其他线程访问,但逻辑意义上讲是私有的)

fork:写时复制,仅要修改的时候复制一份给修改方。

mutex-binary semaphore: mutex 仅单个线程获取和释放 ,semaphor 时系统级别,可以一个线程获取,另一个线程释放。

原子操作:单指令的操作.


程序编译预处理:删除所有#define #if #eif ,删除所有注释,展开宏,展开包含头文件,添加行号,保留#pragma  gcc -E  

编译: 词法分析(产生tokern),语法分析(生产语法树),语义分析(程序语言的语法检查),生产汇编代码文件

汇编:产生机器码,生成ELF文件。(file命令可以查看格式)如:file a.o 
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped

链接: 链接机器码,负责地址和空间分配,符号决议,和重定位(重新计算目标操作地址的过程)。

链接过程的接口是依赖于各个源文件的符号,仅关心全局符号,将各个文件依赖的全局符号相互粘连。过程依赖 重定位表。

c符号 全局函数或者变量 _xxx  c++ _命名空间类名变量名 如:_ZN1C4funEi  c++filter 命令可以逆查看

函数和初始化的全局变量为强符号,未初始化的全局变量为弱符号。 1,强符号不能多次定义,否则ld报错, 2,强弱符号共存,用强符号,3,多个文件中有相同弱符号,选择空间最大那个。

函数级别链接: gcc -ffunction-sections -fdata-sections 链接某些库中的某些函数(链接过程消耗大)

链接过程:1,空间分配,扫描所有目标文件,收集所有段信息,合并相同的段,计算合并后的长度和位置信息,分配虚拟地址空间。2,。符号解析与重定位,利用收集到的信息,读取数据段和重定位信息,进行符号解析与重定位,调整代码中的地址。


编译后的ELF没有运行时主要由代码段(.text)和数据段(.data)组成rodata只读,bss段(.bss)  objdump -h xx.o 可以查看

ELF运行时主要由代码段(.text)和数据段(.data)组成,bss段(.bss) ,堆,栈,共享库(0x400000新0xbfxxxxx, .interp .dynamic ldd命令查看动态库依赖)等

 图8-1  ELF文件结构ELF头:包含文件版本,目标机器型号,程序入口地址, 段表信息等。 readelf -h xx.o 可以查看文件头

ELF linux运行时进程空间布局:

0xffffffff kernelspace#stack# (unused space) #dynamic libraries# (unused space)#heap#read/wirte sections(.data.bss)#readonly sections(.init.rodata.text) reserved 0x00000


在采用段式内存管理的架构中(比如intel的80x86系统),一个程序本质上都是由 bss段、data段、text段三个组成的

BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。

一般在初始化时bss 段部分将会清零(bss段属于静态内存分配,即程序一开始就将其清零了)BSS段属于静态内存分配。

数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。

代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)

栈(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

比如,在C语言程序编译完成之后,已初始化的全局变量保存在.data 段中,未初始化的全局变量保存在.bss 段中。   
l          text和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;
l          而bss段不在可执行文件中,由系统初始化。
 
编译两个小程序如下:
程序1:
int ar[30000];
void main()
{
    ......
}
 
程序2:
int ar[300000] =  {1, 2, 3, 4, 5, 6 };
void main()
{
    ......
}
    发现程序2编译之后所得的.exe文件比程序1的要大得多。 为什么?
区别很明显,一个位于.bss段,而另一个位于.data段,两者的区别在于:
l          全局的未初始化变量存在于.bss段中,具体体现为一个占位符;全局的已初始化变量存于.data段中;
l          而函数内的自动变量都在栈上分配空间。
l          .bss是不占用.exe文件空间的,其内容由操作系统初始化(清零);
l          而.data却需要占用,其内容由程序初始化,因此造成了上述情况。
 
注意:
l          bss段(未手动初始化的数据)并不给该段的数据分配空间,只是记录数据所需空间的大小。
l          data(已手动初始化的数据)段则为数据分配空间,数据保存在目标文件中。
l          DATA段包含经过初始化的全局变量以及它们的值。
l          BSS段的大小从可执行文件中得到,然后链接器得到这个大小的内存块,紧跟在数据段后面。当这个内存区进入程序的地址空间后全部清零。包含DATA和BSS段的整个区段此时通常称为数据区。

一些典型ELF段的名和内容解释如下表所示:

名称

内容

.text

机器指令, 常值数据和常值串.

.data

初始化数据.

.sdata

小初始化数据.

.bss

未初始化变量.

.sbss

小未初始化变量.

.comment

Comments from #ident directives in C.

.init

Main()函数之前执行的代码.

.fini

程序执行完成后执行的代码.

.eini

.fini代码的最后指令;.init,.fini和eini section应以序放入内存.

.debug

DWARF格式的符号调试信息.

.line

符号调试的行号信息.

.relaname

Section name的重定位信息.

.shstrtab

Section名.

.strtab

串表for符号表中的符号.

.symtab

包含符号表.


分段目的为了管理,如代码段只读, 数据段可读写,全局放一起,静态放一起,未初始化的一起,初始化的一起。(多个程序副本运行,系统可以只加载一份代码副本,多个数据副本,节约了内存)

段页内存管理:用于内存寻址,页面,程序运行过程的存储管理,换入换出。

不同进程相同线性地址对应不同物理地址:系统有个页目录 每个进程对应一个页表  页表里面有个基地址, 每个页表的基地址不一样 
两个进程访问相同的虚拟地址的时候  最终会映射到每个进程的基地址加上一个页内偏移(因为两个虚拟地址一样,偏移一样,但是基地址不一样,所以最终到物理地址十不一样的)

函数栈的调用惯例: 1 参数传递顺序(大都从右到左) 2栈维护(函数调用方弹出栈,还是函数本身)3名字修饰(cdecl _函数名 stdcall _函数名+参数字节数) 

堆:程序向运行库申请(避免系统调用开销),运行库向系统申请 brk 和mmap  

brk:data段

mmap:匿名空间 向操作系统申请的一段虚拟地址,没有映射到文件的。一般页大小整数倍

堆分配算法: 空闲链表 --容易越界,回收不知道大小,坏了无法恢复  。  位图:  简单,容错好,碎片多。   

operator new  (函数,可重载,返回void* ,参数size) ,  new operator (算符)


进程载入: os创建进程,控制器到入口函数(通常是运行库的某个入口函数),初始化运行库和环境(堆,IO,线程,全局变量构造(.init) ,调用main, main完后返回入口,进行资源清理--析构全局变量,清理堆,关闭IO等(.finl)

运行时库: 提供 1,入口函数和退出函数 2 Io封装 3标准函数库4堆 5 调试

IO缓冲: 行缓冲,全缓冲

linux 进入和退出系统调用:  

1. 执行int $0x80汇编指令 .  iret退出

2. 执行sysenter汇编指令.  2.6以后内核开始支持,   sysexit退出


信号有2个阶段 :

1,信号产生,内核修改目标进程的信号数据结构。

2,信号传递,内核强迫目标进程对信号做出反应,或改变目标进程的执行状态 或执行信号调用,或者2者都有。


共 享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。采用共享内存通信的一个显而易 见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。(对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而 共享内存则只拷贝两次数据[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。)另一方面:进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。持续性

一、内核怎样保证各个进程寻址到同一个共享内存区域的内存页面

1、page cache及swap cache中页面的区分:一个被访问文件的物理页面都驻留在page cache或swap cache中,一个页面的所有信息由struct page来描述。struct page中有一个域为指针mapping ,它指向一个struct address_space类型结构。page cache或swap cache中的所有页面就是根据address_space结构以及一个偏移量来区分的。

2、文件与address_space结构的对应:一个具体的文件在打开后,内核会在内存中为之建立一个struct inode结构,其中的i_mapping域指向一个address_space结构。这样,一个文件就对应一个address_space结构,一个address_space与一个偏移量能够确定一个page cache 或swap cache中的一个页面。因此,当要寻址某个数据时,很容易根据给定的文件及数据在文件内的偏移量而找到相应的页面。

3、进程调用mmap()时,只是在进程空间内新增了一块相应大小的缓冲区,并设置了相应的访问标识,但并没有建立进程空间到物理页面的映射。因此,第一次访问该空间时,会引发一个缺页异常。

4、对于共享内存映射情况,缺页异常处理程序首先在swap cache中寻找目标页(符合address_space以及偏移量的物理页),如果找到,则直接返回地址;如果没有找到,则判断该页是否在交换区(swap area),如果在,则执行一个换入操作;如果上述两种情况都不满足,处理程序将分配新的物理页面,并把它插入到page cache中。进程最终将更新进程页表。
注:对于映射普通文件情况(非共享映射),缺页异常处理程序首先会在page cache中根据address_space以及数据偏移量寻找相应的页面。如果没有找到,则说明文件数据还没有读入内存,处理程序会从磁盘读入相应的页面,并返回相应地址,同时,进程页表也会更新。

5、所有进程在映射同一个共享内存区域时,情况都一样,在建立线性地址与物理地址之间的映射之后,不论进程各自的返回地址如何,实际访问的必然是同一个共享内存区域对应的物理页面。
注:一个共享内存区域可以看作是特殊文件系统shm中的一个文件,shm的安装点在交换区上。       


共享内存是系统出于多个进程之间通讯的考虑,而预留的的一块内存区。在/proc/sys/kernel/目录下,记录着共享内存的一些限制,如一个共享内存区的最大字节数shmmax,系统范围内最大共享内存区标识符数shmmni等,可以手工对其调整,但不推荐这样

做。shmat()将这个内存区映射到本进程的虚拟地址空间。(堆和栈中间,称为文件映射区域的地方),下面看出靠近栈

mmap

rea:0x7f5d876e5000  mmp
areast:0x7fffc6df32dc  stack
areahp:0x100c010  heap
next
area:0x7f5d876e3000  shmat
areast:0x7fffc6df3318  stack
areahp:0x100c030 heap

注意:在使用共享内存,结束程序退出后。如果你没在程序中用shmctl()删除共享内存的话,一定要在命令行下用ipcrm命令删除这块共享内存。你要是不管的话,它就一直在那儿放着了。创建共享内存时,shmflg参数至少需要 IPC_CREAT | 权限标识,如果只有IPC_CREAT 则申请的地址都是k=0xffffffff,不能使用;
获取已创建的共享内存时,shmflg不要用IPC_CREAT(只能用创建共享内存时的权限标识,如0640),否则在某些情况下,比如用ipcrm删除共享内存后,用该函数并用IPC_CREAT参数获取一次共享内存(当然,获取失败),则即使再次创建共享内存也不能成功,此时必须更改key来重建共享内存。
简单解释一下ipcs命令和ipcrm命令。

取得ipc信息:
ipcs [-m|-q|-s]
-m      输出有关共享内存(shared memory)的信息
-q      输出有关信息队列(message queue)的信息
-s      输出有关“遮断器”(semaphore)的信息
%ipcs -m

gogo@localhost Net]$ ipcs  -l   查看限制  cat /proc/sys/kernel/shmmax 32M


------ Shared Memory Limits --------
max number of segments = 4096
max seg size (kbytes) = 67108864
max total shared memory (kbytes) = 17179869184
min seg size (bytes) = 1


------ Semaphore Limits --------
max number of arrays = 128
max semaphores per array = 250
max semaphores system wide = 32000
max ops per semop call = 32
semaphore max value = 32767


------ Messages: Limits --------
max queues system wide = 3732
max size of message (bytes) = 65536
default max size of queue (bytes) = 65536

删除ipc
ipcrm -m|-q|-s shm_id
%ipcrm -m 105



MySQL数据库MyISAM和InnoDB存储引擎的比较

MySQL有多种存储引擎,MyISAM和InnoDB是其中常用的两种。这里介绍关于这两种引擎的一些基本概念(非深入介绍)。

MyISAM是MySQL的默认存储引擎,基于传统的ISAM类型,支持全文搜索,但不是事务安全的,而且不支持外键。每张MyISAM表存放在三个文件中:frm 文件存放表格定义;数据文件是MYD (MYData);索引文件是MYI (MYIndex)。

InnoDB是事务型引擎,支持回滚、崩溃恢复能力、多版本并发控制、ACID事务,支持行级锁定(InnoDB表的行锁不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,如like操作时的SQL语句),以及提供与Oracle类型一致的不加锁读取方式。InnoDB存储它的表和索引在一个表空间中,表空间可以包含数个文件。

主要区别:

  • MyISAM是非事务安全型的,而InnoDB是事务安全型的。
  • MyISAM锁的粒度是表级,而InnoDB支持行级锁定。
  • MyISAM支持全文类型索引,而InnoDB不支持全文索引。
  • MyISAM相对简单,所以在效率上要优于InnoDB,小型应用可以考虑使用MyISAM。
  • MyISAM表是保存成文件的形式,在跨平台的数据转移中使用MyISAM存储会省去不少的麻烦。
  • InnoDB表比MyISAM表更安全,可以在保证数据不会丢失的情况下,切换非事务表到事务表(alter table tablename type=innodb)。

应用场景:

  • MyISAM管理非事务表。它提供高速存储和检索,以及全文搜索能力。如果应用中需要执行大量的SELECT查询,那么MyISAM是更好的选择。
  • InnoDB用于事务处理应用程序,具有众多特性,包括ACID事务支持。如果应用中需要执行大量的INSERT或UPDATE操作,则应该使用InnoDB,这样可以提高多用户并发操作的性能。