八股文(Linux&操作系统)

文章目录

  • 一. linux常用命令
    • 1. 命令汇总
    • 2. 高频考题
  • 二. 进程 线程 协程
  • 三. 零拷贝
    • 1. DMA(直接内存访问)技术 **
    • 2. 传统的文件传输
    • 3. 如何优化文件传输性能
    • 4. 如何实现零拷贝?
    • 5. 使用零拷贝的项目
    • 6. PageCache(磁盘高速缓存)
  • 四. IO多路复用:epoll poll select
    • 1. 基于TCP的Socket编程
    • 2.多进程模型
    • 3. 多线程模型
    • 4. I/O多路复用
    • 5. select/poll
    • 6. epoll
      • 6.1 基本定义用法
      • 6.2 边缘触发和水平触发
  • 五. 进程间的通信方式
    • 1. 管道
    • 2. 消息队列
    • 3. 共享内存
    • 4. 信号量
    • 5. 信号
    • 6. Socket
  • 六. 中断
    • 1. 中断的定义

一. linux常用命令

1. 命令汇总

1. ps命令

(1) 显示当前所有进程环境变量及进程间关系
1 ps -ef
(2) 显示当前所有进程
1 ps -A
(3) 与grep联用查找某进程
1 ps -aux |grep apache
(4) 找出与 cron 与 syslog 这两个服务有关的 PID 号码
1 ps aux |grep'(cron|syslog)'

八股文(Linux&操作系统)_第1张图片
2. 网络通信命令
查看当前系统的网卡信息:ifconfig或者ip
查看与某台机器的连接情况:ping (ping 不通服务器并不代表 HTTP 请求也不通,因为有的服务器的防火墙是会禁用 ICMP 协议的)
查看当前系统的端口使用:netstat -an
3. pwd
显示当前所在位置
4. socket信息
netstart 或者 ss
5. 吞吐量和PPS
sar -n DEV,显示网口的统计数据;
sar -n EDEV,显示关于网络错误的统计数据;
sar -n TCP,显示 TCP 的统计数据

pps 全称是 Packet Per Second(包 / 秒),表示以网络包为单位的传输速率,一般用来评估系统对于网络的转发能力。

2. 高频考题

查看当前进程:ps
查看当前路径: pwd
显示文件或目录: ls -l(列出详细信息) -a(列出所有,包括隐藏文件)
查看文件内容:cat
分页查看文件内容:more less
查看CPU状态: top

二. 进程 线程 协程

  • 进程
    进程是系统进行资源分配和调度的一个独立单位,是系统中的并发执行的单位。
  • 线程
    线程是进程的一个实体,也是 CPU 调度和分派的基本单位,它是比进程更小的能独立运行的基本单位,有时又被称为轻权进程或轻量级进程
  • 协程
    是一种比线程更加轻量级的存在,协程不是被操作系统内核所管理,而完全是由程序所控制
    八股文(Linux&操作系统)_第2张图片

三. 零拷贝

1. DMA(直接内存访问)技术 **

  • 没有DMA时,I/O 的过程

八股文(Linux&操作系统)_第3张图片

  • 使用了DMA技术
    八股文(Linux&操作系统)_第4张图片
  • 定义
    在进行 I/O 设备和内存的数据传输的时候,数据搬运的工作全部交给 DMA 控制器,而 CPU 不再参与任何与数据搬运相关的事情,这样 CPU 就可以去处理别的事务。

2. 传统的文件传输

八股文(Linux&操作系统)_第5张图片

  • 四次用户态与内核态的上下文切换
    因为发生了两次系统调用,一次是 read() ,一次是 write(),每次系统调用都得先从用户态切换到内核态,等内核完成任务后,再从内核态切换回用户态。
  • 四次数据拷贝
    两次是 DMA 的拷贝,另外两次则是通过 CPU 拷贝的

    要想提高文件传输的性能,就需要减少「用户态与内核态的上下文切换」和「内存拷贝」的次数。

3. 如何优化文件传输性能

要想减少上下文切换到次数,就要减少系统调用的次数。 用户的缓冲区是没有必要存在的。

八股文(Linux&操作系统)_第6张图片

4. 如何实现零拷贝?

  • 方式一: mmap + write
buf = mmap(file, len);
write(sockfd, buf, len);
mmap() 系统调用函数会直接把内核缓冲区里的数据「映射」到用户空间,这样,操作系统内核与用户空间就不需要再进行任何的数据拷贝操作。

