Hi,欢迎来到 卡瓦邦噶! 我叫 laixintao,我有一种特别的幽默感。我的梦想是让网络变得更加开放、自由和快速。这个博客发布我的一些想法,编程相关的文章,以及书、电影、游戏和音乐。

声明:本博客内容仅代表本人观点,和我的雇主无关。本站所有内容未加说明均为原创,一经发布自动进入公共领域,本人放弃所有权利。

程序员如何高效和同行交流

你是否在工作中经常遇到这种对话:

A:你给我的接口为啥访问不通啊?

B:确定是 POST 方法吗?参数 foo 加了吗?

A:加了啊?

B:我看下。

5分钟后。

B:你没加 application/json 啊。

A:还是访问不通啊。

B:我看下。

5分钟后。

B:URL 后面不能加 / 啊,不需要加。

A:……

B:……

本文介绍一些如何避免这种无意义的对话的方法。个人认为,写在简历里面的“沟通能力强”并不是一个软技能,而是一个硬技能。不是说话啰嗦,敬词用的多就是沟通能力强,而是用最少的话把信息描述清楚没有歧义,这要求理智和健全的身心,以及相关的工具和背景知识。


糟糕的沟通:紧张的描述 “我的 xxx 怎么不 xxx?”  “xxx 了怎么办?” 别人很难回答你的问题,必须继续追问你几个回合之后才知道你想表达什么。

有效的沟通:以不让别人追问为原则,描述清楚自己遇到的问题,所处的运行环境,如何复现。能够让别人复现你的场景非常重要。举个例子,前端的问题可以用 jsfiddle 复现,后端问题可以提供一个 docker 命令。


糟糕的沟通:截图一大片代码,中间包含某个报错。并问:为啥我的命令的结果是这样?

有效的沟通:贴出来这个命令一个结果的文字版本,然后提问。

除非是 GUI 方面的问题,一般人都会憎恨从图片中抄写代码。贴 URL 、代码以及转发,要优于贴图片。

如果一些 IM 工具对代码的格式化不好,(比如微信和钉钉),可以将代码写到 Gist 然后贴 Gist 的链接。甚至传文件也比截图要强。

有时候你要将别的群聊内容转发给另一个对象的话,尽量使用 IM 的转发功能,这样别人就可以知道上下文,知道前因后果。如果公司特别大,你经常遇到去问一个人A,A甩手让你去问B,B又让你问C的话,转发功能就比较好用了。

无论是在网络论坛,还是聊天,亦或是邮件,都应该善用引用能,提供原文的地址要优于复述原文。


糟糕的沟通:文档中写:接口参数是xxx,URL是xxx。

不要指望你这么描述能够让一个 HTTP 接口没有歧义。更糟糕的是,我见过 HTTP 的文档上就贴了一个 Java 的 Class,并且里面的注释类似这样: private String button; // 按钮

有效的沟通:在写文档或者 IM 工具中,尽量用 cURL 或 HAR 交流参数信息。如果是 HTTP API 文档,在有条件的情况下,可以搭建像 Swagger 这样的 API 平台。向接口的提供方描述问题的时候,不要说我使用的xxx怎么不行?可以直接贴给对方你的 cURL 命令,这样它们就知道你发送的请求了。现代的浏览器,以及 postman insomnia 这种 API 工具,都支持将 HTTP 请求导出到 cURL。

除了 HTTP,其他的交流也可以使用命令。比如遇到磁盘问题,可以将 df 或者 mount 命令之类的作为信息提供给运维同事。命令优于口头描述。


糟糕的沟通:在吗?

有效的沟通:直接提供你的问题,尽可能描述对方可能用到的所有的信息,不让别人追问。

这一条尤为重要,否则可能会被同事当做神经病。

 

Firefox Setup

今天做了今年最重要的一个决定,从用了将近 10 年的 Chrome 换到 Firefox 了。主要是因为1)Chrome 太慢了,尤其是对于我这种喜欢打开 N 多个 tab 的人 2)Firefox 注重隐私。

