-
问熟悉Linux操作系统么?
答: linux 操作系统按照层次可以分为用户态和内核态两层。用户态主要运行着用户的应用程序,比如shell,nginx等程序。内核态主要运行着驱动等核心程序,管理着底层硬件软件等资源。为什么这么划分, 因为操作系统发展至今已经是多用户多任务的系统了,如果每个用户,每个任务直接访问硬件资源,必定会带来资源竞争等各种问题。同时,硬件的操作又非常复杂的, 如果不谨慎操作,很容易出错,导致整个系统崩溃,因此C P U指令也划分了权限等级 ,所以需要操作系统内核去统一管理,用户态访问内核态的资源主要是通过系统调用。 (什么是系统调用:操作系统为用户态进程访问内核态管理的资源所提供的一种访问方式)内核又可以分为进程管理, 内存管理,文件管理,网络管理几部分。
进程是程序的一次运行,是操作系统分配资源的基本单位。进程管理主要有进程的创建和调度以及回收。进程的创建主要是fork函数,一次调用,两次返回, 然后exec。 copy on write(为什么是copy on write), 复制资源, fork之后一般先运行子进程(避免不必要的copy)。
进程的调度也就是进程状态之间的切换, 主要状态包括,就绪态, 运行态,挂起,进程的运行状态可以通过linux的top命令去查看, 其中有R S Z D,新建一个进程,一般处于就绪态,等到CPU调度之后,就会进入R running 状态,在这期间,如果有等待资源等操作,就会进入S或D状态。
进程的回收是指程序在运行完之后,子进程释放资源、退出,然后右父进程调用waitepid去回收, 如果父进程不及时调用wait或者waitpid, 子进程就会变成僵尸进程,在top命令中显示为Z状态。
僵尸进程除了进程号,不占用其他资源,如果是父进程没有显示的回收, 并且可中断,则可以杀死父进程,由1号进程接管,1号进程如果收到其他子进程的退出信号,会遍历整个僵尸进程的链表,并回收僵尸进程。 僵尸进程的主要危害是占用pid资源, 极端情况下可能因为pid资源被耗尽导致新进程创建失败。进程又可以分为普通进程和后台进程(deamon进程)。普通进程一般都有输入输出, 与用户直接交互,在一定时间内运行完成然后退出。后台进程通常周期性地运行在系统中,等待某种事件的发生,为用户提供某种服务,它脱离了控制终端。后台进程一般通过两次fork + setsid 来创建。要让一个进程脱离终端的控制,就需要脱离父进程的会话组,新建一个会话组。 所以需要先新建fork新建一个子进程,然后父进程退出, 子进程调用setsid成为会话组的组长。 虽然此时,进程已经脱离了终端的控制,但是因为进程是会话组长,还有可能打开终端, 所以需要再fork一次,父进程退出,子进程运行, 这样子进程既脱离了终端的控制,也不会再开终端。
进程管理还包括进程间的通信,主要有管道, 共享内存, 信号,信号量等。管道又分匿名管道和命名管道,匿名管道常见的有shell中的 |,它主要是用在父子进程之间通信。命名管道有PIPE,可以在没有血缘关系的进程之间通信, 在操作系统中以文件的形式表示,继续深入扩展的话就涉及到文件管理。
共享内存允许两个或更多进程访问同一块内存是内存,进行数据交换和通信。不同机器的进程之间的通信还有RPC, HTTP等。也就是网络管理模块。
python中创建一个新进程一般用subprocess.popen, 或者进程池(编程模式)。对于线程,因为cPython的GIL,导致python多线程并不能使用多核, 所以我们还可以选择协程。协程与进程和线程的区别是协程的调度是由程序自身控制,减少了内核上下文的切换,效率更高,也是golang语言高效一个重要原因(自行拓展)。
linux下常见的进程相关的命令有top ps pstree pidstat (举一个最常用的)Linux系统一切皆文件,对于文件管理,就是我们常说的文件系统。 文件系统中有几个关键的概念,目录项, inode,以及数据块。一块硬盘格式化之后, 就按照特定大小将硬盘划分成多个小块叫做block,每个小块用一个编号记录, 这些块儿大部分用来存放用户数据,其他部分用来存放文件系统元数据。一个文件对应一个inode,inode中记录着文件的属性,比如文件的拥有者,文件的权限,创建时间,大小等等,最重要的是里面还保存着文件数据的存放位置。对于目录而言, block存放的是目录项,其实就是文件名和Inode的对应关系,而文件的block中保存的就是文件真正的数据。
文件管理同样涉及的是文件创建, 查找,删除。创建新文件时,文件系统先根据inode bitmap中找出一个空闲的inode,然后再根据block的bitmap中选出空闲的block,用来存放文件内容,并将文件的元数据写到inode中, 然后将文件名和inode组成一个目录项,写到对应目录的数据区,这样,一个文件就创建好了。 文件的查找主要是依据文件路径和文件名,linux系统中所有的文件都是在根/目录之下,而根目录的inode是固定的,为2号,所以就可以从根目录开始,通过2号inode,找到根目录,进而通过目录项一层一层的查找,直到找到文件的inode,最终找到文件。 文件的组织主要是基于红黑树的数据结构(可以拓展红黑树的数据结构)。
文件的删除主要是基于标记删除,删除文件时,同样先查到对应的文件,将文件的inode和block对应的bitmap位置标记为空闲,但是并不真正删除block中的数据。这也是文件删除之后, 如果没有被复写,能够被找回的原因。
在内核中,文件系统从上往下包括VSF层,主要是用来屏蔽不同文件系统的差异,为用户提供统一的接口。接着是buffer/cache层,主要是用来加速文件的读写(可以联想到mysql的innodb_flush_log_at_trx_commit调优和linux的sync命令)。 接着是通用块层,主要是将IO操作转换成数据块的操作,屏蔽不同块儿设备之间的差异。接下来是IO调度层,IO调度层主要是将IO合并, 排序,加速对磁盘的访问。接着就到驱动层和物理设备。
文件又可以分为普通文件和链接文件,链接文件包含软链接和硬链接,链接文件可以通过ln 命令来创建,软链接本质是新建了一个文件,有新的inode,只不过数据区中放的是源文件的路径。 而硬链接则是在目录中新建了一个目录项,目录项中新的文件名对应的inode还是源文件的inode, 所以删除硬链接, 源文件也就删除了, 但是删除软链接,不会删除源文件。因为硬链接共用inode,所以不能夸文件系统,因为不同的文件系统的inode是自行独立管理的。
linux下文件相关的常用命令有touch state lsof iostat,列举一个最常用的,主要用来干啥。内存管理主要包括虚拟地址空间,以及物理内存的分配和释放。首先是虚拟地址概念, 虚拟地址空间主要为进程提供内存隔离的安全特性,以32位系统为例,每个进程有独立的4G虚拟地址空间,确保内存访问安全隔离。虚拟地址和物理地址映射转换,便于地址重定位。 还有虚拟内存的段页式管理,并且挺高了内存的利用率。4G的虚拟空间, 从下往上分别是NULL区域,代码段,数据段,堆,共享区,栈,内核空间。 fork一个进程,父进程和子进程就可以共享代码段,然后分别维护各自的数据段和上下文,提高内存的利用率。堆和栈主要用来分配临时内存,栈的内存,主要用在函数参数,以及局部变量的分配,然后由操作系统管理释放。堆由主要由用户调用malloc分配,底层通过brk(小于128K)和mmap分配,通过free去手动释放,共享区一般是内存放各种so库。
物理内存主要由buddy系统和slab系统管理。与硬盘管理的思想类似,系统将内存划分成一页一页(4K大小),为了避免外部碎片,linux系统采用伙伴系统(buddy system)来管理内存。伙伴系统算法(Buddy system),把所有的空闲页框分组为11个块链表,每个块链表分别包含大小为1,2,4,8,16,32,64,128,256,512和1024个连续页框的页框块。最大可以申请1024个连续页框,对应4MB大小的连续内存,每个页框块的第一个页框的物理地址是该块大小的整数倍。申请内存时,就根据指定大小从合适的链表中选择合适的块。伙伴系统(buddy system)是以页为单位管理和分配内存。但是现实的需求却以字节为单位,直接以页分配,会产生很多内部碎片,严重浪费内存。为了解决内部碎片问题,slab分配器就应运而生了,专为小内存分配而生。slab分配器分配内存以Byte为单位。但是slab分配器并没有脱离伙伴系统,而是基于伙伴系统分配的大内存进一步细分成小内存分配。
linux下常用的内存命令free top vmstate sar网络管理主要控制网络数据的收发。OSI标准可以抽象成七层,TCP/IP四层模型, 由上往下包括物理链路层,网络层,传输层,应用层。 在编程中我们最常接触到的就是socket编程,服务端的主要流程包括 socket bind listen accept close, 客户端socket connet, 对应着TCP/IP的三次握手, 四次挥手。 TCP为什么需要三次握手, 因为TCP是面向连接,并且可靠,所以需要SYN_SENT, SYN_SENT + ACK, ACK(DATA + ACK),因为第二次的SYN_SENT和ACK是可以合到一起的,所以是三次。三次握手过程中会产生半连接状态,半连接状态主要是服务端为了记住第一次连接是从哪个客户端发起的,这个过程也会引发SYN 洪水攻击,主要解决办法是增加半连接长度(tcp_max_syn_backlog)和使用SYN_COOKIE, accept之前, 三次握手已经完成,socket已经放到了连接队列, accept只是从连接队列中取出socket,。 为什么需要四次挥手,因为TCP是全双工模式,断开连接时第二次的ACK和第三次的FIN信号不会合并,所以需要四次挥手。四次挥手过程中可能出现TIME_WAIT或者CLOSE_WAIT。TIME_WAIT可以通过tcp_tw_recycle加速回收, tcp_tw_reuse 或者复用来解决。使用过程中还涉及到网络的流量控制和拥塞控制,比如我们在下载电影时, 下载速度会逐渐提升,这个过程就是流量控制和拥塞控制的过程。
参考:https://www.missshi.cn/api/view/blog/5db596173b4ab2329f000004
网友评论