操作系统与Linux常用知识总结

操作系统与Linux常用知识总结


Java、大数据开发学习要点(持续更新中…)



一、操作系统部分:

1. 进程与线程间的区别、协程相关

  • 进程和线程的区别,可以用JVM进程内存模型举例对以下方面总结:

    (1)从资源层面看:线程拥有较少的系统资源(只有如程序计数器,一组寄存器和栈),并且同一进程的不同线程间共享进程资源。
    (2)从系统调度层面看:线程是CPU调度的基本单元,而进程是除CPU外系统资源的分配单元。
    (3)从系统开销层面看:创建进程需要操作系统为其分配或回收资源(内存空间、I/O资源等),开销较大。同样的在进程切换时,需要修改进程PCB信息、保存当前执行上下文和恢复另一进程的执行环境;而线程共享进程的许多资源,线程切换只会涉及少量寄存器、缓存内容,开销较小。
    (4)从通信层面看:进程间通信需要依靠进程间通信方法实现;而同一进程的不同线程由于共享地址空间,通信甚至不需要内核干预。

  • 协程
      主流JVM实现是通过用户线程与内核线程的1比1模型设计实现的。即使在编程中我们常用线程池(几十到上百线程)来应对百万计的请求,其中的线程切换虽然是内核轻量级进程的切换,但频繁的上下文切换(寄存器、缓存等)仍然是开销巨大的。但试想将上下文切换的工作在用户态下由用户程序自己实现那么这部分开销就可以缩减。协程就是基于这种思想的实现,协程运行在线程之上,一个协程执行完成主动让出使另一个协程运行在此线程上
      其实现实际就是线程的一个代码片执行到需要请求其他资源而阻塞的情况,不让出CPU执行时间,而是将自己的栈空间记录保存下来;而后,跳转到下一个代码片,当被调用唤醒后,原代码片恢复原来的执行上下文并获得调用结果。(典型的场景是用协程实现生产者消费者模型,Java有Quasar框架来实现协程)

2. 进程有哪些状态?

  • 运行态:占有CPU,在CPU上运行(每个CPU核心同一时刻只有一个进程处于运行态)。
  • 就绪态:已经具备运行条件,但没有空闲CPU,暂时不能运行(等待获取CPU资源)。
  • 阻塞态:因为某一事件而暂时不能运行(如等待I/O操作的结果,为提高CPU利用率,进程需要将其他所需资源分配到位才能得到CPU的服务)。
  • 创建态:在进程被创建的时候,操作系统为进程分配资源、初始化PCB。
  • 终止态:在进程被从系统中撤销,操作系统会回收进程拥有的资源、撤销PCB。

五状态转移图如下图所示:
进程状态的转换
当引入虚拟内存后,进程状态扩展为七种:关于进程调度参考此文章
进程七状态模型

3. 进程同步和线程同步分别有哪些方法?

(1)进程同步机制为了解决操作系统由于异步性导致的进程按不可预知的速度向前推进,需要同步机制实现进程间的制约。主要的方法是通过①信号量机制:设定一个记录型信号量,通过PV原语实现资源不足和足够的情况下进程阻塞和唤醒从而实现进程同步。②管程:一种特殊的软件模块,类似数据私有方法共有的类且同时只能有一个进程访问(Java中synchronized的实现就是类似于管程的实现)。③其他方法如:自旋锁、分布式系统(RPC)等。
(2)线程同步机制:线程同步可以联想到Java中锁的机制,主要方法是①互斥量:设定一个互斥对象,只有持有这个对象的线程才能访问共享资源(同步代码块、AQS中的state字段)。②信号量:设定同时访问共享资源的最大线程数量(实际实现也是信号量中的state字段)。③事件(Event) :wait/notify、park/unpark,通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较。

4. 进程与线程的通信方式有哪些?