八股文(Linux&操作系统)_第7张图片
三次数据拷贝,四次上下文切换

  • 方式二:sendfile
    以linux内核2.4版本为例子,2.1(没有这么完善),2.4支持网卡支持SG-DMA技术
    八股文(Linux&操作系统)_第8张图片
  • 总结
    零拷贝(Zero-copy)技术,因为我们没有在内存层面去拷贝数据,也就是说全程没有通过 CPU 来搬运数据,所有的数据都是通过 DMA 来进行传输的。
    零拷贝技术的文件传输方式相比传统文件传输的方式,减少了 2 次上下文切换和数据拷贝次数,只需要 2 次上下文切换和数据拷贝次数,就可以完成文件的传输,而且 2 次的数据拷贝过程,都不需要通过 CPU,2 次都是由 DMA 来搬运。

5. 使用零拷贝的项目

Kafka ,Nginx

6. PageCache(磁盘高速缓存)

  • 优点弊端
    读写磁盘相比读写内存的速度慢太多了,所以我们应该想办法把「读写磁盘」替换成「读写内存」。于是,我们会通过 DMA 把磁盘里的数据搬运到内存里,这样就可以用读内存替换读磁盘。
    但是,内存空间远比磁盘要小,内存注定只能拷贝磁盘里的一小部分数据
    读写速度快 但内存小
  • 两大功能
    缓存最近被访问的数据; (LRU算法)
    预读功能;
  • 对于大文件的处理
    另外,当传输大文件时,不能使用零拷贝,因为可能由于 PageCache 被大文件占据,而导致「热点」小文件无法利用到 PageCache,并且大文件的缓存命中率不高,这时就需要使用异步 IO + 直接 IO 的方式。

    sendfile 系统调用实现了零拷贝技术,零拷贝技术的文件传输方式相比传统文件传输的方式,减少了 2 次上下文切换和数据拷贝次数,只需要 2 次上下文切换和数据拷贝次数,就可以完成文件的传输,而且 2 次的数据拷贝过程,都不需要通过 CPU,2 次都是由 DMA 来搬运,使用零拷贝的项目有nginx、kafka。

四. IO多路复用:epoll poll select

1. 基于TCP的Socket编程

八股文(Linux&操作系统)_第9张图片

  • 服务端:socket()函数 创建网络协议为 IPv4,以及传输协议为 TCP 的 Socket
  • 服务端: bind()函数 给这个 Socket 绑定一个 IP 地址(找网卡)和端口(找应用程序)
  • 服务端:listen()函数:监听
  • 服务端: accept() 函数 从内核获取客户端的连接,如果没有客户端连接,则会阻塞等待客户端连接的到来
  • 客户端: connect() 函数发起连接,该函数的参数要指明服务端的 IP 地址和端口号,然后万众期待的 TCP 三次握手就开始了
  • 当 TCP 全连接队列不为空后,服务端的 accept() 函数,就会从内核中的 TCP 全连接队列里拿出一个已经完成连接的 Socket 返回应用程序,后续数据传输都用这个 Socket。
    八股文(Linux&操作系统)_第10张图片

2.多进程模型

八股文(Linux&操作系统)_第11张图片
服务器的主进程负责监听客户的连接,一旦与客户端连接完成,accept() 函数就会返回一个「已连接 Socket」,这时就通过 fork() 函数创建一个子进程,实际上就把父进程所有相关的东西都复制一份,包括文件描述符、内存地址空间、程序计数器、执行的代码等。

  • 存在的问题:僵尸进程
    当「子进程」退出时,实际上内核里还会保留该进程的一些信息,也是会占用内存的,如果不做好“回收”工作,就会变成僵尸进程,随着僵尸进程越多,会慢慢耗尽我们的系统资源。父进程要“善后”好自己的孩子,怎么善后呢?那么有两种方式可以在子进程退出后回收资源,分别是调用 wait() 和 waitpid() 函数。

3. 多线程模型

八股文(Linux&操作系统)_第12张图片
当服务器与客户端 TCP 完成连接后,通过 pthread_create() 函数创建线程,然后将「已连接 Socket」的文件描述符传递给线程函数,接着在线程里和客户端进行通信,从而达到并发处理的目的。并且使用线程池的方式来避免线程的频繁创建和销毁。

4. I/O多路复用

只使用一个进程来维护多个 Socket
八股文(Linux&操作系统)_第13张图片
一个进程虽然任一时刻只能处理一个请求,但是处理每个请求的事件时,耗时控制在 1 毫秒以内,这样 1 秒内就可以处理上千个请求,把时间拉长来看,多个请求复用了一个进程,这就是多路复用
select/poll/epoll 内核提供给用户态的多路复用系统调用,进程可以通过一个系统调用函数从内核中获取多个事件
select/poll/epoll 是如何获取网络事件的呢?在获取事件时,先把所有连接(文件描述符)传给内核,再由内核返回产生了事件的连接,然后在用户态中再处理这些连接对应的请求即可