这篇文章记录一些配置项目。

设置

Firefox 自带的网络功能非常强大, 在 Chrome 需要第三方进程和插件才能实现的功能,用 Firefox 内置的配置就可以实现。

比如说可以不借助任何插件,直接将代理设置为 clash 的端口,将 DNS 设置为使用 CloudFlare 提供的 DNS over HTTPS。

换到 Firefox 之后感觉网速变慢了,刚开始以为是我 DoH 的问题,打开控制台看了了一下并不是。大部分的请求前面都有一段 block 的时间。这里另外需要改一个参数。

Firefox 默认每个 domain 使用的连接数是 6,如果请求数超过了可用连接数,那么这些请求就会被 block 住。

图中深红色的时间就是客户端在等到其他请求释放连接的时间。

修改的方式是在地址栏输入 about:config 打开高级模式。搜索 Network.http.max-persistent-connections-per-server ,将其改成 1024 。需要重启 Firefox 生效。

可以看到重启之后已经没有等待连接的时间了。

我使用的插件

之前的 Firefox 如果安装很多插件,会影响启动速度。可能是因为每个插件都要占用一个 Container 进程吧。所以除非必须,我不安装一些胡里花哨的东西。

  1. 1Password X:  使用体验竟然比 Chrome 好很多;
  2. Default Bookmark Folder: 用它来添加书签的时候可以搜索位置,适合我这种收藏狂魔 + 整理癖;
  3. Ghostery – Privacy Ad Blocker:不光能屏蔽广告,还能屏蔽 tracker;
  4. Tampermonkey: 这个同 Chrome 一样;

版本

目前有以下版本:

我常用的是 firefox-developer-edition,也装了 nightly 和稳定版。

使用体验

其他优点:

  • Firefox 在打开很多 tab 的情况下,如果放不开了,tab 就会变成可以滑动的;而 Chrome 直接不显示最新的 tab 了;
  • Firefox 的页面滚动比较平滑;UI也比较好,添加书签,刷新,后退等都有动画;
  • 开发者工具友好,信息比较直观,格式化的很漂亮。比如开发者工具的 network 栏,Chrome 跟其相比就相当于一个古老的 jQuery 实现的 table,Firefox 就像一个响应式的 UI 一样。如果直接输入一个返回 Json 的 URL,Firefox 默认会启用一个 Json 格式化的工具,比大部分的 Chrome json 插件和 Json 格式化网站都要强;
  • 内置的跟网络相关的工具很全。比如内置的代理,内置的 DNS over HTTPS;
  • 竟然内置了 Pocket,更方便了;
  • 开发者工具很多地方都带一个问号标志,点击一下可以直接跳到 MDN 的文档;
  • 进程树洁净。只有主进程和一些插件生存的 Container 进程。不像 Chrome 要那么多 Render 进程要占一屏的 htop;

缺点:

  • 中文字体的渲染在 OS X 下,相比于 Chrome 有些模糊(也有可能是我的错觉),2K 显示器下有点模糊,但是 Mac 的显示器还是非常清楚的;
  • 我最喜欢的 WordPress 编辑器很多快捷工作都不工作了,比如用 - 自动切换到列表模式, 改成代码样式等;
  • 一些网站的开发者用 Chrome,导致这些网站只在 Chrome 下工作,比如微信,在 Firefox 下聊天会遇到无法看到消息的 BUG;

 

就想到这些,以后朋友有用的 trick 再分享。

再见了,Chrome。


2019年12月08日更新:

网页版微信有巨大 BUG:聊天框会挂;网页版 微博在 firefox 登陆不上,微博也太烂了。

发现 Firefox 有 Container 功能,太实用了。测试自己网站的另外一个账号再也不用开隐身模式了,直接开 Container 就好了,相当于是 Cookie 完全不同的另一个浏览器,网站多账号轻松实现。

 

硬盘分区介绍