进程的通信方式:

  • 共享存储:在内存中开辟一块空间,各进程互斥访问(使用信号量控制),可以避免管道和消息队列等方法从内核到用户空间的数据的四次拷贝(进程->内核->管道/队列->内核->进程),效率最高(两个进程通过将页表将虚拟地址映射到物理地址,物理地址上有一块公共的内存区域)。
  • 管道:在Linux实现中管道就是一个文件,半双工形式进行,写进程在写完后系统调用进程切换到读进程进行数据读取。(shell中的管道符号|,将前面命令的输出给到后面的命令)
  • 消息队列:消息队列是存放在内核中的消息链表。传输结构化数据克服管道只能承载无格式字节流以及缓冲区大小受限等缺点。
  • 信号量:信号量是一个计数器(常用记录型信号量,提供PV原语),多用于多进程对共享数据的互斥访问。
  • 信号:信号是Linux的系统中用于进程间互相通信或者操作的一种机制,比如kill -9终止某个进程(kill -系列就是信号)。
  • 套接字:主要用于跨计算机的进程通信。

线程的通信方式:

  • 文件共享:线程间通过写数据到文件与从文件中读取数据来实现通信。
  • 网络共享:通过网络编程的手段实现通过网络的线程通信。
  • 变量共享:共享内存(堆)中的同一个变量或者对象。
  • 线程协作API:wait/notify(同一对象的锁的持有者间调用,wait会释放对象锁,但对执行顺序还是有要求)、park/unpark(令牌许可机制,park等待令牌,unpark提供令牌,因此不要求两者的执行顺序,但是如果在同步代码中使用的话不会释放对象锁导致死锁发生)

5. 进程调度算法有哪些?

批处理系统采用的算法

算法思想是否可抢占优点缺点是否会产生饥饿
先来先服务(FCFS)作业/进程调度时,每次调度都是从后备队列/就绪队列中选择最早到达的作业/进程为其服务(作业调度将作业调入内存、分配资源、创建进程;进程调度为之分配处理机)非抢占式公平且实现简单对长作业有利,但对短作业不利
最短作业优先(SJF)作业/进程调度时,每次调度都是从后备队列/就绪队列中选择最短服务时间的作业/进程为其服务(对于进程调度时称为短进程优先SPF)SJF和SPF是非抢占式的,但有最短剩余时间优先算法(SRTN)是抢占式的平均周转时间短,进程等待时间短对短作业有利,但不利于长作业,短作业的频繁到来可能导致长作业的饥饿
高响应比优先(HRRN)作业/进程调度时,计算每个作业/进程的响应比,选择响应比最高的作业/进程为其服务非抢占式综合考虑了等待时间和运行时间,既考虑了作业的到达先后顺序,又能避免长作业不被响应的问题每次调度前需要计算响应比增加系统开销

交互式系统采用的算法:

算法思想是否可抢占优点缺点是否会饥饿补充说明
时间片轮转算法(RR)此算法只针对进程,以各个进程到达就绪队列的顺序(FCFS),轮流让各个进程执行一个固定大小的时间片。在时间片结束后,如果未执行完就立即剥夺处理机分配,重新进入就绪队列队尾等待抢占式(时钟中断)公平;响应快高频率的进程切换,系统开销较大;不区分任务紧急程度时间片大小对算法的效果影响较大,时间片过大算法就向FCFS算法趋近,而时间片过小则进程切换过于频繁,系统开销巨大
优先级调度算法调度时选择优先级最高的作业/进程进行调度,甚至还会用于I/O调度均有优先级区分了任务的重要程度;动态优先级调整可以灵活调整各作业/进程的偏好程度高优先级的进程源源不断到来会导致低优先级进程的饥饿进程的优先级设置中通常有静态优先级动态优先级
多级反馈队列调度算法(1)设置多级就绪队列,各级队列优先级从高到低,时间片从小到大;(2)新进程首先进入第1级队列,按FCFS等待被分配,若时间片结束未进程未结束,进入下一级队列队尾(如果已在最后一级则重新返回这级队列队尾);(3)只有k级及之前的所有队列未空时,才为k+1级队列队首进程分配时间片抢占式对各进程较为公平(FCFS);每个新到达的进程可以快速被响应(RR);短进程只需要较少时间就能完成(SPF) ;不必估计进程运行时间;灵活调整对进程的偏好可能会产生饥饿(源源不断的短进程进入)何理解灵活调整进程偏好:比如I/O密集型进程由于阻塞重新放回就绪队列,此时I/O进程仍然会被放入原队列保持较高优先级

多级反馈队列调度算法理解图:
多级反馈队列调度算法

6. 进程死锁与处理策略

