20分钟入门|Unix系统与设计哲学——程序员应该知道的那些事儿

Unix

写在前面

这是一篇一直想写的东西,在上一份工作的时候给新员工培训,我就发现不止新入职的员工,很多多年工作经验的同事也对Unix系统和shell知之甚少,然而这些知识可以更好点帮你认识你所使用的系统原理、语言设计思想、提高工作效率。

整篇文章包含两部分,这是上半部分,主要介绍Unix系统和它的特性Unix设计哲学以及Unix树形文件系统结构

全文1.2w字,大部分都是我个人这些年使用类Unix系统的理解和总结,适合开发、测试同学阅读。

阅读完上下整篇文章,你会:

  • ✅ 了解Unix系统特性与设计思想,了解Unix系统目录结构,以及各个目录功能。
  • ✅ 了解什么是终端和Shell,以及关于环境变量。
  • ✅ 获得一些实用的居家旅行、高效工作的 装X技巧 shell和终端使用技巧。

常见操作系统

目前市面上常见的电脑操作系统就这三种了——Windows、MacOS和Linux。

大多数人一开始接触电脑时使用的应该都是微软的Windows系统;后来有一部分人先富起来了,就接触到了MacOS。

然而很少一部分人才会接触到Linux,这部分人大概率是服务端开发、运维人员,或者是嵌入式开发、内核工程师,但是真正拿Linux系统作为日常工作生活的主力系统的人更少之又少。

我算是其中一位,上学与工作期间使用Linux作为主力系统,换Mac也是最近些年的事情。

我最初投向Linux怀抱的原因很好笑,就是单纯的打dota激战正酣的时候,windows蓝屏了,然后我一气之下就把双系统windows的盘符格式化了。😂😂😂

Linux、MacOS与Unix

Linux,其实严格上来说,只是指操作系统内核。现在各种服务器和个人电脑中使用的是各种各样Linux发行版,也就是内核外面套着的各种界面和样式壳子。

MacOS则是苹果公司推出的具有友好且优美图形界面的操作系统,从一开始开放硬件到现在的专属系统,不管怎么样,B格一定拉满。

在我看来,Linux和Mac其实是一回事,都是一种类Unix的操作系统。

题外话:

就像现在经常听到的讨论鸿蒙OS是不是Android一样,在我看来,鸿蒙OS和Android都是一种Linux,Android在Linux上打了补丁套了一层虚拟机、framework和UI;鸿蒙OS是在Linux上打了补丁,套了一层虚拟机、自己的和Android的framework和UI。

那么问题来了,Unix是啥

Unix系统是贝尔实验室研究开发的一套多用户、多任务、多层次(multi-user、multi-processor、multi-level)、支持多种处理器架构的分时操作系统,可以说是现代操作系统的起源。

目前它的商标权由国际开放标准组织所拥有,只有符合单一UNIX规范的UNIX系统才能使用UNIX这个名称,否则只能称为类UNIX(UNIX-like)。

当然Unix也不是凭空出现的,他的前身是Multics操作系统,多任务信息与计算系统(MULTiplexed Information and Computing System)的缩写,它是一套分时多任务操作系统,是1964年由贝尔实验室、麻省理工学院及美国通用电气公司所共同参与研发,并安装在大型主机上。1969年,因MULTICS项目的工作进度过于缓慢,最后终究遭裁撤的命运,贝尔实验室由此退出此项目。

当时,肯·汤普逊正在撰写一个称为“星际旅行”(Space Travel)的游戏程序。贝尔实验室退出Multics计划后,由贝尔实验室的两位软件工程师肯·汤普逊与丹尼斯·里奇以B语言和汇编语言为基础而发展出UNIX,1973年汤普逊和里奇用C语言重写了Unix,成为后来普及的版本。

一般来说,Linux是一套遵从POSIX(可移植操作系统接口,Portable Operating System Interface,X则表明其对Unix API的传承。)规范的一个操作系统,它能够在普通PC计算机上实现全部的UNIX特性,具有多任务、多用户的能力。

MacOS内核名为Darwin,是以BSD源代码(Berkeley Software Distribution,伯克利Unix版本)和Mach微核心为基础,也就是是一个派生自Unix的操作系统,BSD非常地宽松,因此BSD常被当作工作站级别的Unix系统。

Windows NT声称自己实现了部分POSIX,但是值得称赞的是,Win10H2版本后,微软迈出了重大的一步,系统利用虚拟化技术集成了WSL(Windows Subsystem for Linux),可以在Windows系统中获得和原生Linux系统一致的体验,以至于很多人调侃,目前阶段,体验最好的Linux发行版是Windows。😂