这篇文章介绍跟硬盘分区相关的知识和概念。我觉得和这相关的内容比较难理解,是因为一部分是硬件上的概念,一部分是软件的(文件系统),很多资料介绍的时候,没有放到一起对比,读者看到的时候就会对一些概念很模糊。比如硬盘的分区有分区类型,文件系统有类型,这两种类型有啥区别?硬盘有扇区大小,文件系统有 block 大小,这两者又有什么区别?这篇文章试图深入浅出,从基本的原理讲起,介绍一些概念,它们分别是做什么的,为什么要这么做。

认识硬盘

硬盘在 Linux 中,就是一个 block device,就是存储数据用的。你把数据输入到硬盘中,硬盘帮你存到一个位置。下次需要的时候,再从这个位置读出来。

那么给硬盘一个位置,它怎么去找这个位置的数据的呢?

这要从硬盘的结构说起(虽然现在大部分的机器都使用 SSD 了,但是很多资料都是基于机械硬盘的,所以这里以机械硬盘为例,介绍一下 CHS 寻址的原理。)。硬盘是由几张碟片组成的,每张碟片的正反两面都可以保存数据。

所以这个问题就转换成了:在几个圆面中,如何确定一个位置。首先我们想,在一个圆面中,确定一个位置需要几个参数?很显然是2个,距离圆心的距离可以确定一个圆,再加上一个“角度”可以确定这个圆上的一个点。那么在硬盘的结构中,再加上一个参数确定是第几个圆面就可以了。

这几个参数我们分别叫它:

  • Cylinder/Track:磁道,柱面,确定距离圆心的位置;
  • Head: 磁头,这个是读写数据的物理装置,实际上硬盘在运行的时候,是盘片在转的,磁头负责移动,调整读取的柱面;
  • Sector: 扇区。上面两个参数确定了一个圆形,Sector 就可以确定这个圆形中哪个扇区了。

这就是 CHS 寻址的方式。

从中可以也看出,硬盘存储的读写单位是“一个扇区”。实际上,在分区的时候,(分区软件)也会用“第几个到第几个扇区”来表示,不会让你涉及到 Track 和 Head 的,这是属于硬件自己用来寻址的东西。

使用 fdisk 我们可以看到硬盘有多少个扇区,一个扇区多大。实际上从 1980 年以来,几乎所有的硬盘扇区大小都是 512bytes@yiran 纠正:现在一些新磁盘的物理扇区是 4k 了,系统中看到的逻辑扇区是512字节)

为什么要分区?

现在我们知道有了扇区的位置,硬盘就可以把数据写入或读出。那为什么要分区呢?原因以下几点:

  • 隔离文件系统的腐烂(鸡蛋不放在一个篮子里)。我们要在设备上建立文件系统,操作系统才能使用文件系统来读写。文件系统是对存储设备的规划,记录着每一块都存了什么(inode, block)。万一文件系统的元数据错乱了,那么整个文件系统的数据可能都读不到了;
  • 提高存储的利用率。参考之前的博文:Linux 文件系统 inode 介绍,一个文件最小将占用一个 block,如果 block 太大的话,将会浪费很多空间。比如 block size 是 4k,而存储的都是 1k 的文件,那么有 3/4 的空间是浪费的。如果 block 太小,那性能就很低,因为 kernel 是以 block 为单位拷贝的;我们可以分一个区,建一个 block size 为 512bytes 的文件系统来专门存储这些小文件;
  • 限制文件增长。crontab 写日志太多了导致所有的进程都挂了,这肯定是不合理的。但是文件的增长不会越过文件系统,跑到另一个分区上,所以我们可以通过分区,来给特定的进程分配写空间。

基于此,我们可以在系统中为不同的存储内容划分区。比如为用户程序 /usr 单独分区,/home 单独分区。

分区的本质是什么?

不同的分区还是在一块硬盘上,相当于是对不同的扇区分组管理罢了。那么这个分组信息保存在哪里呢?

答案是第一个硬盘的第一个扇区上。硬盘的第一个扇区也是系统启动的时候第一个读的地方(基于 BIOS 的启动流程)。前面说到,一个扇区的大小是 512bytes,这 512bytes 都有什么呢?