死锁是指,在并发环境下,进程因为资源竞争所造成的相互等待对方所占有的资源,导致各进程都阻塞的现象(可以引申到Java并发)。死锁产生有下面四个必要条件

  • 资源互斥
  • 进程拥有资源不可抢占
  • 进程各自保持拥有资源并申请其他资源
  • 循环等待,多个进程形成资源循环等待环

处理策略主要是:预防死锁(破坏必要条件)、避免死锁(银行家算法使进程的资源请求在操作系统存在安全序列)、解除死锁(资源剥夺、终止进程、进程退回)。

7. 内存分页与分段

  • 分段与分页的对比
  1. 信息的物理单元对用户不可见,信息的逻辑单元对用户可见。
  2. 大小固定且由系统决定长度不固定且决定于用户编写的程序
  3. 分页用户进程地址是一维的(只需要一个逻辑地址即可推算),分段用户进程地址是二维的(需要给出段名和段内地址)。
  4. 分段更容易实现信息的共享和保护(只能共享不属于临界资源的代码),当需要让内存中的某个片段共享给多个进程,只需要将各个进程的段表指向同一个段即可。
  • 加快分页过程
  1. 如何提高逻辑地址和物理地址的映射速度?(快表)
      系统一旦访问了某一个页,就会在一段时间内稳定工作在这个页上(时间局部性)。所以为了提高访问页表的速度,计算机配备了一组能容纳部分页表的硬件寄存器。当系统再次需要将地址转换时,先访问这组硬件寄存器(即,快表)。
  2. 页表过大怎么解决?
      页表存在的问题是,页表必须连续存放在多个连续的页框中,页表过大时离散存储失去了其本质意义,所以可以再建一级索引(二级页表)来让原页表连续页表项分组离散存储,暂时不需要的页表可以存放在虚拟内存中。

8. 操作系统虚拟内存

操作系统中,为了拓展内存的存储空间,将部分磁盘的空间用于内存页的交换空间,Linux系统中的SWAP分区就是虚拟内存的实现。虚拟内存建立在内存分页或分段管理的基础上根据局部性原理,根据局部性原理,将程序暂时不用的内存页放在外存中,当程序需要时,内存中存在空闲页框就通过调页将需要的页加载入内存中(内存管理单元发现程序请求的逻辑地址对应页号的页未在内存中,产生缺页中断请求),如果内存中没有空闲页框,则通过页面置换交换将当前内存中符合页面置换算法(FIFO、LRU、LFU、时钟置换等)的页面放入外存(未被修改过的直接删除)

页面置换算法就是用于页面置换时,遵循哪种方法选择需要调出内存的页面

  • 最佳置换算法:每次选择淘汰的页面将是以后永不使用,或者在最长时间内不再被访问的页面,这样可以保证最低的缺页率。(无法实现)
  • 先进先出置换算法(FIFO):每次选择淘汰的页面是最在进入内存的页面。(算法性能差)
  • 最近最久未使用置换算法(LRU):淘汰最近最久未使用的页面。(算法性能好,但是需要特定硬件支持,ps:leetcode有算法题)
  • 最近使用最少置换算法(LFU):维护key对应的频率,每次置换掉频率最小的。
  • 时钟置换算法(NRU):简单的CLOCK算法的实现是为每个页面设置一个访问位,并将内存中的页面都通过链接指针链接成一个循环队列。当某页面被访问时,其访问位为1。当需要淘汰一个页面时,只需检查页的访问位。 如果是0,就选择该页换出;如果是1,则将它置为0,暂不换出,继续检查下一个页面,若第一轮扫描中所有页面都是1,则将这些页面的访问位依次置为0后,再进行第二轮扫描(第二轮扫描中一定会有访问位为0的页面,因此简单的CLOCK 算法选择一个淘汰页面最多会经过两轮扫描)

局部性原理

  • 时间局部性(缓存):如果执行了程序中某条指令,那么这条指令很可能再次执行;如果某个数据被访问过,那么不久后这个数据很可能被再次访问。(程序中的大量循环)
  • 空间局部性(预读):一旦程序访问了某个存储单元,不久后,其附近的存储单元也很可能被访问。(很多数据在内存中是连续存放的,指令也是顺序存放在内存中的)

