Linux你必须知道的小知识-17

80 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天,点击查看活动详情

2.50 说说线程池的设计思路,线程池中线程的数量由什么确定?

参考回答

  1. 设计思路

    实现线程池有以下几个步骤: (1)设置一个生产者消费者队列,作为临界资源。

    (2)初始化n个线程,并让其运行起来,加锁去队列里取任务运行

    (3)当任务队列为空时,所有线程阻塞。

    (4)当生产者队列来了一个任务后,先对队列加锁,把任务挂到队列上,然后使用条件变量去通知阻塞中的一个线程来处理。

  2. 线程池中线程数量

    线程数量和哪些因素有关:CPU,IO、并行、并发

    如果是CPU密集型应用,则线程池大小设置为:CPU数目+1 如果是IO密集型应用,则线程池大小设置为:2*CPU数目+1 最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目
    

    所以线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。

答案解析

  1. 为什么要创建线程池

    创建线程和销毁线程的花销是比较大的,这些时间有可能比处理业务的时间还要长。这样频繁的创建线程和销毁线程,再加上业务工作线程,消耗系统资源的时间,可能导致系统资源不足。同时线程池也是为了提升系统效率。

  2. 线程池的核心线程与普通线程:

    任务队列可以存放100个任务,此时为空,线程池里有10个核心线程,若突然来了10个任务,那么刚好10个核心线程直接处理;若又来了90个任务,此时核心线程来不及处理,那么有80个任务先入队列,再创建核心线程处理任务;若又来了120个任务,此时任务队列已满,不得已,就得创建20个普通线程来处理多余的任务。 以上是线程池的工作流程。

2.51 进程和线程相比,为什么慢?

参考回答

  1. 进程系统开销显著大于线程开销;线程需要的系统资源更少。
  2. 进程切换开销比线程大。多进程切换时需要刷新TLB并获取新的地址空间,然后切换硬件上下文和内核栈;多线程切换时只需要切换硬件上下文和内核栈。
  3. 进程通信比线程通信开销大。进程通信需要借助管道、队列、共享内存,需要额外申请空间,通信繁琐;而线程共享进程的内存,如代码段、数据段、扩展段,通信快捷简单,同步开销更小。

2.52 简述Linux零拷贝的原理?

参考回答

  1. 什么是零拷贝

    所谓「零拷贝」描述的是计算机操作系统当中,CPU不执行将数据从一个内存区域,拷贝到另外一个内存区域的任务。通过网络传输文件时,这样通常可以节省 CPU 周期和内存带宽。

  2. 零拷贝的好处

    (1)节省了 CPU 周期,空出的 CPU 可以完成更多其他的任务

    (2)减少了内存区域之间数据拷贝,节省内存带宽

    (3)减少用户态和内核态之间数据拷贝,提升数据传输效率

    (4)应用零拷贝技术,减少用户态和内核态之间的上下文切换

  3. 零拷贝原理

    在传统 IO 中,用户态空间与内核态空间之间的复制是完全不必要的,因为用户态空间仅仅起到了一种数据转存媒介的作用,除此之外没有做任何事情。

    (1)Linux 提供了 sendfile() 用来减少我们的数据拷贝和上下文切换次数。

    过程如图:

    a. 发起 sendfile() 系统调用,操作系统由用户态空间切换到内核态空间(第一次上下文切换)

    b. 通过 DMA 引擎将数据从磁盘拷贝到内核态空间的输入的 socket 缓冲区中(第一次拷贝)

    c. 将数据从内核空间拷贝到与之关联的 socket 缓冲区(第二次拷贝)

    d. 将 socket 缓冲区的数据拷贝到协议引擎中(第三次拷贝)

    e. sendfile() 系统调用结束,操作系统由用户态空间切换到内核态空间(第二次上下文切换)

    根据以上过程,一共有 2 次的上下文切换,3 次的 I/O 拷贝。我们看到从用户空间到内核空间并没有出现数据拷贝,从操作系统角度来看,这个就是零拷贝。内核空间出现了复制的原因: 通常的硬件在通过DMA访问时期望的是连续的内存空间。

    (2)mmap 数据零拷贝原理

    如果需要对数据做操作,Linux 提供了mmap 零拷贝来实现。