在 Linux 中一切都是文件,硬盘也是一个文件,用 /dev/sda 表示(这是 SCSI 接口,IDE 接口会是 /dev/hda ,具体命名方式和编号见此)。这样,我们就可以将这个“文件”的前 512bytes 拷贝出来。

然后可以用 Vim 带的 xxd 命令看一下这个文件的内容:

这里面的内容可以分成4部分:

  1. 001-440bytes(一共 440bytes):给 BIOS 执行的代码; 这个其实很有意思,感兴趣的朋友可以将这段 dump 成机器码看一下。启动系统需要将代码加载到内存,但是我们需要系统启动才能加载代码。所以这个过程又叫做 boot,即 “pull oneself over a fence by one’s bootstraps”
  2. 441-446bytes(一共 6bytes):MBR Disk 签名;
  3. 447-510(一共 64bytes):分区表,一共 4 部分,每部分16 bytes;
  4. 最后的 511 和 512(一共2bytes):固定为 0x55AA,表示硬盘可以用于启动;

00001be 到最后 00001fd 之间,记录的都是分区表的信息。

  • 分区1: 0004 0104 82fe c2ff 0008 0000 0048 4500
  • 分区2: 80fe c2ff 83bb c1bb 0050 4500 0000 e001
  • 分区3:0000 0000 0000 0000 0000 0000 0000 0000
  • 分区4:0000 0000 0000 0000 0000 0000 0000 0000

根据上面 fdisk -l 显示,我这个机器只有两个分区,所以分区3和4是空的。这 16bytes 里面都记录的什么呢?我们拿其中一个分区来说明,这里就用第2个分区说吧:

80fe c2ff 83fe c1bb 0050 4500 0000 e001

0字节,80,是一个标志:

  • 80 此分区可以用于系统启动;
  • 00 此分区不能用于系统启动;

1-3字节,fe c2ff 这就是用我们上面说的 CHS 地址表示,以及后面的 5-7 字节,分别表示此分区开始的位置是:

  • fe Cylinder位置是 fe;
  • c2 Head 开始位置是 c2;
  • ff Sector 开始位置是 ff;

相应的,这个分区的结束位置是 bb c1 bb

开始和结束中间的第4字节,是分区类型。在这里是 83 ,表示 type 是 Linux.

fdisk 中可以通过 l 命令列出所有的 type:

但其实,这个分区类型在 Linux 中用处并不大,无论是 ext2 还是 ext3 还是其他 Linux 分区,都是 83。这个标志位不同的操作系统有不同的解释方法,比如 Windows,会用这个标志来区分不同的分区类型,所以你看到在这个表中,FAT32 和 NTFS 这些常见的 Windows 分区都分别占用了一种标志位。说到底,这个标志位其实就是个普通的标志,怎么解释归操作系统的,甚至不同的操作系统安装在同一个硬盘上也是可行的,比如 0x07 ,OS/2 认为这个标志位是 HPFS 类型的分区,Windows 认为是 NTFS 类型的分区。

要注意的是,这个标志位和文件系统并没有本质的关系。既然 Linux 不关心这个标志位,那么无论这个分区的类型是什么,我都可以在这上面建一个文件系统。甚至我可以在系统运行的时候覆盖写入这个标志位。比如我把当前的这个分区改成 FAT12,也是一点问题都没有的。

8-11字节:0050 4500 逻辑 block 地址的第一个扇区的绝对地址。

11-15字节:0000 e001 此分区一共有多少个扇区。

MBR 分区的限制:从这里可以看出,4个字节表示第一扇区的绝对地址,4个字节表示此分区有多少个扇区,那么 MBR 分区表最多可以支持的硬盘大小是:

512 * (2^32 -1 ) * 2 ,是 4TiB -1Kb。

然而,这样分区的话,必须要最后一个分区是 2TiB,这样才能利用起 4Tib。如果一个用户有一个 4TiB 的硬盘,想要平均分成4区,每个分区 1TiB,是不行的。这会对很多用户造成困惑,所以在商业宣传的时候,就直接说 MBR 支持 2TiB。参考1 参考2 yiran补充