👉扩展阅读:关于WSL安装与使用,请移步另一篇博客:MacOS/Linux程序员Win10平台生存指北

参考:

  1. 维基百科:UNIX
  2. 维基百科:Linux
  3. 维基百科:MacOS

Unix系统特点

国外Geek如何看Unix

1. Multiuser System :

多用户系统

Unix系统采用时间片轮转的方式,保证计算资源在多个用户之间共享。

2. Multitask System :

多任务系统

单个用户可以同时处理多个任务,比如,编辑一个文件的同时打印另一个文件,发送邮件的同时浏览网络。

内核从设计之初就支持用户的多任务需求,这里的多任务的设计,可以让用户看到一个任务在前台,其他任务在后台。用户可以切换、终止或挂起任务。

3. The Building-Block Approach :

积木式方法

Unix开发者一贯坚持简洁原则,因此Unix有一堆命令,每个命令只完成一件事情。但是你可以通过”|”管道符号将两个命令连接起来,完成更多的业务。

直至今日,很多Unix工具都支持将另一个程序的输出作为自己的输入。我们通过这种机制可以得到各种各样的新工具。

个人理解

这点设计思想和Windows截然不同,Windows上的软件习惯提供给用户一堆功能,而Linux依然贯彻小而美的理念,当然无可厚非,面向用户不同,Unix面向的是Developers,Windows面向的是普罗大众;这直接也决定了操作系统的生态与主要用途。

4. The UNIX Toolkit :

UNIX实用工具箱

Unix有一个内核,但仅靠内核无法帮助用户。因此,我们需要使用通常与UNIX系统一起附带的各式各样实用工具。(各种命令行工具)

5. Pattern Matching :

模式匹配

Unix提供了非常复杂的模式匹配功能,可以使用正则表达式进行匹配。

6. Programming Facility :

便于编程

Unix提供shell程序,同时这也是为程序员而不是为普通用户设计的编程语言。它具有编程所需的所有控制结构、循环和变量。这些功能用于设计shell脚本(可以调用UNIX命令的程序)。

系统的许多功能都可以由这些shell脚本控制和管理。

7. Documentation :

完善的文档

它有一个代表手册的“man”命令,这是任何命令及其配置文件最重要的参考手册。

参考:Features of Unix

国内学者如何看Unix

  1. UNIX系统在结构上分为核心程序(kernel)和外围程序(shell)两部分,而且两者有机结合成为一个整体。核心部分承担系统内部的各个模块的功能,即处理机和进程管理、存储管理、设备管理和文件系统。核心程序的特点是精心设计、简洁精干,只需占用很小的空间而常驻内存,以保证系统的高效率运行。外围部分包括系统的用户界面、系统实用程序以及应用程序,用户通过外围程序使用计算机。

  2. UNIX系统提供了良好的用户界面,具有使用方便、功能齐全、清晰而灵活、易于扩充和修改等特点。UNIX系统的使用有两种形式:一种是操作命令,即shell语言,是用户可以通过终端与系统发生交互作用的界面;另一种是面向用户程序的界面,它不仅在汇编语言,而且在C语言中向用户提供服务。

  3. UNIX系统的文件系统是树形结构。它由基本文件系统和若干个可装卸的子文件系统组成,既能扩大文件存储空间,又有利于安全和保密。

  4. UNIX系统把文件、文件目录和设备统一处理。它把文件作为不分任何记录的字符流进行顺序或随机存取,并使得文件、文件目录和设备具有相同的语法语义和相同的保护机制,这样既简化了系统设计,又便于用户使用。

  5. UNIX系统包含有非常丰富的语言处理程序、实用程序和开发软件用的工具性软件,向用户提供了相当完备的软件开发环境。

  6. UNIX系统的绝大部分程序是用C语言编程的,只有约占5%的程序用汇编语言编程。C语言是一种高级程序设计语言,它使得UNIX系统易于理解、修改和扩充,并且具有非常好的移植性。

  7. UNIX系统还提供了进程间的简单通信功能。

参考:赵文庆.UNIX和计算机软件技术基础.上海:复旦大学出版社,2011:9-23

Unix的设计思想

UNIX不仅仅是一个操作系统,更是一种生活方式。经过几十年的发展,UNIX在技术上日臻成熟的过程中,它独特的设计哲学和美学也深深地吸引了一大批技术人员,他们在维护、开发、使用UNIX的同时,UNIX也影响了他们的思考方式和看待世界的角度。

