Linux内核学习笔记(一)
1. 开机后的加载过程
主板上一通电,BIOS(基本输入输出)先进入内存,BIOS会有引导项,去磁盘加载第二个程序mbr,mbr里面有分区表,mbr通过分区表找到OS程序的分区引导程序,加载完引导程序之后就能看懂文件啦,就会把文件加载到内核,变成进程,这个文件叫做kernel。kernel接下来会接管cpu(reset),把计算机由实模式变成保护模式。
注意:BIOS来自于芯片;mbr来自于磁盘的第0磁道;引导程序来自于分区前固定宽度二进制代码;kernel才是来自于文件系统的文件,然后变成一个进程
保护模式:内核有一个GDT(全局描述符表),其描述了段机制(分段),其划分了内核空间和用户空间,并且添加了用户空间的指令不能访问内核空间的约束,保护模式分为ring0、ring1、ring2,ring3....,其中,内核空间是ring0,用户空间是JVM等进程(ring3),用户空间不能调“关机”等内核里面实现的指令,当用户调内核时,会发成态的切换,从用户态切换到内核态。切换是通过系统调用来完成。
中断:中断通过物理器件“晶振”来完成,晶振(时钟中断/硬中断)会每隔一个单位时间产出一个震荡频率,cpu感知到之后会放弃当前执行的进程,去访问内核中的进程调度,之后切换进程的运行(时间片轮转)。
用户空间和内核空间如何切换?:通过编译器。如果jvm需要切换到内核态,那么绝对不会把内核态的read函数等直接压到jvm的栈中,而是通过编译器,编译器触发软中断(80中断),把用户态调用内核态的需求变成中断码,如果cpu响应了中断,就会切换到内核态,执行read方法。read方法在内核栈中执行,把执行的状态压到用户栈。
2. 网络通信细节
发展历程BIO(阻塞式IO) => NIO(非阻塞式IO) => AIO(异步IO)。
BIO模型阻塞模型依靠多线程来应付,每个线程只能负责处理一个连接。
非阻塞
这个非阻塞发生在线程中,使用的是socket方法的NOBOLCK属性。线程挨个问(轮询),如果谁有数据要处理,再进行阻塞处理。
多路复用select非阻塞select,这次的轮询是放在内核中的,提高了性能。线程通过询问内核来处理哪些socket。
select作用:监控更多的文件描述符,等待一个或者多个文件描述符可用。
多轮复用epoll
select方式下,线程需要去询问内核那些socket是准备好的,可以去操作的,而在epoll方式下,内核会把这些socket的描述符放在内核中的空间中,这个空间与线程中的空间是可以互访的,所以线程不需要进行询问啦。
注意:内核的地址空间实际上不是内核的空间,因为内核的空间用户程序是无法访问。内核可以访问所有的地址,这里的空间和线程空间是两个指针指向同一个地址空间。这个地址进程和内核都可以访问。而select方式会在各自的空间中拷贝信息。所以效率不如epoll。效率提高的关键因素是使用了mmap,mmap将一个文件或者其它对象映射进内存。
netty就是使用的epoll方式的NIO。
3. 零拷贝
如果用户调用了read和write系统调用,原始的流程如下,会发生多次系统状态的切换:
原始流程
零拷贝(内核由sendfile实现)就是如果使用read,不会再与用户进行交互,而直接发给socket,减少了内核态和用户态的切换。