主分区和扩展分区

从这里也可以看出,分区数据一共 64bytes,每个分区表需要 16bytes 的信息。那么一共可以有 4 个分区。我第一次用电脑的时候,是 Windows,一直不明白“本地磁盘 CDEF”是什么意思。其实就是分区软件的快速分区模式默认平均将硬盘分了4个区而已。

分区表决定了我们只能创建 4 个分区,如果我们想要更多的分区怎么办?

还记得在文件系统中 block 寻址的时候如果超过 inode 能存放的 block 怎么办吗?答案是:inode 存放的 block,实际的内容是指向真正的 block 的地址。这里也用了同样的原理,我们可以创建一个类型为 Extended 类型(标志位是 5)的主分区,然后这个分区中每个分区的最后都保存着指向下一个分区的地址。

逻辑分区必须是连续的(显而易见),但是主分区可以不连续。除此之外逻辑分区和主分区在使用上并没有差别。逻辑分区也可以启动系统。

介绍到这里,应该能解决读者大部分的问题了(至少这些内容回答了我的很多疑问)。更加深入的问题,可能就要读者基于这些内容,自行搜索更详细的资料了。

 

扇区大小和Block大小

看文本文你应该对这个问题有所了解,扇区是一个硬盘的概念,几乎所有的硬盘扇区都是 512Bytes,如果不是,可能会出问题的。而 Block 指的是一个逻辑上的概念。但是可能在一些情景下依然对它们有些困惑。我研究了一番相关的内容,所以在这里多少一些,以便将来跟我有同样疑问的朋友,能找到这里,节省一些时间。

扇区大小的概念,出入很小。但是 Block 在不同的情景下是有不同的含义的。

首先是文件系统的 block,这里的 block 会影响存储文件使用的 block 大小。道理很简单,文件系统以 block 为单位寻址,如果 block 大小为 4k,那么即使文件写入 1k,也需要占用 4k。

创建文件系统,会自动分配 inode 和 block:

IO 中的 block:IO 是以 block 为单位的,这个 block 不一定是文件系统的 block 大小,也不一定是扇区的大小,可以比扇区更小,但是这是一种浪费,因为硬盘每次写会写 512bytes,如果 IO 的 block 是 256bytes,那么相当于写入相同一个扇区的内容,用了两次物理写入操作。此外,我们写入磁盘必须经过 syscall,在用户空间和 kernel 空间之间拷贝数据,也是以 block 为单位。我们可以用 madvice 这个系统调用向 Kernel 建议 IO block size。

以下是我用 dd 从硬盘拷贝相同的数据,使用不同的 block size,可以见期速度的影响。

但是 IO 其实是一个很复杂的问题,三言两语是说不清楚的,推荐一本书 Linux System Programming,里面用了四章介绍 IO 相关的话题。

除此之外,在看到 block 的时候,你还要注意它说的是什么语境。比如 ls -s 命令展示的 block,是以每个 block=1024bytes 展示的,而 stat 里面的 block 是 512bytes

 

建议用相关工具实践一下分区,建议在虚拟机里面操作,不用担心搞坏宿主机。玩一下这些命令:

  • xxd (vim提供)
  • fdisk
  • mount
  • grub
  • ss
  • dd

这篇文章参考的资料:

  1. Linux Partition HOWTO
  2. 分区标志
  3. Parition Types
  4. Linux System Administrators Guide: Chapter 5. Using Disks and Other Storage Media
  5. Linux 是如何启动的?
  6. Linux MBR
  7. 分区类型和文件系统类型的区别
  8. 如何确定 block size
 

编译LFS的一些经验

LFS 全称叫做 Linux From Scratch ,顾明思议,就是“从头开始的 Linux”。官方网址 中文翻译网址