二、Linux部分

1. Linux的I/O复用技术

这个博客老哥从浅至深总结的很好

注意:用户进程请求I/O在Linux中是缓存I/O机制,操作系统会将 I/O 的数据缓存在内存的页缓存中(Page Cache),也就是说,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间,这种方式对CPU和内存开销是比较大的。

几种I/O的方式:超详细总结

  • 阻塞式I/O:进程在系统调用后,从数据到达内核缓冲区到数据从内核缓冲区复制到应用进程缓冲区都是处于阻塞态的。
  • 非阻塞式I/O:进程在I/O系统调后,并不会阻塞,但需要不断进行系统调用询问I/O是否完成(轮询)。
  • I/O多路复用(select、poll、epoll):实际就是基于事件通知的I/O模型,单个进程可以处理多个I/O事件。
  • 异步I/O(AIO):用户进程发起I/O系统调用后,直接返回,内核完成所有操作直至数据拷贝到用户进程缓存中,这个是唯一一个完全不阻塞的I/O方式。(通过回调实现)

I/O复用的实现:
  select,poll,epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,一个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的(非阻塞式同步IO,内核缓冲中数据就绪后进程自己负责去读取)。

  • select
int select (int n, fd_set *readfds, fd_set *writefds, 
			fd_set *exceptfds, struct timeval *timeout);

  select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。有描述符就绪(有数据可读、可写、或者有异常,底层是数组的形式),调用select函数(阻塞),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以通过遍历fdset,来找到就绪的描述符。

select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上默认为1024,且监听事件类型较少。

  • poll
int poll (struct pollfd *fds, unsigned int nfds, int timeout);

struct pollfd {
   int fd; /* file descriptor */
   short events; /* requested events to watch */
   short revents; /* returned events witnessed */
};

poll函数运行的方法类似于select,但是用pollfd(链表)来记录监视文件的描述符。

总结select和poll的相似与区别

两个函数都是在返回后轮询文件描述符来获取就绪的socket。本质区别在于select的描述符类型结构是数组实现,监听描述符数量受限于FD_SETSIZE;poll的描述符类型使用链表实现,没有描述符数量限制。poll提供的事件类型更多,且描述符利用率上比select高。

  • epollepoll详解见此
      在高的并发访问情况下,select模型下由于描述符数量限制,需要开大量的进程进行监听;poll模型由于监听描述符过多,轮询链表会导致效率线性下降。同时上述两个方法存在大量内核到用户空间的数据拷贝和轮询的cpu占用,并发场景下效果不好。
      并且并发场景下,通常连接数总数远远大于活跃连接数,在select/poll时代,服务器进程每次都把这100万个连接告诉操作系统(从用户态复制句柄数据结构到内核态),让操作系统内核去查询这些套接字上是否有事件发生,轮询完后,再将句柄数据复制到用户态,让服务器应用程序轮询处理已发生的网络事件,这一过程资源消耗较大,因此,select/poll一般只能处理几千的并发连接。
      epoll通过在Linux内核中申请一个简易的文件系统(红黑树)。把原先的select/poll调用分成了3个部分:

    1)调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)
    2)调用epoll_ctl向epoll对象中添加所有连接的套接字
    3)调用epoll_wait收集发生的事件的连接

  当某个进程调用epoll_create方法后,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关:

struct eventpoll{
    ....
    /*红黑树的根节点,这颗树中存储着所有添加到epoll中的需要监控的事件*/
    struct rb_root  rbr;
    /*双链表中则存放着将要通过epoll_wait返回给用户的满足条件的事件*/
    struct list_head rdlist;
    ....
};

每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插入时间效率是O(logn),其中n为树的高度)。而所有添加到epoll中的事件都会与FD建立回调关系,也就是说,当相应的事件发生时会调用这个回调方法。这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中。

总结一下就是,epoll通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符放入链表中,当进程调用epoll_wait() 时便得到通知。
  相比于select/poll,epoll的优势就是无最大并发连接限制(文件描述符只受限于Linux打开文件最大数),同时回调和双向链表的检测就绪实现避免了遍历(活跃文件描述符才回调)。