UNIX重要的设计原则

  • 简洁至上(KISS原则)
  • 提供机制而非策略

道格拉斯·麦克罗伊是Unix系统上管道机制,也是Unix文化的缔造者之一。他归纳的Unix哲学如下:

  1. 程序应该只关注一个目标,并尽可能把它做好。
  2. 让程序能够互相协同工作。
  3. 应该让程序处理文本数据流,因为这是一个通用的接口。

参考:Unix哲学

简洁至上

这也就是著名的KISS(Keep It Simple, Stupid)原则,在Unix中,尽可能让一个代码模块只做一件事情,把这件事情做好,然后把一个个小功能组合起来实现大功能。

关于这一点,阮一峰大佬的笔记如下:

1. 清晰原则。

代码要写得尽量清晰,避免晦涩难懂。清晰的代码不容易崩溃,而且容易理解和维护。重视注释。不为了性能的一丁点提升,而大幅增加技术的复杂性,因为复杂的技术会使得日后的阅读和维护更加艰难。

2. 模块原则。

每个程序只做一件事,不要试图在单个程序中完成多个任务。在程序的内部,面向用户的界面(前端)应该与运算机制(后端)分离,因为前端的变化往往快于后端。

3. 组合原则。

不同的程序之间通过接口相连。接口之间用文本格式进行通信,因为文本格式是最容易处理、最通用的格式。这就意味着尽量不要使用二进制数据进行通信,不要把二进制内容作为输出和输入。

4. 优化原则。

在功能实现之前,不要考虑对它优化。最重要的是让一切先能够运行,其次才是效率。”先求运行,再求正确,最后求快。”(Make it run, then make it right, then make it fast.)90%的功能现在能实现,比100%的功能永远实现不了强。先做出原型,然后找出哪些功能不必实现,那些不用写的代码显然无需优化。目前,最强大的优化工具恐怕是Delete键。

参考:https://www.ruanyifeng.com/blog/2009/06/unix_philosophy.html

提供机制而非策略

如何理解机制(mechanism)和策略(policy)呢?

就像前面说到的“积木式方法”,我们拼乐高,乐高积木的凸起和凹槽就是机制,我们如何拼便是策略

我的理解:机制是接口能力、是框架,策略是业务实体。前者是不变的,或者变化较少的;后者是善变的,根据不同场景可以进行调整的。

这样的方式更灵活,也更强大。

下面是我对这个特性的一些理解。

让程序处理文本数据流

道格拉斯·麦克罗伊归纳的Unix哲学之一,绝大数Unix程序都会把标准输入stdin作为自己的输入,然后把需要输出的信息打印到标准输出stdout。这样利用管道机制,就可以让数据在命令间串行流动起来。

我们再把现在的那些流式编程思想和Unix比较,是不是有些类似?用管道连接的每一个命令,其实都是这个文本输入流的过滤器。

这个思想也引入了另一个Unix理念:“沉默是金”。

其含义也很简单,如果没有必要,就别输出乱七八糟的信息,以免影响下一个命令的处理流程。

设备驱动框架

我们日常使用的手机,包含了各式各样的外设:触摸屏、按键、摄像头、扬声器等等,都是基于Linux设备驱动框架进行实现的。

Linux内核本身提供了Device - Bus - Driver设备驱动框架(机制),各个厂商才可以实现各种各样外设驱动(策略)供用户使用。

SELinux强制访问控制

Linux内核提供了LSM(Linux Secrity Module)安全机制,才有了后来NSA(美国国家安全局)实现的SELinux(Security-Enhanced Linux)模块。

当然后者其实也是一种机制,各个系统厂商可以配置自己的策略文件sepolicy去实现不同的强制访问控制——任何未经明确允许的行为都会被拒绝。我们现在使用的Android手机,在4.4版本之后已经默认集成并开启了这项功能。

一切皆文件

在Unix系统中一切皆文件。我们平时接触到的文件和目录都是文件,各种外设(鼠标、屏幕)在Unix系统中也是一个个设备节点文件,我们网络编程访问网络使用的套接字(Socket)在Unix系统中也是一个文件,系统中正在运行的进程也有文件与之对应。

一切皆文件

这些文件都可以简单的利用Linux提供的文件操作接口(File Operations),使用fopen、fclose、fwrite、fread等方法进行访问操作。

举个例子,在有权限的情况,你可以通过向对应的屏幕设备节点写入1或者0,进行屏幕亮灭的控制。