LFS 可以说是一个项目,也可以说是一本书。它会教你如何从头构建你自己的 Linux 发行版。这里说的“从头”,是纯粹的从头的,包括编译工具,也是需要自己构建出来的。比如 Gcc,这是整个构建过程中比较核心的工具,LFS 先从 Gcc 源码编译了一个 Gcc,又编译了一个 Glibc。因为 Libstdc++ 是依赖 Glibc 的,所以有了 Glibc 之后,才可以第二次编译 Gcc(这次带上 Libstdc++)。其他工具也是一样,这样就有了一个临时的工具集。当编译一个 Linux 所有的工具链都准备好之后,就用 chroot 进入临时的系统,独立于宿主系统。在这个临时系统中编译 Linux。即,这个工具从编译工具到最后 boot 启动成功,都是需要自己编译的,没有使用现成的 binary。

LFS 让我感到很惊艳,这是个了不起的工程。从上面的例子可以看出,这个工程需要经验老到的梳理很多循环依赖,和编译错误。

现在的编程世界有了各种方便的包管理工具,和提前编译好的二进制文件。一般我们需要什么工具,只要找对应平台的二进制来下载使用就好了。却从来没有想过,如果“从头”编译一个东西会是怎样的。会不会有一天,像《基地》里面的帝国那样,机器都无比复杂,没有任何一个人清楚怎么修理,而导致帝国的崩坏呢?

小时候我经常陷入这样的幻想:如果从零制作一个东西,会是怎么样的。比如说桌子,造桌子需要斧头,和钉子,这些又需要钢铁,那么就需要炼钢。炼钢技术需要有火,还有要XX等等。这个项目,也算满足了我的一个幻想。

废话了这么多,只想跟大家推荐一下 LFS,真的很值得一试。即使最后你不使用最终构建的 Linux 作为自己日常使用的 Linux,从中也能学到很多东西。比如,我今天从这个项目中才知道 su - 后面的这个 - 是什么含义

这篇博客,也记录一下我构建 LFS 时候的一些坑,因为我发现中文方面的资料和讨论比较少。希望对后面去玩 LFS 的人有些帮助。

环境问题

我是在 Mac 上,使用 VirtualBox 虚拟化出来的 Fedora 构建的(推荐大家这么做)。也可以直接使用 Linux 系统编译,不用虚拟化和 Docker 啥的,因为书里面就会教你如何构建一个纯净的工具系统。但是不推荐用 OS X 来构建,APFS 会有很多坑。

最开始需要分区,挂载到宿主系统。我在虚拟机里面没有搞定,然后想起来,我在 Virtualbox 里面呀!遂直接用 Virtualbox 挂载了一个新的硬盘上去。搞定。

第一次编译 LFS 尽量不要自己做定制,严格按照书上的来,不然可能会白费一些力气。

下载

要下载的内容基本都是源代码,在准备的章节中,LFS 整理了一个 URL 列表,可以直接用 wget 下载。我发现即使用代理,有些下载还是很慢,2个小时都没下载完。后来直接去 DigitalOcean 开了一台 VPS,3min就下完了。推荐还是用外网的机器直接下载然后 scp 拷贝回来吧。

机器配置

LFS 每一个工具的构建都写的很标准,标明了构建耗时。用的单位是 SBU (Standard Build Unit),表示时间需要构建 BinUtils 的几倍长。有些构建相当耗时,我一开始给构建用的机器分配了 2CPU/8G MEM ,最后加到了 4CPU/8G MEM。但是还是觉得挺慢,所以推荐大家有多少资源就给多少吧。LFS 整个过程大多数时间都在等待编译完成。

在 LFS 中可以学到什么

尽量看懂书里面的每一行脚本,和解释,能学到很多东西的,不要急着粘贴命令。

The key to learning what makes a Linux system work is to know what each package is used for and why you (or the system) may need it.

操作仔细

注意一下编译的输出。由于编译太耗时了,有可能编译结束的时候,就忘记自己当前在哪一步了。一定要仔细一些,我有次 make 之后忘记执行 make install 了,其实后面执行命令的时候,通过 ./configure 输出,可以看到某些工具用的不是工具链里面,是系统里面的,是可以提前发现的。LFS 越到后面,发现错误的成本越大。轻则需要从错误处再走一遍,重则找不到错误的源头,功夫白费了。

