图片版在这:http://www.javashuo.com/article/p-wcrjpnzs-ha.htmlhtml
上完5103其实就该总结一下的......仍是懒 (呵node
函数调用时,函数参数、返回地址、环境、函数内非static的局部变量存入栈。(栈空间是专门留给函数用的)git
程序内全部malloc/new出来的空间、全局变量、全部的static变量存入堆。程序员
(Ref:http://www.javashuo.com/article/p-npysctrh-gx.html)github
堆是从低地址向高地址增加的,栈相反。参考下图算法
User programs are typically executed in user mode. In user mode, program cannot control devices or access memory address directly. All privileged operations should be executed by using system call. A user program executes in the USER mode and it is given limited privileges. Region of memory it can access is restricted. It cannot execute certain instructions such as HALT or setting or changing timers. It cannot directly access any I/O devices or network ports. The primary reason is to protect the system and other user processes and the file system from a misbehaving program.shell
Kernel code is trusted to be safe and correct. This code is executed in Privileged/Supervisor Mode. It can execute any operation (i.e. instruction). A user process executes the kernel code by making a system call.
安全
System Call的全过程:服务器
System Call和普通函数调用(procedure call)的对比:网络
system call:
1. Push all parameters into user mode stack
2. put the code of system call into CPU register (in other words, call this procedure)
3. execute a trap instruction, so it will start executing in kernel mode at a fixed address.
4. kernel code will be dispatched to a system-call handler, and the system-call handler runs in kernel mode.
5. After the kernel code finished, switch back to user mode and return to the instruction following TRAP in system call library procedure.
6. After the system call finished, return to user program.
7. The user program will clean up the parameters of system call in the stack, and user program continues.
procedure call:
1. Push all parameters into user mode stack
2. put the code of procedure call into CPU register (in other words, call this procedure)
3. execute the procedure in user mode
4. After procedure call finished, return to user program.
5. The user program will clean up the parameters of system call in the stack, and user program continues.
The procedure call does not need to switch betweenuser mode and kernel mode, so it will save time
Interrupts & exceptions & System calls & traps
Interrupts source: External devices (eg: finish I/O operation on hard disk). Goal: CPU could work in parallel with devices
Exceptions source: Errors in CPU when executing instructions. Goal: Handle internal errors in CPU (eg: divided by zero)
System calls source: Program manually call system call functions provided by operating system. Goal: Execute features which needs OS support (eg: I/O)
Traps source: TRAP instruction in program. Goal: Transfer from user mode into kernel mode
fork()函数
1.Only the thread who called fork() will be forked to new child process. So the child process only have 1 thread.
2.The child process will copy all the memory data from its parent process. Then they will run as independent processes. In modern operating systems, they will do
copy-on-write in fork(), which means both parents and child will receive a read-only copy of the parent's data space, instead of copying the whole data in the first
place. The copy will actually occur when one of them want to modify some data.
3.Based on (2), mutex variables will be copied into child process. Suppose we added locked a mutex before fork(). After fork(), the mutex in the child process will
remain locked, and there will be only one thread in the child process, which means this mutex in child process will never be unlocked(and this is called deadlock). Even if we unlocked mutex in parent process, the one in child process will not be modified.
4.Based on (2), File descriptors will be copied into child process. So they will have the same file pointer, which will point to the same file table. Thus the file status
opened in both processors(include their filename, current file offset) are shared.
clone()函数
Linux provides a more powerful function than fork for creating a new process or thread.
• It can be used to create a new process as in case of fork.
• It can also be used to create a new thread in the address space of the calling process.
进程的上下文切换(略)
进程和线程的区别
A process represents one single sequential activity. – one execution context (Program counter and stack)
A thread represents one activity -- one context and stack per thread. All Threads of one process can share resources such as memory, open files, and communication channels.
All threads in a process share the following items:
– Address space
– Global variables
– Open files / resources
– Child processes
– Signals and signal handlers
– Accounting Information
Thread specific items:
– Program counter and registers (execution context)
– Stack
– Execution state (ready, waiting, running)
进程之间私有和共享的资源
线程之间私有和共享的资源
协程
进程是资源分配的最小单位,线程是CPU调度的最小单位。而协程能够理解为同一个线程经过上下文切换来“超线程”,并发执行两个工做。比起线程,协程更加轻量,并且多个协程访问同一资源不须要加锁(由于本质上仍是在同一个线程内)。
协程的详细定义能够参考这里。另外go语言对协程有很好的支持。
进程/线程之间的通讯方式总结
进程之间的通讯方式以及优缺点 管道(PIPE) 有名管道:一种半双工的通讯方式,它容许无亲缘关系进程间的通讯 优势:能够实现任意关系的进程间的通讯 缺点: 长期存于系统中,使用不当容易出错 缓冲区有限 无名管道:一种半双工的通讯方式,只能在具备亲缘关系的进程间使用(父子进程) 优势:简单方便 缺点: 局限于单向通讯 只能建立在它的进程以及其有亲缘关系的进程之间 缓冲区有限 信号量(Semaphore):一个计数器,能够用来控制多个线程对共享资源的访问 优势:能够同步进程 缺点:信号量有限 信号(Signal):一种比较复杂的通讯方式,用于通知接收进程某个事件已经发生 消息队列(Message Queue):是消息的链表,存放在内核中并由消息队列标识符标识 优势:能够实现任意进程间的通讯,并经过系统调用函数来实现消息发送和接收之间的同步,无需考虑同步问题,方便 缺点:信息的复制须要额外消耗 CPU 的时间,不适宜于信息量大或操做频繁的场合 共享内存(Shared Memory):映射一段能被其余进程所访问的内存,这段共享内存由一个进程建立,但多个进程均可以访问 优势:无须复制,快捷,信息量大 缺点: 通讯是经过将共享空间缓冲区直接附加到进程的虚拟地址空间中来实现的,所以进程间的读写操做的同步问题 利用内存缓冲区直接交换信息,内存的实体存在于计算机中,只能同一个计算机系统中的诸多进程共享,不方便网络通讯 套接字(Socket):可用于不一样及其间的进程通讯 优势: 传输数据为字节级,传输数据可自定义,数据量小效率高 传输数据时间短,性能高 适合于客户端和服务器端之间信息实时交互 能够加密,数据安全性强 缺点:需对传输的数据进行解析,转化成应用级的数据。 线程之间的通讯方式 锁机制:包括互斥锁/量(mutex)、读写锁(reader-writer lock)、自旋锁(spin lock)、条件变量(condition) 互斥锁/量(mutex):提供了以排他方式防止数据结构被并发修改的方法。 读写锁(reader-writer lock):容许多个线程同时读共享数据,而对写操做是互斥的。 自旋锁(spin lock)与互斥锁相似,都是为了保护共享资源。互斥锁是当资源被占用,申请者进入睡眠状态;而自旋锁则循环检测保持者是否已经释放锁。 条件变量(condition):能够以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一块儿使用。 信号量机制(Semaphore) 无名线程信号量 命名线程信号量 信号机制(Signal):相似进程间的信号处理 屏障(barrier):屏障容许每一个线程等待,直到全部的合做线程都达到某一点,而后从该点继续执行。 线程间的通讯目的主要是用于线程同步,因此线程没有像进程通讯中的用于数据交换的通讯机制
Process Control Block
存储了如下信息:
Process state Number
User ID, accounting info, scheduling priority
Process context
Memory info, page tables
Open files, current /root dir
Pending signals and I/O
进程的几种状态:
New – process is being created and initialized
Running – currently executing
Ready – waiting to get CPU to become running
Blocked – waiting for some event, such a I/O completion
Swapped – partially executed, and its memory image has been stored on the disk
Terminated – process has completed due to normal or abnormal exit.
sleep: A process executing a system call may go to sleep to wait for a resource.
wakeup: When the resource is available it is awakened
注意区分两个概念:
忙等待(busy-waiting): while(i!=1) {…} 消耗 CPU
阻塞等待(blocked-waiting): 挂起,不消耗 CPU
临界区:同时只有一个进程能够访问的资源。为了互斥访问临界资源,每一个进程在进入临界区以前,须要先进行检查。
A sequence of code is called CRITICAL SECTION has the following properties: Mutual exclusion Property : At any time, at most one process can be executing the critical section code. In general, updates to shared data structures are performed in a critical section.
信号量
The motivation was to avoid busy waiting by blocking a process execution until some condition is satisfied.
Binary Semaphore:value取值只能取0和1。0 表示临界区已经加锁,1 表示临界区解锁。
经典的信号量同步问题
1111111111111
死锁
死锁的发生条件:
预防死锁的方法
银行家算法:避免死锁。在进程申请资源时,检查申请的合理性。只有不致使死锁的申请才被承认。
安全性算法:由银行家算法改进更通用的安全性算法。用于multiple resource type的状况。
Ref:https://blog.csdn.net/Caoyang_He/article/details/80819411
管程(monitor)
略
Paged memory management:eliminates fragmentation by non-contiguous allocation
页表使用虚拟地址中的页号做为索引,以找到相应的物理页号。每一个进程都有它本身的页表,用来将程序的虚拟地址空间 映射到主存中。为了指出页表在存储器中的位置,硬件包含一个指向页表首地址的寄存器,咱们称之为页表寄存器 ( page table register )
OS中会同时运行不少的进程。进程的地址空间,以及它在主存中能够访问的全部数据 , 都 由 驻 在主 存 中 的页表所定义 。 操 做 系统只是简单地加载页表寄存器用来指向它想激活的进程的页表 , 而不是保 存整个页 表 。 因为不一样进程使用相同的虚拟地址 , 所以每一个进程有各 自 的页表 。 操 做系统负责分配物 理主存和更新页表 , 所以不一样进程的虚拟地址空间不会发生冲突 。
虚拟存储器系统必须使用 write-back机制,对存储器中的页进行单独的写操做(暂时不修改磁盘上对应的页),而且在该页被替换出存储器时再被复制到磁盘中去。为此,为了追踪读入 主存中 的页是否被写过 ,能够在页表中增长一 个脏位 (dirty bit) 。 当被指向的这个页中任何字被写时就将这一位置1 。修改过的页也被称为dirty page
页表的结构
程序中使用的地址都是逻辑地址(Page Number和Offset)。CPU想访问某内存空间时,首先在TLB(位于CPU中,至关于一个页表的cache)中找页号对应的页框号。若是找到了就直接去物理内存读该页框的内容。若是没找到就先进入页表找,获得页框号和物理地址(Frame Number+Offset),再去读物理内存。
另外,每一个页表项使用 1 位有效位,就像在 cache 中设计的一样 。 若是该位为0,表示该页不在主存中,就发生一次缺页 。 若是该位为1,代表该页在主存中,能够直接根据物理页号去内存中读取这个页 。 (《计算机组成与设计——硬件/软件接口》 P294)
TLB的做用就是避免每次都要查找页表(这个速度是比较慢的)。TLB 的每一个标记项存放虚页号的一部分,每一个数据项中存放了物理页号 。因为咱们每次访问的是 TLB 而不是页表, TLB 须要包括其余状态位,如脏位和引用位。 以下图,灰色线表示直接经过TLB hit找到内存中的页面,没找到的就要去页表查了。
页表能够提供该虚拟地址的物理页号(能够用来建立TLB项),或者指出该页在磁盘上,这时就会发生缺页 。 因为页表对于每一个虚页都有 一个相应的项,并不须要标记 ; 换句话说,不一样于 TLB, 页表并不属于 cache 。在后面FastMATH的例子中能够更深入的体会到这一点。
各个步骤所需的时间(clock)能够参考下图:
TLB和cache的关系
TLB也在CPU中,属于一种特殊的cache。那么它和普通的L一、L2 Cache有什么关系呢?
以HI书的图5-30中介绍的FastMATH芯片为例。原版的图比较复杂,简化一下大概是这样:
这里面CPU想访问一个虚拟地址,而后有这么几种可能性(VA: 虚拟地址 PA: 物理地址):
能够看出:
上面这套流程假定了在访问 cache 以前,全部存储器地址都被转换成物理地址。在这种结构中,cache 是按物理地址索引 (physically indexed) 而且是物理标记 ( physically tagged) 的(全部 cache 的索引和标记都用物理地址,而不是虚拟地址) 。 在这个系统中,假定 cache命中,那么访问主存的时间要包括对 TLB 访问和 cache 访问的时间,固然,这些访问能够流水地执行 。这种设计叫作物理寻址的Cache。
另外还有一种虚拟寻址Cache,这里Cache里的数据是按虚拟地址索引并访问的。但这就有个问题了:若是两个很类似的进程用了某个如出一辙的虚拟地址,那么存放在cache中就会致使混乱。一个办法是对不一样进程搞个别名,但这样就增长了软件的复杂度。
这两种设计观点经常使用的折中方法是采用虚拟地址索引的 cache-—-有时仅仅使用地址的Page offset(页内偏移)部分,由 于没有被转换,所以其实是物理地址——但使用物理标记。这些采用虚拟索引和物理标记的设计,试图同时拥有虚拟地址索引 cache 的优越性能以及物理寻址 cache (physical addressed cache) 的简单结构 。 例如 ,在 这种状况下就没有别名的问题 。 要实现这种方法,必须在最小页大小 、 cac he 大小以及相联度之间进行谨慎的权衡 。(HI P301,Problem set Q2(b))
Page Fault
物理内存空间是有限的,因此引入虚拟内存的概念。当程序试图访问的内存地址如今不在物理内存中时,就会发生Page Fault。此时OS得到控制权,并进入TRAP(进入内核态,并由系统将对应的内存页从虚拟内存载入物理内存)。
操做系统在建立进程的时候一般会在闪存或磁盘上,为进程中全部的页(所有虚拟地址空间)预留一块磁盘空间 。 这一磁盘空间称为交换区 (swap space) 。 同时,它也会建立一个数据结构来记录每一个虚拟页在磁盘上的存放位置 。 这个数据结构多是页表中的一部分,也多是辅助数据结构, 寻址方式和页表同样 。
Page Fault后的过程以下图:
页面置换算法
内存满时用于选出要放入虚拟内存的页面。(略)
当页表太大时须要分红多级页表。
二级页表:第二级页表能够按须要动态调入,从而节省空间。
Working Set
The memory references generated by a program over some period of time tend to be confined to some small number of pages which reflects its "locality" during that execution phase. Try to maintain the pages which belong to the current working set (locality). Working set is estimated based on a time window of execution instead of some number of memory references. Parameter t defined as a time window. Working-set algorithm removes page p from then working-set if age(p) > t
程序在一段时间内访问的内存块集中在特定的几块。所以能够记录最近(某个time window内)用过的k个Page,保证它们不会被置换出去。
分段内存管理(略)
分段和分页的比较
对程序员的透明性:分页透明,可是分段须要程序员显式划分每一个段。
地址空间的维度:分页是一维地址空间,分段是二维的。
大小是否能够改变:页的大小不可变,段的大小能够动态改变。
出现的缘由:分页主要用于实现虚拟内存,从而得到更大的地址空间;分段主要是为了使程序和数据能够被划分为逻辑上独立的地址空间而且有助于共享和保护。
页面置换算法
FIFO:最简单的......
LRU:Replace 最久没用过的。注意如何实现LRU算法也是个常常被问的问题
NRU:按如下优先级(顺序按timestamp而定)淘汰页面:没被访问过也没被修改过 > 没被访问过但修改过 > 访问过但没被修改过 > 访问过也修改过。
Working Set:为每一个页面设置一个R bit(记录当前时钟时间间隔内是否访问过该页面)和timestamp(记录上次访问时间),而后按如下规则淘汰页面:
其中τ是一个参数,表示一个Working set的大小(按时间计算)。R==1表示刚刚用过,确定不能淘汰。若是有页面知足R==0且timestamp已经在τ以外,直接淘汰它。若是扫描了一圈发现不存在这样的页面,就把R==0且timestamp最小(距离如今最久远)的一个淘汰掉。
Working Set Clock:对基本工做集算法的改进,提升了一点点性能,但意思仍是同样的
Polling: busy waiting until device finish
Interrupt: 用中断处理,但频繁中断占用太多 cpu
DMA:传输一大段才 interrupt,而不是每一个 word/byte 都中断
1. CPU sets up the DMA controller register to handler DMA transfer from a disk to memory.
•Number of bytes to be transferred
•Memory address where to transfer the data.
2. DMA controller issues a request to disk controller read the next block in its buffer and write to a memory address.
3. Disk controller reads disk block in its buffer, and writes it to memory buffer.
4. After writing the block to the memory, the disk controller “acks” the DMA controller, which then decrements the count, increases the memory address, and goes to step 2 if
the count is greater than 0.
5. DMA controller interrupts the CPU when the count reaches 0.
Disk && Disk Storage Management(略)
Concurrent Access to Files
On Unix system:
•Writes to an open file are visible immediately to other users that have this file open at the same time.
•When a process forks a child, a file open at the time of fork is shared with the child. Both parent and the child share the file position pointer
inode
Ref:https://blog.csdn.net/xuz0917/article/details/79473562
文件存储在硬盘上,硬盘的最小存储单位叫作“扇区”(Sector)。每一个扇区储存512字节(至关于0.5KB)。操做系统读取硬盘的时候,不会一个个扇区的读取,这样效率过低,而是一次性连续读取多个扇区,即一次性读取一个“块”(block)。这种由多个扇区组成的“块”,是文件存取的最小单位。“块”的大小,最多见的是4KB,即连续八个sector组成一个block。
文件数据都储存在block中,那么很显然,咱们还必须找到一个地方储存文件的“元信息”,好比文件的建立者、文件的建立日期、文件的大小等等。这种储存文件元信息的区域就叫作inode,中文译名为”索引节点“。每个文件都有对应的inode,里面包含了与该文件有关的一些信息。如图所示
The addresses of all storage blocks of a file are recorded in one (or more) "index block". This block is maintained on the disk and is accessed through the directory entry. When a file is opened, this block is read into memory. Once this block is in memory, any block of the file can be accessed directly
Unix的iNode是一个多级的索引结构。以下图,
Suppose that file-block size is 4KB. The first 10 direct pointers will point first 40KB of the file.
Suppose that the first level indirect block has 128 pointers. (设32bit pointer,4KB/32bit=128)
Then, with the block size of 4KB, we can now address additional 128*4KB, i.e. about 0.5 MBytes.
For a file larger than that, the INODE contains the second level indirect blocks. This block has pointers (128) for 128 first level indirect blocks, each of which points to data blocks of 0.5 MBytes. Thus by using the second level indirect pointer, we can now store additional 64 MBytes in the file.(128*128*4KB)
With third level indirect block we get file size close to 8GB. (128*128*128*4KB)
文件夹也有inode。访问文件夹时,先访问该文件夹的iNode,而后顺着iNode找该文件夹所在的block。
Symbolic Linking(符号连接/软连接) | Hard-Link(硬连接) | |
相似Windows的快捷方式,只记录源文件的位置。 是个独立的文件,占用空间 |
与源文件指向相同的iNode,不占用空间。 源文件的全部hard link都被删除时,源文件才真正被删除。iNode keep count of hard links. |
|
Pros | 能够为文件夹建立symbolic link 源文件能够在不一样的文件系统。 |
可用来保护源文件 |
Ref:
https://github.com/CyC2018/CS-Notes/blob/master/notes/%E8%AE%A1%E7%AE%97%E6%9C%BA%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%20-%20%E7%9B%AE%E5%BD%95.md
https://github.com/huihut/interview#-%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F
主机字节序与网络字节序
主机字节序又叫 CPU 字节序,其不是由操做系统决定的,而是由 CPU 指令集架构决定的。主机字节序分为两种:
各架构处理器的字节序
网络字节顺序是 TCP/IP 中规定好的一种数据表示格式,它与具体的 CPU 类型、操做系统等无关,从而能够保证数据在不一样主机之间传输时可以被正确解释。网络字节顺序采用大端(Big Endian)排列方式。
大内核和微内核
大内核
大内核是将操做系统功能做为一个紧密结合的总体放到内核。因为各模块共享信息,所以有很高的性能。
微内核
因为操做系统不断复杂,所以将一部分操做系统功能移出内核,从而下降内核的复杂性。移出的部分根据分层的原则划分红若干服务,相互独立。在微内核结构下,操做系统被划分红小的、定义良好的模块,只有微内核这一个模块运行在内核态,其他模块运行在用户态。由于须要频繁地在用户态和核心态之间进行切换,因此会有必定的性能损失。
编译系统
如下是一个 hello.c 程序:
#include <stdio.h> int main() { printf("hello, world\n"); return 0; }
在 Unix 系统上,由编译器把源文件转换为目标文件。
gcc -o hello hello.c
这个过程大体以下:
静态连接
静态连接器以一组可重定位目标文件为输入,生成一个彻底连接的可执行目标文件做为输出。连接器主要完成如下两个任务:
目标文件
动态连接
静态库有如下两个问题:
共享库是为了解决静态库的这两个问题而设计的,在 Linux 系统中一般用 .so 后缀来表示,Windows 系统上它们被称为 DLL。它具备如下特色:
111