本文主要是介绍daily reading,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
操作系统: 让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 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意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。
一些典型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,这样可以提高多用户并发操作的性能。
这篇关于daily reading的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!