编译完第5章建议备份一下 $LFS/tools ,以后编译别的东西可以直接用这个工具链。

一些可能的坑

  • 进入第六章之后,如果中途中断了(总不能一口气编译完吧)。下次进入的时候要重新操作 6.26.4,进行 mount 和进入 chroot 和 mount 一些虚拟文件系统;

    If the virtual kernel file systems have been unmounted, either manually or through a reboot, ensure that the virtual kernel file systems are mounted when reentering the chroot. This process was explained in Section 6.2.2, “Mounting and Populating /dev” and Section 6.2.3, “Mounting Virtual Kernel File Systems”.

  • 即使编译 stable 版本的 LFS,也可能遇到一些书中没有标明的测试失败。只要不是大面积的失败,几个失败可能并不是重要的。我编译的过程中遇到的未预期的测试失败记录在这里了,供后面的人参考。
  • 推荐使用 virtualbox 添加一块新盘来做,这样即使 host 机器有什么问题(比如 grub 设置错误导致无法启动),可以很方便的将这个新盘 mount 到另外一台虚拟机,直接更改里面文件。
  • 如果使用 virtualbox 的话,在设置 grub 的时候,要注意将 grub 安装到 /dev/sda,然后 boot 的磁盘添加 /dev/sdb.

相关资料推荐

推荐一些相关的资料,解决了我的疑惑,也可能解决你的疑惑。

  1. The magic behind configure, make, make install 编译的过程中你要运行无数次 ./configure && make && make install ,这篇文章解释了这些命令是哪里来的,以及 ./configure 和 Makefile 是从哪里来的;
  2. Libraries: Static or shared?

电影推荐

有些编译很漫长,第三轮 glibc 要将近1小时,第三轮 Gcc(加上 check)需要4h左右。准备些爆米花边看电影边编译吧。

  • 《勇士》 汤姆·哈迪主演的拳击题材的电影;
  • Rudderless 一个比较复杂的话题,音乐题材;
  • 《血钻》 莱昂纳多·迪卡普里奥主演的战争动作片,讲南非黑钻的故事。

最后,如果在构建的过程中遇到问题,可以在这里留言,大家交流学习。

 

/bin /usr/bin 和 /usr/local/bin 的故事

Linux 系统的 $PATH 变量通常会包含 /bin /usr/bin /usr/local/bin 。这三个目录下都有一些 binary 程序,至于有什么区别,网上有很多错误的解释。(就像介绍 HTTP GET 和 POST 有什么区别这个问题一样)。本文试图说明这个问题,基于我的理解和 Linux 文档。(本文里说的 Linux,更准确的说,应该是 Unix系系统)

TL;DR

