1,磁盘的组成:第一个扇区很重要,包含
主引导分区(MBR):安装引导加载程序的地方,446bytes;
分区表:记录整个磁盘分区的状态,64bytes;
硬盘默认的分区表仅能写入四组分区信息,这些信息我们称之为主分区(Primary)或扩展(Extended)分区;
分区的最小单位是柱面;
主分区和扩展分区加起来最多有四个(硬盘的限制);
扩展分区最多只能有一个(操作系统的限制);扩展分区的目的是使用额外的扇区来记录分区信息,扩展分区本身不能格式化。
能够格式化后作为数据访问的分区为主分区和逻辑分区;逻辑分区是由扩展分区持续切割出来的分区;
逻辑分区的数量依操作系统而不同,
分区的目的:数据的安全性和系统的性能考虑;
2,开机流程:
BIOS:开机主动执行的韧体(写入硬件上的软件程序),会认识第一个可开机的设备,并到给设备读取第一个扇区的MBR位置;
引导加载程序Boot loader:一个可读取内核文件来执行的软件;功能有三:提供菜单;载入内核文件;转交其他loader;loader可以安装在MBR 中,也可以安装在每个分区的引导扇区;
内核文件:开始操作系统的功能;
每个分区都拥有自己的启动扇区;实际可开机的内核文件是放在各分区内的;loader只会认识自己的系统分区内的可开机内核文件和其他loader而已;
3,Linux的Ext2文件系统
文件系统的最前面有一个启动扇区,里面安装引导装载程序;后面有若干个block group组成;每个block group由superblock、文件系统描述、block对应表、iNode对应表、iNode table、data block组成;
super block记录文件系统的整体信息;iNode记录文件的属性,一个文件占用一个iNode,同时记录文件数据所在的block号码;block实际记录文件的内容,一个文件可以占用多个block;
iNode记录block号码的区域定义为12个直接,一个间接,一个双间接,与一个三间接记录区;
block对应表和iNode对应表记录所有的block和iNode是否被使用;
目录的iNode记录该目录的相关权限和属性,并记录分配到的那块block号码;而block则是记录该目录下的文件名和该文件名占用的iNode号码数据
系统读取一个文件的步骤:先读取根目录的iNode(号码为2),判断是否有权限读取根目录的block,若有权限则读取block,在其中找到相应的子目录的iNode号码,重复上述步骤;
系统新建一个文件的步骤:先确定用户对于欲添加文件的目录是否具有w和x的权限,若有的话才能添加;根据iNode bitmap找到没有使用的iNode号码,并将新文件的权限属性写入;根据block bitmap找到没有使用的block号码,并将实际的数据写入block中,且更新iNode的block指向数据;将刚写入的iNode与block数据同步更新iNode bitmap与block bitmap,并更新superblock的内容;
ext2非日志文件系统,ext3日志文件系统。
较之 Ext3 目前所支持的最大 16TB 文件系统和最大 2TB 文件,Ext4 分别支持 1EB(1,048,576TB, 1EB=1024PB, 1PB=1024TB)的文件系统,以及 16TB 的文件。
4,挂载:将文件系统和目录树结合的操作
每个文件系统都有自己的信息,这个文件系统只有挂载到目录树上才能被我们使用。挂载点一定是目录,该目录是这个文件系统的入口。
5,当一个文件类型为目录时,
r权限表示能够读取该目录下的文件名列表;
w权限表示能够异动该目录文件列表;新建、删除、重命名文件;转移该目录内的文件位置;
x权限表示能够进入该目录成为工作目录cd。
6,链接文件:
硬链接:不会新建新的文件;在某个目录下新建一条文件名连接到某iNode号码的关联记录而已;即多个文件名对应同一个iNode号码。
不能跨文件系统,不能连接目录。
符号链接:会新建一个独立的文件,文件内容是源文件的文件名,让数据的读取指向它连接的那个文件的文件名。
7,流的三种缓冲:
全缓冲:在填满标准I/O缓冲区后才进行实际I/O操作。
行缓冲:在输入和输出中遇到换行符时,标准I/O库执行I/O操作。
不带缓冲:标准I/O库不对字符进行缓冲存储。
8,C程序的存储空间布局
高地址:命令行参数和环境变量、栈、堆、未初始化的数据(由exec初始化为0)、初始化的数据和正文(由exec从程序文件中读入):低地址。
内核使程序执行的唯一方法是调用exec函数。
9,进程
ID为0的进程是调度进程,即交换进程swapper;ID为1的进程是init进程。
父进程和子进程共享正文段;子进程获得父进程数据段、堆和栈的副本,但父子继承并不共享这些存储空间;
新的实现是读时共享,写时复制;copy-on-write。
fork的一个特性是父进程的所有打开文件描述符都被复制到子进程中,父子进程每个相同的打开描述符共享一个文件表项,父子进程共享同一个文件偏移量。
进程终止的8种方式:
1,从main函数返回;
2,调用exit;
3,最后一个线程从启动例程返回;
4,从最后一个线程调用pthread_exit;
5,调用_exit或_Exit;
异常终止方式:6调用abort;
7,接到一个信号;
8,最后一个线程对取消请求作出响应;
终止进程如何通知其父进程它是如何终止的:3个终止函数(exit、_exit、_Exit)将其退出状态作为参数传递给函数。
异常终止情况,内核产生一个指示其异常终止原因的终止状态,在任意一种情况下,该终止进程的父进程都能用wait或waitpid函数取得其终止状态。
孤儿进程:父进程在子进程之前终止;在一个进程终止时,内核逐个检查所有活动进程,以判断它是否是正要终止进程的子进程,如果是,则该进程的父进程ID就更改为1。
僵尸进程:一个已经终止但是其父进程尚未对其进行善后处理(获取终止子进程的有关信息、释放它仍占用的资源)的进程;
创建守护进程的步骤:调用fork()创建新进程;在父进程中调用exit,保证子进程不是进程组组长;调用setsid创建新的会话;将当前目录改为根目录(如果当前目录作为守护进程的目录,当前目录就不能被卸载);将标准输入、标准输出、标准错误重定向到/dev/null。
当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始执行。因为调用exec并不创建新进程,所以前后的进程ID不变,exec只是用磁盘上的一个新程序替换了当前进程的正文段、数据段、堆段、和栈段。
10,信号
可靠信号支持排队,不会丢失,不可靠信号不支持排队,可能会丢失;
每个进程都有一个信号屏蔽字,它规定了当前要阻塞递送到该进程的信号集;
在信号产生和递送之间的时间间隔内,称信号是未决的。
信号处理的三种方式:忽略该信号,捕捉该信号,执行系统默认动作;
可重入函数是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错。不可重入函数在信号处理函数中被视为不安全函数;
当对某个进程的信号产生时,先判断该进程的信号屏蔽字是否阻塞了该信号,如果没有那么就处理该信号。
SIGPIPE信号产生的原因:在管道的读进程终止时写管道;类型为SOCK_STREAM的套接字已不在连接时,进程写该套接字。
11,进程间通信:pipe、fifo、消息队列、信号量、共享内存。
12,线程
线程间共享的资源:文件描述符表、每种信号的处理方式、当前工作目录、用户ID和组ID、内存地址空间。
线程间非共享资源:线程ID、处理器现场和栈指针、独立的栈空间、errno变量、信号屏蔽字、调度优先级。
线程的终止方式:1,线程可以简单地从启动例程中返回,返回值是线程的退出码;2,线程可以被进程中的其他线程取消;3,线程调用pthread_exit。
13,socket
listen函数:对于给定的监听socket,内核要维护两个队列:
(1)已由客户发出并到达服务器且还没有完成TCP三次握手的套接字;
(2)已完成握手的套接字。
当来自客户的SYN到达时,TCP在未完成连接队列中创建一个新项,然后进行三次握手,若握手正常结束,该项就从未完成连接队列移到已完成连接队列的队尾。
当进程调用accept时,已完成队列中的队头项将返回给进程,或者如果该对列为空,那么进程将被投入睡眠,直到队列中新加一项才被唤醒。
客户接收到三路握手的第二个分节时,connect返回,而服务器要直到接收到第三个分节才返回,即相差RTT的一半。
当阻塞于某个慢系统调用的一个进程捕获某个信号且相应信号处理函数返回时,该系统调用返回一个EINTR错误。
三点注意:
(1)当fork子进程时,必须捕获SIGCHLD信号;
(2)当捕获信号时,必须处理被中断的系统调用;
(3)SIGCHLD的信号处理函数应使用waitpid函数以免留下僵死进程。while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) printf("child %d terminated\n", pid);
TCP的十一种状态:三次握手的原因是TCP是全双工的,连接是双方共同建立的;四次挥手的原因是TCP的半关闭状态,具有连接的一端结束它的发送后还能接收来自另一端数据的能力。
TIME_WAIT状态也称为2MSL等待状态,当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL,这样可以让TCP再次发送最后的ACK以防这个ACK 丢失。
五种I/O模型:阻塞式I/O、非阻塞式I/O、I/O复用、信号I/O、异步I/O
(1)select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。
(2)select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要一次拷贝,而且把current(当前进程)往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省不少的开销。
LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.ET(edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认(这句话不理解)。