文件系统树状结构

单一根文件的树状存储

在Unix 操作系统中,所有的文件和目录都被组织成以一个根节点开始的倒置的树状结构。

文件系统的最顶层是由根目录开始的,系统使用 / 来表示根目录。在根目录之下的既可以是目录,也可以是文件,而每一个目录中又可以包含子目录文件。如此反复,构成一个庞大的文件系统。

Linux根目录树状结构

举个例子

一个Android系统中常见路径:/storage/emulated/0/Download/,通常以/结尾会被认为是一个目录的路径。

第一个/被认为是根目录,根路径是树状结构的起始,可以当做是0,所以/文件分隔符前没有任何字符,之后的storage/emulated/0/Download/都代表一级子目录。

特殊目录与隐藏文件

在Unix文件系统中有两个特殊的目录:

  1. 当前目录:通常用一个.表示,表示用户所在的工作目录(working directory)
  2. 父目录:通常用两个点 .. 来表示,就是当前目录的上一级目录。

这两个目录在每一个目录下都会以链接的形式存在,使用ls -a的时候就可以看到。

同样,用这个命令我们还可以看到一些以.开头的文件或者目录,比如用户home目录下经常遇到的~/.bashrc文件或者~/.ssh/目录,这些都是隐藏文件/目录——在默认方式访问时,文件管理器和shell程序并不会显示这些文件/目录。

各目录功能

以下是对Linux系统根目录一些子目录的解释:

/bin

Binaries (二进制文件) 的缩写,存放着各种基础命令的二进制可执行文件。

/bin目录中通常存放的是系统管理员和用户都能使用的命令,这些命令可以在系统修复、启动时使用,不需要依赖文件系统。

参考:Difference between /bin and /usr/bin

/boot

启动 Linux 时使用的一些核心文件,包括一些连接文件以及镜像文件。

/dev

Device(设备) 的缩写,该目录下存放的是 Linux 的外部设备节点文件,利用文件操作接口即可方便的控制外设。

/etc

“等等”的缩写,该目录下存放的一些系统程序所需要的配置文件。

/home

Unix是一个多用户系统,每个用户都有一个自己的目录,一般该目录名是以用户的账号命名的,

Linux系统用户的主目录通常在这个目录下对应用户名的子目录中,如上图中的 alice、bob 和 eve。

用户拥有其用户主目录下文件的所有访问操作权限,通常在命令行会使用~代替/home/<username>

注:MacOS系统的用户目录通常在/Users/<username>

/lib

Library(库)的缩写,该目录存放着系统动态链接库(so),类似于Windows里的 DLL 文件。

注:MacOS的Library目录和Linux的区别较大,主要是一些系统程序。

/lost+found

该目录一般情况下是空的,可以理解为回收站,当系统异常关机后,这里就存放了一些文件。

/kernel

该目录存放系统的内核文件。

/media

系统自动挂载的设备所在目录,Linux系统会自动识别一些设备,例如U盘、mp3、光驱等等,系统识别后,Linux设备驱动框架会把识别的设备挂载到media目录下。

注:MacOS系统对应目录为/Volumns

/mnt

mount(挂载)的缩写,通常是为了用户临时手动挂载一些设备所使用。比如一些默认无法识别的文件系统,可以通过手动挂载的方式进行访问。

注:MacOS系统对应目录为/Volumns

/opt

optional(可选) 的缩写,一些应用软件会安装到此目录,通常是用户级的。比如你自己下载了一个软件,可以选择安装到此目录,它所有的数据、库文件等等都是放在同个目录下面。

安装到此目录的应用,通常隐含着一个意思,就是我随时可以把这个目录rm -rf强制删除,当个渣男说拜拜。

/proc

Processes(进程) 的缩写,此目录下存放的并不是真实存在的文件或者外设,而是一套虚拟文件系统,存储的是当前内核运行状态的一系列特殊文件,它是系统内存的映射,系统内存中运行的每一个进程都会在此目录存在对应的文件,我们可以通过一些指令,获取指定进程的状态,也可以改变进程的状态。

比如Android native开发时会通过一些系统命令获取当前的进程信息,监控当前进程所打开/持有的inode数目,便是通过/proc/self/下的信息进行获得。

比如可以通过下面的命令来屏蔽主机的ping命令,使别人无法ping你的机器:

1
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all

注:MacOS无此目录。

扩展阅读:Linux下/proc目录简介

/root

系统管理员(超级用户)的用户主目录,普通用户的主目录在/home/<username>