这些 bin 的位置,仅仅是几个目录而已,并没有本质的区别。哪些命令放在哪里,完全看用户和发行版的喜好。比如,有一些发行版/bin 作为 -> /usr/bin 的符号链接[命令,Ubuntu 放在 /usr/bin 下,OS X 就放在了 /bin 下。

但是通常来说,我们都认为这几个目录这样安排:

  • /bin 放置系统的关键程序,比如 ls cat ,对于“关键”的定义,不同的发行版会有不同的理解;
  • /usr/bin 放置发行版管理的程序,比如 Ubuntu 自带 md5sum ,这个 binary 就会在这个目录下;
  • /usr/local/bin 放置用户自己的程序,比如你编译了一个 gcc,那么 gcc 这个可执行 binary 应该在这个目录下;
  • 除此之外,还有对应的三个目录 /sbin /usr/sbin /usr/local/sbin ,放置系统管理的程序,比如 deluser chroot service ;

需要再次强调,这是一种文件的管理方式而已,你甚至可以把自己的 binary 放到 $HOME/bin 下。还有,OS X 用 homebrew 安装的软件,会放在 /usr/local/Cellar 下,然后在 /usr/local/bin 创建一个指向相关 bin 目录的符号链接;但是在 Ubuntu 下,会放到 /usr/bin 下。

更深的理解

(这一段来自知乎的 in nek)另外,需要知道 / /usr /usr/local 这些都是 prefix,你编译一个软件的之后,要执行 ./configure --prefix=/usr/local 然后 make && make install 。那么 /usr/local 就会作为 prefix,库文件就放在 /usr/local/lib 下面,配置文件就放在 /usr/local/etc 下面,可执行文件(binary)就放在 /usr/local/bin 下面。

然后我们看看这些prefix是怎么选择的。如果你编译过FreeBSD一类的系统,你会发现,这些系统的系统库,基础工具和内核是放到一套代码树中的,编译这个代码,内核和核心库,工具是一同完成的,这些都被认为是操作系统的一部分。这些核心文件,就以根目录作为prefix。所以,/是所有操作系统核心程序的prefix。

在这个核心之外增加新的程序,构成一个发行版,这个发行版增加的程序就用/usr作为prefix。

你把发行版安装好了,安装发行版之外应用程序,那些程序通常用/opt, /srv作为prefix。

但如果你自己从源代码开始编译一个应用程序,这些程序是专门向你这个Site编译的,这种情况下,默认的prefix是/usr/local。

一些历史…

可以看到,不同的发行版会有不同的理解。所以就有 Filesystem Hierarchy Standard 想要指定一个文件等级的标准。(其实我认为这个标准没有太大意义,这种规范不能强迫所有的发行版去理解。与其去理解这个,不如去理解你的发行版是怎么思考的,适用和学习你称手的发行版)。

即使现在大多数人认为这三个目录的含义就像本文开头讲的那样,但事实是,这三个目录创建的时候的目的不是这样的。

事实是,Ken Thompson 和 Dennis Ritchie 在 1969 年创建 Unix 的时候,用的是 PDP-11,磁盘是两块 RK05,每块只有 1.5M。

后来系统变得越来越大了,一块磁盘不够用了,需要用到第二块磁盘,于是就 mount 了第二块,叫做 /usr 用来放用户的文件(想象一下,有两块磁盘,一块放系统,一块放用户数据,非常合理)。然后把系统的目录,/bin /sbin lib … 都复制到新磁盘下,在新磁盘读写。

后来他们有了第三块磁盘(啊!!),mount 在了 /home 下,只把用户的文件移动了过去。这样通过不同的磁盘挂载到不同的地方,系统可以利用起3块磁盘了。

当然,他们必须有个规则:“当系统第一次启动的时候,第一块磁盘里面必须有所有需要的程序,来挂在第二块磁盘到 /usr,比如 mount 。如果 mount 放在 /usr/bin/mount 这里,就会遇上先有鸡还是先有蛋的问题。” 非常合理。

/bin 和 /usr/bin 的分裂是人为的,1970 年的这个实现细节一直延续了下来。要怪就怪那些墨守成规,不问为什么的人。

可以说这个分裂是“没有必要的”,如果磁盘空间足够的话。但是后来由于种种原因,这个“没有必要”的假设就被打破了。其中一个原因是,后来引入了共享库(动态链接),/lib 和 /usr/bin 必须 match。之前没有这个问题,因为所有的东西都是静态链接的。

Anyway,自从有了这两个目录,人们便开始赋予它们含义:/ 用来存放上游的文件,/usr 放本地的内容;后来演变成 / 放从 AT&T 官方发行的内容,/usr 放发行版的内容,那时是 IBM AIX 或 Dec Ultrix,/usr/local 放自己本地的内容;再后来人们觉得 /usr/local 放安装的新 package 不够好,那再加一个 /opt ! 将来说不定还有 /opt/local

后来有一些组织尝试将它们标准化,比如我们前面提到的 Filesystem Hierarchy Standard ,但是他们并没有尝试去理解一开始为什么会这样……

 

 

参考资料:

  1. Filesystem Hierarchy Standard
  2. THDP: Linux Filesystem Hierarchy
  3. Understanding the bin, sbin, usr/bin , usr/sbin split