LT(水平触发):进程通过epoll_wait()检测到事件到达时,收到通知,但进程可以不立即处理该事件,下次调用epoll_wait()还会再收到通知。
ET(边缘触发):进程收到通知后必须立即处理事件,减少了同一事件的触发次数,效率更高。

2. Linux的文件系统

Linux中有个重要的概念:一切都是文件。Linux常规的文件系统ext系列一个文件由inode(索引)和block(文件块)组成。

  • inode中存放权限、拥有者与群组、容量、修改时间、block对应地址等文件元数据信息。每个inode都有一个号码,操作系统用inode号来识别不同的文件。文件系统类型为ext4和xfs下inode大小为256k。(注:inode不保存对应文件名信息,这个对应关系保存在上级目录的block中)
  • block则是存放在磁盘中的文件块,一个文件按其大小至少有一个block,多个block根据链接分配、索引分配等方式管理block块。由于磁盘扇区一般小于block,所以一个block可能由多个连续扇区存储。
  • 磁盘盘区会划分为inode区和block区来分别存储文件inode和block。

关于block磁盘分配方式见此处

3. 硬链接和软链接的区别

  • 基于索引节点的共享方式(硬链接):多个用户文件名对应的inode指针可以指向同一个inode(Linux中两个不同文件名的inode节点号相同),并在索引节点中用Count记录文件共享用户数量。
  • 基于符号链的共享方式(软链接):多个用户文件名指向的不同的inode,block中通过其中记录的想要共享的文件存放路径来实现访问(Linux中文件名指向不同inode,但block中记录了共享文件的访问路径)。

4. 进程的产生

  在 Linux 系统中,进程通过非常简单的方式来创建,fork 系统调用会创建一个源进程的拷贝(副本)。调用 fork 函数的进程被称为父进程使用 fork 函数创建出来的进程被称为子进程。父进程和子进程都有自己的内存映像,父进程和子进程相互独立(但父子进程共享父进程创建子进程时打开的文件)。
  进程创建的过程:内核为子进程开辟一块新的用户空间的进程描述符,然后从父进程复制大量的内容。为这个子进程分配一个 PID,设置其内存映射,赋予它访问父进程文件的权限,注册并启动

5. 僵尸进程和孤儿进程

  • 孤儿进程:一个父进程退出后,它的子进程还在运行,这些子进程就成了孤儿进程。孤儿进程会被init进程(PID=1)收养,完成状态回收(init进程会循环调用wait()来检查孤儿进程是否需要被释放了)。
  • 僵尸进程:子进程执行完成后其进程描述符不会被立即释放,在子进程完成后(exit)会留下僵尸进程的数据结构,内核会给其父进程发送SIGCHLD信号告知其用wait()或waitpid()处理,而这个信号可能不会被立即响应,于是可能留下很多僵尸进程。在Linux中使用ps命令显示出来状态为Z,要清除大量僵尸进程可以通过kill -9僵尸进程的父进程让init进程来接管僵尸进程的状态回收。

6. Linux常用命令

  • 进程打开文件数量:lsof -p pid
  • 通过给内核发送指定的信号来操作进程:最经典的kill -9 pid来终止一个进程;
    kill -2 pid功能类似于Ctrl+C,是程序在结束之前,能够保存相关数据,然后再退出。
  • 显示文件或目录:ls (-l -a)-l是显示详细信息(ll呢?)
  • 创建目录:mkdir 文件名
  • 查看文件内容的几个命令:
  1. cat查看整个文件内容(输出可重定向)
  2. moreless分页显示文本文件内容
  3. tail -n 1000显示最后1000行、tail -n +1000显示1000行后的内容、tail -f动态显示文件
  4. head -n 1000显示前1000行
  5. sed -n '10,20p' 文件名查看文件中间10-20行的内容
  • 查看系统cpu与内存等情况:top,还可以-H -p pid看一个进程中的线程占用cpu情况用于配合jstack排查代码中的问题
  • 查看当前运行的进程:ps一般和管道与grep(ps au | grep)一起用来定向查看某个进程是否开启
  • 查看网络状态信息:netstat,一般也是与管道和grep来查看对应端口进程的状态
  • 磁盘状态查看命令:
  1. 查看所有磁盘使用情况:df
  2. 查看当前文件或目录占用的磁盘空间:du

参考

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值