本文 首发于 🍀 永浩转载 请注明 来源

线程与进程

一、进程

  1. 计算机内存空间

​ 用户空间装着用户进程需要使用的资源,比如你在程序代码里开一个数组, 这个数组肯定存在用户空间;内核空间存放内核进程需要加载的系统资源, 这一些资源一般是不允许用户访问的。但是注意有的用户进程会共享一些内 核空间的资源,比如一些动态链接库等等。

  1. 对于操作系统,进程就是一个数据结构,直接看 Linux 的源码:

    struct task_struct {
      		// 进程状态
          long state; 
      		// 虚拟内存结构体
          struct mm_struct *mm; 
      		// 进程号
          pid_t pid;
          // 指向父进程的指针
          struct task_struct __rcu *parent; 
          // 一个数组,包含该进程打开的文件指针 
      		struct files_struct *files;
    };
    

    ​ 其中比较有意思的是 mm 指针和 files 指针。

    ​ mm 指针指向:进程的虚拟内存,也就是载入资源和可执行文件的地方;

    ​ files 指针指向:一个数组,这个数组里装着所有该进程打开的文件的指针。

二、文件描述符

每个进程被创建时, files 的前三位被填入默认值,分别指向标准输入 流、标准输出流、标准错误流。我们常说的「文件描述符」就是指这个文件 指针数组的索引(0,1,2),所以程序的文件描述符默认情况下 0 是输入,1 是输出, 2 是错误。

​ linux一切皆文件,对于一般的计算机,输入流是键盘,输出流是显示器,错误流也是显示器, 所以现在这个进程和内核连了三根线。因为硬件都是由内核管理的,我们的 进程需要通过「系统调用」让内核进程访问硬件资源。

​ 如果我们写的程序需要其他资源,比如打开一个文件进行读写,这也很简 单,进行系统调用,让内核把文件打开,这个文件就会被放到 files 的第 4 个位置:

输入重定向:command < file.txt,file[0]指向file.txt,程序从file[0]读取数据

输出重定向:command > file.txt,file[1]指向file.txt, 程序像file[1]写入数据

管道符:cmd1 | cmd2 把一个进程的输出流和另一个进程的输入流接起 一条「管道」,数据就在其中传递

**注意:**一个简单的 files 数组,进程通过简单的文件描述符访问相应资源, 具体细节交于操作系统,有效解耦,优美高效。

三、线程是什么

​ 之所以Linux 中线程和进程基本没有区别呢,因为从 Linux 内核的角度来看,并没有把线程和进程区别对待。都是用 task_struct 结构表示的,唯一的 区别就是共享的数据区域不同

​ 换句话说,线程看起来跟进程没有区别,只是线程的某些数据区域和其父进 程是共享的,而子进程是拷⻉副本,而不是共享。就比如说, mm 结构 和 files 结构在线程中都是共享的,我画两张图你就明白了:

**注意:**对于新建进程时内存区域拷 ⻉的问题,Linux 采用了 copy-on-write 的策略优化,也就是并不真正复制父 进程的内存空间,而是等到需要写操作时才去复制。所以 Linux 中新建进 程和新建线程都是很迅速的