Linux系统基本介绍!详细解析Linux系统中的进程,线程和文件描述符

692 阅读4分钟

这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战

基本概念

  • Linux中,进程就是一个数据结构
  • 理解文件描述符,重定向,管道命令的底层工作原理,就可以从操作系统的角度理解Linux中的线程和进程是没有区别的

进程

计算机结构

  • 计算机结构:
    • 内存空间:
      • 上半部表示用户空间
      • 下半部表示内存空间
    • 进程
    • 磁盘
    • 输入输出设备
  • 用户空间: 装着用户进程需要使用的资源
  • 内核空间: 存放内核进程需要加载的系统资源,这些资源一般是不允许用户访问的. 但是有的用户进程会共享一些内核空间的资源,比如一些动态链接库等
  • 编译好的可执行程序只是一个文件. 不是进程,可执行文件必须要载入内存,包装成一个进程才能运行
  • 进程: 是要依靠操作系统创建的,每个进程都有固有属性
    • 进程号PID
    • 进程状态
    • 打开的文件
      • 进程创建好后,读入程序,程序才会被系统执行
  • 对于操作系统,进程就是一个数据结构:
struct task_struct {
	// 进程状态
	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_structLinux对于一个进程的描述,称为进程描述符
  • **mm: 指向的是进程的虚拟内存,即载入资源和可执行文件的位置
  • *files: 指向一个存放所有该进程打开的文件指针的数组

文件描述符

  • *files:
    • 文件指针数组
    • 通常情况下,一个进程会从files[0] 读取输入,将输出写入files[1], 将错误信息写入files[2]
  • 文件描述符:
    • 每个进程被创建时 ,file的前三位被填入默认值,分别指向标准输入流,标准输出流,标准错误流
    • 文件描述符就是指这个数组的索引
    • 程序的文件描述符默认情况下0是输入 ,1是输出 ,2是错误
  • 默认情况下,计算机的输入流是键盘,输出流是显示器,错误流也是显示器.进程通过系统调用让内核进程访问硬件资源.在Linux中,一切皆文件,设备也是文件,可以进行读和写. 比如当前进程如果需要调用其余资源,则需要进行系统调用,让内核将文件打开,这个文件会被放到files的第4个位置
  • 输入重定向:
    • 程序读取数据会从files[0] 中读取
    • 只要将files[0] 指向一个文件,而不是键盘
    • 程序就会从这个文件中读取数据,而不是键盘
$ command < file.txt
  • 输出重定向:
    • 程序会将数据写入到files[1] 中输出
    • 只要将files[1] 指向一个文件,而不是显示器
    • 程序就会将数据写入到这个文件中,而不会写入到显示器
$ command > file.txt
  • 错误重定向:
    • 程序会将数据写入到files[2] 中输出
    • 只要将files[2] 指向一个文件,而不是显示器
    • 程序就会将数据写入到这个文件中,而不会写入到显示器
$ command > file.txt
  • 管道符: 将一个进程的输出流和另一个进程的输入流接起一条 [管道], 数据就在这条管道中进行传递
$ cmd1 | cmd2 | cmd3
  • Linux中一切皆文件:
    • 不管是设备,进程 ,socket套接字还是真正的文件,全部都可以读写,统一装进一个简单的files数组
    • 进程通过简单的文件描述符访问相应的资源. 具体细节交于操作系统,能够实现有效解耦,优美高效

线程

  • 多线程和多进程都是并发,都可以提高处理器的利用效率
  • Linux中线程和进程基本没有区别:Linux内核角度来说,线程和进程没有区别对待
    • 系统调用函数pthread() 可以新建一个线程. 系统调用fork() 可以新建一个子进程
    • 无论是线程还是进程,都是用task_struct结构表示的,唯一的区别就是共享的数据区域不同
      • 线程基本上和进程没有区别,只是线程的某些数据区域和父进程是共享的,子进程是拷贝数据,而不是共享
    • 多线程程序要利用锁机制,避免多个线程同时同时向同一区域写入数据,否则会造成数据错乱
  • 问题: 既然进程和线程基本没有区别,并且多进程数据不共享,就不会存在数据错乱的问题,为什么多线程的使用比多进程要更加普遍呢?
    • 因为在现实场景中,数据共享的并发更加普遍
  • 只有Linux系统将线程看作是共享数据的进程,不做特殊对待. 其余操作系统是对线程和进程区别对待的,线程和进程各自持有特殊的数据结构
  • Linux中新建线程和进程的效率都是很高的:
    • 对于新建进程时内存区域拷贝的问题
    • Linux采用copy-on-write策略优化
    • 即并不真正复制父进程的内存区域空间,而是等到写操作时才去复制
    • 所以,在Linux中新建线程和进程都是很迅速的