5. select/poll

  • select实现多路复用的方式
    将已连接的 Socket 都放到一个文件描述符集合,然后调用 select 函数将文件描述符集合拷贝到内核里,让内核来检查是否有网络事件产生,检查的方式很粗暴,就是通过遍历文件描述符集合的方式,当检查到有事件产生后,将此 Socket 标记为可读或可写, 接着再把整个文件描述符集合拷贝回用户态里,然后用户态还需要再通过遍历的方法找到可读或可写的 Socket,然后再对其处理。
    2 次「遍历」文件描述符集合
    2 次「拷贝」文件描述符集合

  • select与poll对比

  • 区别:用来表示文件描述符集合的数据结构
    select 使用固定长度的 BitsMap,表示文件描述符集合
    poll 用动态数组,以链表形式来组织,突破了 select 的文件描述符个数限制,当然还会受到系统文件描述符限制。

  • 相同
    都是使用「线性结构」存储进程关注的 Socket 集合,因此都需要遍历文件描述符集合来找到可读或可写的 Socket,时间复杂度为 O(n),而且也需要在用户态与内核态之间拷贝文件描述符集合,这种方式随着并发数上来,性能的损耗会呈指数级增长

6. epoll

6.1 基本定义用法

int s = socket(AF_INET, SOCK_STREAM, 0);
bind(s, ...);
listen(s, ...)

int epfd = epoll_create(...);
epoll_ctl(epfd, ...); //将所有需要监听的socket添加到epfd中

while(1) {
    int n = epoll_wait(...);
    for(接收到数据的socket){
        //处理
    }
}

八股文(Linux&操作系统)_第14张图片

  • 对select和poll的改进
  • epoll 在内核里使用红黑树来跟踪进程所有待检测的文件描述字,把需要监控的 socket 通过 epoll_ctl() 函数加入内核中的红黑树里,红黑树是个高效的数据结构,增删改一般时间复杂度是 O(logn)。
  • epoll 使用事件驱动的机制,内核里维护了一个链表来记录就绪事件,当某个 socket 有事件发生时,通过回调函数内核会将其加入到这个就绪事件列表中,当用户调用 epoll_wait() 函数时,只会返回有事件发生的文件描述符的个数,不需要像 select/poll 那样轮询扫描整个 socket 集合,大大提高了检测的效率。

6.2 边缘触发和水平触发

epoll 支持两种事件触发模式,分别是边缘触发(edge-triggered,ET)和水平触发(level-triggered,LT)。

  • 边缘触发
    使用边缘触发模式时,当被监控的 Socket 描述符上有可读事件发生时,服务器端只会从 epoll_wait 中苏醒一次,即使进程没有调用 read 函数从内核读取数据,也依然只苏醒一次,因此我们程序要保证一次性将内核缓冲区的数据读取完;
  • 水平触发
    使用水平触发模式时,当被监控的 Socket 上有可读事件发生时,服务器端不断地从 epoll_wait 中苏醒,直到内核缓冲区数据被 read 函数读完才结束,目的是告诉我们有数据需要读取;
    在这里插入图片描述

五. 进程间的通信方式

1. 管道

  • 引入
$ ps auxf | grep mysql

上面命令行里的「|」竖线就是一个管道,它的功能是将前一个命令(ps auxf)的输出,作为后一个命令(grep mysql)的输入,从这功能描述,可以看出管道传输数据是单向的,如果想相互通信,我们需要创建两个管道才行。
管道这种通信方式效率低,不适合进程间频繁地交换数据

  • 管道基本原理

八股文(Linux&操作系统)_第15张图片
所谓的管道,就是内核里面的一串缓存。从管道的一段写入的数据,实际上是缓存在内核中的,另一端读取,也就是从内核中读取这段数据。另外,管道传输的数据是无格式的流且大小受限。

  • 父子进程管道
    八股文(Linux&操作系统)_第16张图片
    使用 fork 创建子进程,创建的子进程会复制父进程的文件描述符,这样就做到了两个进程各有两个「 fd[0] 与 fd[1]」,两个进程就可以通过各自的 fd 写入和读取同一个管道文件实现跨进程通信了。
    八股文(Linux&操作系统)_第17张图片
    管道只能一端写入,另一端读出,父进程关闭读取的 fd[0],只保留写入的 fd[1];子进程关闭写入的 fd[1],只保留读取的 fd[0];
    八股文(Linux&操作系统)_第18张图片
  • 匿名管道与命名管道