我对root账户的理解就是可以操作/根(root)目录的用户。

/sbin

s就是Super User(超级用户)的意思,超级用户的二进制文件,这里存放的是一些需要超级用户权限才能使用的“高级”命令。

/selinux

前面我提到过,一套强制访问控制(MAC)机制对应的存储目录。

/srv

通常和服务器有关,个人电脑没怎么见过,该目录存放一些服务启动之后需要的数据。

是用户主动生产的数据、对外提供服务所使用的文件。

参考:关于linux下/srv、/var和/tmp的职责区分

/sys

也是内核设备框架sysfs文件系统使用的目录,类似于/proc目录,只不过这个目录映射的是系统内核的设备树,用户态程序可以通过sysfs获取当前系统外设的一些信息,并使用驱动暴露的一些文件接口操纵外设。

/tmp

temporary(临时)的缩写,用来存放一些临时文件的、缓存文件的目录,比如浏览器下载过程中会先在此目录进行缓冲。

保存一些使用完毕后可随时销毁的缓存文件。(有可能是由系统或程序产生、也有可能是用户主动放入的临时数据、系统会自动清理)

参考:关于linux下/srv、/var和/tmp的职责区分

/usr

虽然 /usr 目录发音是 user ,但实际它是 Unix System Resources 的缩写,意思是 Unix 系统资源 。用户的很多应用程序、运行库、文档、配置都存在都这个目录下,有点类似于Windows下的program files目录。这个目录中的文件都是只读的。

  • /usr/bin:包含对所有用户都能使用的非基础命令,是系统中大部分命令的存储目录。(注:与/bin目录区别。)

  • /usr/include:C语言头文件所在目录(Unix是用C语言写的)。

  • /usr/lib:包含着所有 /usr/bin/usr/sbin目录中可执行命令程序需调用的库文件。

  • /usr/share:通常包含着命令的一些数据文件、配置、文档等。

  • /usr/local:通常通过源码编译安装的软件会安装到此目录,Unix程序员约定俗成的目录。

  • /usr/src:内核源代码所在目录(你用Unix系统很容易就可以接触到源码)。

    参考:Linux 目录结构:Unix 系统资源目录(/usr)

/var

variable(变量)的缩写,通常习惯将那些易变的、易失的文件放在这个目录下,很多程序的日志文件保存在此目录。

比如你在本机搭建了一个Apache或者nginx服务器,通常网站数据也被保存在这个目录。

系统产生的不可自动销毁的缓存文件、日志记录。(系统和程序运行后产生的数据、不对外提供服务、只能用户手动清理)(包括mail、数据库文件、日志文件)

参考:关于linux下/srv、/var和/tmp的职责区分

/run

是一个临时文件系统,存储的是系统启动、运行需要的文件,系统重启时,该目录下文件会被抛弃重新生成。通常/var/run目录,会链接到 /run目录

参考:Linux 系统目录结构

为什么这么设计?

可能上面的那些目录,大多数看了也头晕,为什么/usr目录下很多内容和/目录里的是相同的?

历史原因和发展原因。

以下摘自阮一峰大佬博客Unix目录结构的来历

话说1969年,Ken Thompson和Dennis Ritchie在小型机PDP-7上发明了Unix。1971年,他们将主机升级到了PDP-11。

小型机PDP-7

当时,他们使用一种叫做RK05的储存盘,一盘的容量大约是1.5MB。

RK05存储磁盘

没过多久,操作系统(根目录)变得越来越大,一块盘已经装不下了。于是,他们加上了第二盘RK05,并且规定第一块盘专门放系统程序,第二块盘专门放用户自己的程序,因此挂载的目录点取名为/usr。也就是说,根目录”/“挂载在第一块盘,”/usr”目录挂载在第二块盘。除此之外,两块盘的目录结构完全相同,第一块盘的目录(/bin, /sbin, /lib, /tmp…)都在/usr目录下重新出现一次。

后来,第二块盘也满了,他们只好又加了第三盘RK05,挂载的目录点取名为/home,并且规定/usr用于存放用户的程序,/home用于存放用户的数据。

从此,这种目录结构就延续了下来。

注:这部分原文来自Rob Landley的邮件

以上,未完待续。

欢迎关注公众号“小黑杂说”。


20分钟入门|Unix系统与设计哲学——程序员应该知道的那些事儿
https://wuruofan.com/2022/03/09/20-minutes-learning-unix-system-and-its-philosophy/
作者
rf.w
发布于
2022年3月9日
许可协议