捋一捋进程、线程、线程池的概念

一次面试中,面试官问:进程、线程和线程池了解过么?

我:进程和线程了解过,线程池不太了解。

然后就没有然后了。。。

还是得把基本概念了然于胸才行,下次才能给面试官扯清楚了!

一、进程

进程的概念

进程(process),是指计算机中已运行的程序,是操作系统中分配资源的最小单位。但是进程不是基本的运行单位(线程才是),而是线程的容器

当我们用 C 语言写一个 hello 程序,编译后得到可执行文件a.out,在终端输入./a.out,然后回车,这就等同于我们下达了运行程序的命令,就会产生进程。进程是一个活动的实体,它需要一些资源才能够完成工作,比如CPU使用时间、存储器、文件以及I/O设备等。这里就可以理解前面说的进程就是分配系统资源的最小单位。每个进程都有自己的地址空间,具体看一下进程的存储空间分布情况,下图是32位Inter x86的Linux中典型的存储空间布局,主要有四部分组成:

  • 正文段:存储CPU执行的机器指令
  • 数据段
    • 初始化的数据
    • 未初始化的数据(也被称为bss段)
  • 堆栈段
    • 堆:分配的动态内存就存放在堆区
    • 栈:函数调用所需要的信息存放在这一部分

在这里插入图片描述

为什么要有进程?

最开始,编程是通过在纸带上打孔,然后将纸带输入计算机进行运行,一个纸带读完了才能读下一个纸带。为了解决这种低效率的排队等候问题,有人发明了批处理系统。将程序(也就是任务)成批地提交给计算机,由计算机自动完成后再输出结果,从而减少作业建立和结束过程中的时间浪费。后来,汇编语言出现,就不再用二进制编码了。但是也有问题产生,当一个程序运行的时候,会一直占用CPU的资源,如果这个程序的大部分时间都在执行IO密集型任务(比如网络IO、磁盘IO等操作,这种任务比较耗时,但CPU消耗不大,因为操作IO的速度远低于CPU和内存的速度),该程序霸占着CPU会造成资源的浪费,此时如果将CPU让给其他的程序使用,等到当前的程序完成数据的接收,再将CPU的资源交还回来,这样就达到了资源的最大利用。但是如何才能解决这个问题呢?

核心也就是如何解决管理和控制不同程序间的计算机资源?解决方案:定义最小的资源分配单位,也就是进程,这也是为什么说进程是分配资源的最小单位。有了进程概念后,每个要运行的程序都分配指定的资源,这样将资源细分化,使得资源被充分地利用,多个程序分时共享硬件和软件资源,因为速度很快,对于我们用户,就感觉多个程序在同时执行。

二、线程

线程的概念

线程(thread)是操作系统能够进行运算调度的最小单位。大部分情况下,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。(摘自wiki

为什么要有线程呢?

进程能有效提升CPU的利用率。但进程间依然有资源利用优化空间,以及进程间通信的麻烦问题。多线程则可以共享同一进程地址空间上的资源,能在资源的空闲时刻更好的利用,且不存在进程间通信的麻烦。

线程和进程在Linux内核如何实现的?

Linux系统是如何创建进程的呢?对于操作系统,进程就是一个数据结构,我们直接来看 Linux 的源码(include/linux/sched.h 632行 ):

struct task_struct {
    // 进程状态
    volatile long			state;
    // 虚拟内存结构体
    struct mm_struct  *mm;
    // 进程号
    pid_t              pid;
    // 指向父进程的指针
    struct task_struct __rcu  *parent;
    // 子进程列表
    struct list_head        children;
    // 存放文件系统信息的指针
    struct fs_struct        *fs;
    // 一个数组,包含该进程打开的文件指针
    struct files_struct        *files;
};

task_struct就是 Linux 内核对于一个进程的描述,也可以称为「进程描述符」。mm指向的是进程的虚拟内存,也就是载入资源和可执行文件的地方;files指针指向一个数组,这个数组里装着所有该进程打开的文件的指针。每个进程被创建时,files的前三位被填入默认值,分别指向标准输入流(files[0])、标准输出流(files[1])、标准错误流(files[2])。

首先要明确的是,多进程和多线程都是并发,可以提高处理器的利用效率。我们知道系统调用fork()可以新建一个进程,函数pthread()可以创建一个线程。但是进程和线程都是用task_struct结构体表示的,他们最大的不同就是共享的内存区域不同。

也就是说创建的子进程,会拷贝一份父进程的内存资源(个人感觉这里有点深拷贝的意思);然后创建的子线程,共享父进程的那些内存资源(浅拷贝,父进程和子线程都有指针指向同一块内存区域)。比如mmfiles都是在线程中共享的。画张图就明白了。

在这里插入图片描述
在这里插入图片描述

三、线程池

线程池(thread pool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。

任务调度以执行线程的常见方法是使用同步队列,称作任务队列。池中的线程等待队列中的任务,并把执行完的任务放入完成队列中。(摘自wiki

为什么要有线程池?

因为有些场景需要频繁地创建线程和销毁线程,这样使得系统的压力很大。线程池可以在初始化的时候批量创建线程,然后通过队列等方式提交业务逻辑,线程池中的线程进行逻辑的消费工作,可以在操作的过程中降低线程创建和销毁的开销,但是调度的开销还是存在的。

参考文章

linux进程、线程、线程池和协程的由来

Linux的进程、线程、文件描述符是什么

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章