匿名管道顾名思义,它没有名字标识,匿名管道是特殊文件只存在于内存,没有存在于文件系统中,shell 命令中的「|」竖线就是匿名管道,通信的数据是无格式的流并且大小受限,通信的方式是单向的,数据只能在一个方向上流动,如果要双向通信,需要创建两个管道,再来匿名管道是只能用于存在父子关系的进程间通信匿名管道的生命周期随着进程创建而建立,随着进程终止而消失。

命名管道FIFO突破了匿名管道只能在亲缘关系进程间的通信限制,因为使用命名管道的前提,需要在文件系统创建一个类型为 p 的设备文件,那么毫无关系的进程就可以通过这个设备文件进行通信。另外,不管是匿名管道还是命名管道,进程写入的数据都是缓存在内核中,另一个进程读取数据时候自然也是从内核中获取,同时通信数据都遵循先进先出原则,不支持 lseek 之类的文件定位操作。

2. 消息队列

消息队列克服了管道通信的数据是无格式的字节流的问题,消息队列实际上是保存在内核的「消息链表」,消息队列的消息体是可以用户自定义的数据类型,发送数据时,会被分成一个一个独立的消息体,当然接收数据时,也要与发送方发送的消息体的数据类型保持一致,这样才能保证读取的数据是正确的。消息队列通信的速度不是最及时的,毕竟每次数据的写入和读取都需要经过用户态与内核态之间的拷贝过程。

3. 共享内存

八股文(Linux&操作系统)_第19张图片
共享内存可以解决消息队列通信中用户态与内核态之间数据拷贝过程带来的开销,它直接分配一个共享空间,每个进程都可以直接访问
带来新的问题,多进程竞争同个共享资源会造成数据的错乱

4. 信号量

为了防止多进程竞争共享资源,而造成的数据错乱,所以需要保护机制使得共享的资源,在任意时刻只能被一个进程访问。正好,信号量就实现了这一保护机制。

信号量其实是一个整型的计数器,主要用于实现进程间的互斥与同步,而不是用于缓存进程间通信的数据。
八股文(Linux&操作系统)_第20张图片

  • 一个是 P 操作,这个操作会把信号量减去 1,相减后如果信号量 < 0,则表明资源已被占用,进程需阻塞等待;相减后如果信号量 >=
    0,则表明还有资源可使用,进程可正常继续执行。
  • 另一个是 V 操作,这个操作会把信号量加上 1,相加后如果信号量 <=
    0,则表明当前有阻塞中的进程,于是会将该进程唤醒运行;相加后如果信号量 > 0,则表明当前没有阻塞中的进程;
  • 信号初始化为 1,就代表着是互斥信号量,它可以保证共享内存在任何时刻只有一个进程在访问,这就很好的保护了共享内存。
  • 信号初始化为 0,就代表着是同步信号量,它可以保证进程 A 应在进程 B 之前执行

5. 信号

上面说的进程间通信,都是常规状态下的工作模式。对于异常情况下的工作模式,就需要用「信号」的方式来通知进程。

信号是进程间通信机制中唯一的异步通信机制,因为可以在任何时候发送信号给某一进程,一旦有信号产生,我们就有下面这几种,用户进程对信号的处理方式。
信号事件的来源主要有硬件来源(如键盘 Cltr+C )和软件来源(如 kill 命令)。
1.执行默认操作。Linux 对每种信号都规定了默认操作,例如,上面列表中的 SIGTERM 信号,就是终止进程的意思。
2.捕捉信号。我们可以为信号定义一个信号处理函数。当信号发生时,我们就执行相应的信号处理函数。
3.忽略信号。当我们不希望处理某些信号的时候,就可以忽略该信号,不做任何处理。有两个信号是应用进程无法捕捉和忽略的,即 SIGKILL 和 SEGSTOP,它们用于在任何时候中断或结束某一进程。

6. Socket

跨网络与不同主机上的进程之间通信,就需要 Socket 通信了。

六. 中断

1. 中断的定义

在计算机中,中断是系统用来响应硬件设备请求的一种机制,操作系统收到硬件的中断请求,会打断正在执行的进程,然后调用内核中的中断处理程序来响应请求。
中断是一种异步的事件处理机制,可以提高系统的并发处理能力

你可能感兴趣的:(八股文,linux,服务器,运维)