操作系统
一、孤儿进程和僵尸进程
Linux中父进程使用fork函数创建子进程,子进程退出之后父进程应该用wait或waitpid函数回收子进程的资源并获取子进程的状态。
孤儿进程:父进程先于子进程退出,此时子进程就是孤儿进程,子进程就会被指派给进程号为1的init进程。
僵尸进程:子进程先于父进程退出,父进程没有调用wait函数回收子进程的资源和获取子进程的状态,此时子进程就变成了僵尸进程,但是子进程占用的是进程号,不占用CPU。
二、用户态、内核态
用户态就是操作系统的一种非特权状态,此时只能访问被授权的资源
内核态就是操作系统的一种特权状态,次数可以访问底层系统资源和硬件资源。例如使用CPU读取文件需要IO资源。
用户态到内核态需要保存上下文,这是非常消耗资源的。
三、操作系统的IO模型
IO模型:
同步阻塞:调用recv之后阻塞等待数据从内核返回到用户空间
同步非阻塞:执行recv不管有没有数据都返回了,没有数据就是不是过来看看数据准备好了没有
IO复用:单线程处理多个IO操作,线程会等通知,具体就是使用select/poll,等任意一个文件描述符上有IO时间发生就会通知程序进行IO操作
信号驱动IO:类似于事件驱动,线程会注册一个信号函数,在内核数据准备好的时候就中断当前程序,执行信号函数,信号函数里包含了要调用的函数,例如recv。
异步IO:调用aio_read命令把数据准备好,不需要程序调用recv命令,而是主动把数据复制到用户进程空间之后执行aio_read命令事先指定好的函数。
read操作:
涉及到调用IO的用户进程和系统内核,通过系统调用会向内核发送读请求,内核会向硬件发送读指令等待读就绪,DMA将读取的数据放到内核缓冲区,内核再将数据从缓冲区拷贝到用户进程空间。
同步和异步就是描述被调用方是否立即处理请求并响应
阻塞和非阻塞就是描述调用方发出请求之后是否等待响应。
select、poll、epoll:
他们都是IO多路技术,用来同时监听多个文件描述符
select:最原始的IO多路复用技术,最多只能监听1024个文件描述符
poll:比select监听的文件描述符个数更多,但是复杂度也随之增加
epoll:优化了poll的复杂度,提高了效率。
epoll两种模式:LT和ET。
epoll主要通过epoll_wait来获取就绪的fd。
LT模式就是epoll_wait检测到会通知程序,程序可以先不处理,等到epoll_wait还会通知。
ET就是检测到必须要处理,效率更高,但是ET得配合非阻塞socket防止阻塞读/写的时候其他文件描述符事件被饿死。
套接字socket:相当于是网络编程中一种通信链路的端点,一般和IP和端口号结合使用。
分为:流式套接字(TCP socket)和数据报套接字(UDP socket)
网络传输数据的流程:
1、服务端和客户端都创建socket对象,服务端根据TCP或者UDP创建对应的套接字,客户端也创建对应的套接字用于连接服务器。
2、服务端将套接字绑定到服务端的IP和端口号上,然后调用监听函数监听端口,等待客户端连接
3、客户端调用connect函数连接服务器指定的IP和端口号,服务端调用accept阻塞地接受请求来建立连接。
4、服务端和客户端可以使用recv、recvfrom函数来接收数据放到缓冲区。
5、管理socket连接释放资源
什么是零拷贝:
常见的形式有:mmap、sendfile、DMA、directI/O
mmap+write:3次拷贝、4次上下文切换
mmap代替read指令减少一次内核->用户空间的一次拷贝。mmap会将内核缓冲区的一段区域映射到page cache(内存)中,然后在进程虚拟空间中开辟一段虚拟地址,虚拟地址的页表指向的就是page cache,所以读数据的时候直接读page cache的内容即可。需要注意的是,mmap采用基于缺页异常的懒加载方式,第一次访问pagecache时会出现缺页异常,此时
mmap主要有三个缺点:缺页异常(page cache未命中有同步IO到磁盘)、TLB抖动(CPU的TLB命中率低会有cache miss)、回写粒度固定。
sendfile:3次拷贝和2次上下文切换
如果只是发送数据,则不需要把数据从内核态拷贝到用户态,而是直接将页缓存->socket 缓存。
sendfile+SG-DMA:2次零拷贝和2次上下文切换。
在sendfile的基础上,硬盘数据->页缓存->网卡设备
操作系统IO流程:
进程调用read系统指令,从用户态切换到内核态。
DMA把物理设备的数据都拷贝到页缓存里面,发起中断提醒用CPU把页缓存的数据拷贝到用户态中
内核唤醒线程,从内核态切换到用户态。
响应请求的时候会进行调用write函数,从用户态切换到内核态,由内核把用户态数据拷贝到内核态。再从socket缓存通过DMA拷贝到磁盘中
再使用DMA技术把数据复制到网卡上,完成以后在从内核态切换回用户态。
这种流程涉及到四次数据拷贝,也会涉及到4次上下文切换。
零拷贝就是减少数据拷贝次数/CPU拷贝次数。
虚拟地址(逻辑地址):一个进程中特有的一段连续的逻辑地址,例如0x00001,他相当于指针,不是具体的物理内存的地址,进程中线程的变量、函数地址均以逻辑地址存在。使用虚拟地址的原因有两点:
内存隔离:操作系统通过页表将虚拟地址映射到物理地址来实现内存隔离,避免所有进程都操作物理地址。
避免内存碎片化:如果直接分配物理地址给进程可能会出现内存碎片化问题
物理地址:实际硬件内存的地址,物理地址是全局共享的。
虚拟内存:
虚拟内存是由虚拟地址、页表、缺页中断、交换空间四个核心部分组成
好处:
1、用于拓展用户可使用的内存容量,程序也有一个更大的逻辑地址空间,可以启动内存占用更大的程序
2、做内存隔离,进程的虚拟内存是独立的,提供系统稳定性与安全性
分页、页表、分段:
分页(Paging)是将内存划分成固定大小的块,称为页(Page),通常为 4KB 或 8KB。进程的虚拟地址空间被分成同样大小的页,物理内存也被分成页帧(Page Frame)。分页的主要目的是管理内存、减少内存碎片,允许虚拟内存的实现(配合页表),使得进程可以运行在比物理内存更大的虚拟地址空间中。分页通过页表(Page Table)来映射虚拟页到物理页帧,地址转换由硬件完成,速度较快。分页的优点是内存利用率高,磁盘IO少,碎片少,但可能存在页内碎片(每个页可能有部分未被使用
页表就记录了虚拟页号和物理页框之间的映射关系。记录虚拟地址的页号、偏移量、对应的实际页框号。传入虚拟页号就能拿到实际物理页框号。需要引入快表TLB(CPU缓存,存储最近的虚拟->物理的映射关系)加速访问。所以!先从TLB查有没有映射关系,然后使用Table Walk Unit从页表中查映射关系。
多级页表:节省连续的存储空间,因为进程的虚拟内存地址是连续的,所以这里就类似于B+树那样,顶端是4级索引、下面是三级索引、再下面是二级索引、再一级索引、再对应物理内存地址。
优点在于存储更多数据的情况下能节省连续的内存,页目录和页表存储空间不需要连续,缺点在于增加寻址次数,例如二级页表:先查页目录,再查页表,再查物理内存
分段(Segmentation),它将内存划分为可变长度的段(Segment),每个段对应进程的一个逻辑部分,比如代码段、数据段、栈段等。每个段有自己的基址和界限寄存器,用于地址转换。分段的优点是支持逻辑上的模块化,便于共享和保护,但容易产生外部碎片,因为段的大小不同,可能导致内存空间无法有效利用。
DMA技术:
直接内存访问技术,无需CPU干预的情况下外部设备与主存储器之间进行数据传输。
CPU只需要给DMA发送指令并且让出总线控制权即可。
四、进程与线程
进程是资源分配的基本单位,线程就是处理器调度和执行的基本单位。
进程:它就是一个应用程序,例如JVM,每个进程都有独立的内存空间,一个进程有多个线程
线程:它就是一个执行单元,例如在一个JVM虚拟机中多个线程会共享堆内存与方法区,每个线程又有自己的虚拟机栈、程序计数器与本地方法栈。
进程上下文切换:
每个进程都私有一段连续的虚拟内存地址。因此进程切换的时候包括以下步骤:
切换页目录使用新的虚拟地址空间。
切换内核栈与硬件上下文。(内核栈保存执行内核代码时存储函数调用的函数的参数、返回地址与局部变量)
(硬件上下文指的是CPU的寄存器的值,例如程序计数器、堆栈指针)。
线程上下文切换:
切换内核栈与硬件上下文。
不同的线程依然共用同一个虚拟内存空间。
进程切换为什么消耗性能:
因为保存寄存器的内容非常消耗性能。
页表查找过程很慢,因为我们用TLB来保存常用的地址映射(虚拟->物理),但是进程切换之后页表也要切换,这样TLB就失效了,也就相当于虚拟地址映射到物理地址的过程变慢。
线程上下文切换的原因:
当前执行任务的线程的时间片用完了,要让出CPU资源。
获取不到锁阻塞了,
当前任务碰到IO阻塞,那么就会被挂起。
用户主动挂起,调用object.wait()方法。
避免线程上下文切换:
加锁的时候用乐观锁CAS
使用最少线程
使用Java21的虚拟线程减少上下文切换开销
进程间通信的方式(IPC):
管道:半双工,无格式字节流
匿名管道:只用于有继承关系的进程,非继承关系的进程找不到这个管道
命名管道:可以直接通过名字找到进程,然后找到管道来通信。
消息队列:进程间传递的数据以格式化的信息为单位,如果进程之间不存在可直接访问的共享空间,则必须使用操作系统提供的消息队列来通信,进程将消息发送至消息缓冲区,其他进程从中获取信息。
套接字:上面写了
信号:信号就是用于通知其他进程的,本身不携带信息,只是携带固定的一些值,其他进程会先把异常对应的函数准备好,然后信号来了直接执行对应的函数即可。
信号量:用于多进程之间的同步与互斥,使用信号量可以实现加锁,避免多个进程同时操作共享空间的资源,信号量有p操作(申请资源)、v操作(归还资源)。
申请资源会把数值减去M,表示被其他进程使用。归还资源会把数值加M,表示被归还了。
共享存储空间:每个进程的虚拟内存空间都指向相同的物理内存空间,一个进程对内存的修改另一个进程也能看到。使用shemget创建共享内存,使用ipcs查看共享内存。
mmap:两个进程都映射到同一个文件,两个进程进行读写的时候就完成通信。
零拷贝:
mmap+write:RocketMQ就是用这种方式
mmap会在进程的虚拟地址空间中分配一段虚拟地址范围,这段范围与内核缓冲区的数据建立映射关系,用户在虚拟地址上的操作就会映射到内核缓冲区的数据上,这样就不需要把内核缓冲区的数据拷贝到用户缓冲区上。
write可以直接将内核缓冲区的数据拷贝到socket缓冲区中。
因此这样就有四次用户态到内核态切换,三次拷贝。
sendfile:Kafka就是这种方式
进程调用sendfile,DMA把数据拷贝到内核缓冲区,然后可以直接将内核缓冲区的数据拷贝到socket缓冲区,最后DMA再从socket缓冲区拷贝到网卡。需要两次用户态与内核态切换、三次内存拷贝。
如果网卡支持SG-DMA技术,可以把内核缓冲区的描述符与数据长度传到socket缓冲区,然后直接从内核缓冲区拷贝到网卡,减少一次内存拷贝,需要两次用户态与内核态切换、两次内存拷贝。
五、系统有很多小文件有什么弊端?如何处理?
NameNode下面有DataNode,数据存储在DataNode,客户端上传文件,服务端会把文件分成block,存储在不同的DataNode里面。
小文件就是HDFS中文件的size远小于block的文件。
弊端:
1、元数据的增删效率低
2、服务启动时间长
3、限制了拓展性
4、访问性能降低,因为会在多个DataNode反复切换。
判断小文件数量是否过多:
单表小文件个数超过1000个。
使用巡检工具巡检,查看巡检报告看小文件数量
处理办法:
https://community.transwarp.cn/article/1088
SQL端规范:主要是表结构
SQL语句规范:避免大表直接join,
存储端合并:
计算端合并:
固态与机械硬盘的区别:
固态是存在内存芯片里面,机械是存在磁盘盘片上面
读写速度、价格、性能。
机械硬盘不容易掉盘,寿命长
固态反之。
六、Linux的软链接与硬链接
这是两种不同的文件链接方式,用于创建文件的访问路径。
软链接:符号链接,类似于win系统的快捷方式,它包含目标文件的路径信息。用ln -s命令创建。
硬链接:通过文件的索引节点(lnode编号)进行连接,使用ln命令创建。
七、Linux的栈大小一般是多少
linux栈的大小会直接影响循环的次数,栈空间大小除去申请的局部变量大小即可。
命令:ultimate -s
linux下栈的大小模型是8M或者10M,也可以使用ultimate -s 数字(单位KB)来修改栈的大小
 
                    
                     
                    
                 
                    
                 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号