Unix 编程概览(全概括长篇、持续更新)

在了解 Unix 编程概念之前要知道 Linux 和 Unix 分别指的是什么,POSIX 是什么,它们之间又是什么关系。

简而言之,Linux 和 Unix 是两种操作系统,Linux 模仿了 Unix 的设计但并不基于 Unix。所以其实可以说他们没关系但也有关联。而 POSIX 是一种接口标准,它规范了 Unix 编程接口。Linux 也完全遵守了 POSIX 标准并且进行了实现。所以 POSIX 标准可以让规范的程序移植于多种操作系统,因为 API 兼容。

在进行 Unix 编程之前需要又需要了解以下的基础内容,会一一简单描述:

内核

在介绍 Unix 编程的所有内容之前几乎都要知道的基本概念,操作系统和内核。一般来讲、内核特指管理和分配计算机硬件资源的“核心层”软件。例如 CPU、RAM 和各种其他硬件设备。而操作系统是包括内核在内以及附带的一系列工具,例如命令行、图形界面、文件浏览器、文本编辑器等。

内核主要负责:进程管理、内存管理、抽象文件系统、访问设备、联网和提供应用程序调用的 API。

注:Linux 内核可执行文件在 /boot/vmlinuz,vm 表示虚拟化的,z 表示压缩过的。

CPU 在执行的时候有两种状态:核心态和用户态。核心态运行时有完全的控制能力,例如访问内存、管理硬件、设备的初始化等。而用户态则权限非常受限,例如只能访问被划分为用户态才能访问的内存区域。操作系统置于内核空间,所以所有用户态状态的执行指令无法直接访问和修改内核数据。

一个有趣的说法:进程自身其实对自己一无所知,无法创建新进程甚至杀死自己都做不到。一个应用程序被关闭其实是在向内核发请求:我可以结束了!
这些在底层上都是内核才有权限做的,应用程序开发时在表象上之所以可以“为所欲为”因为调用了内核提供的 API,也就是请求内核执行某些操作。

Shell

shell 是一种用于解释用户输入命令的程序。其实 shell 并没有唯一规范,因为它种类很多,在不同时代、不同的操作系统上都出现过不同的 shell,例如一般在 Linux 上默认的是 bash 作为 shell 的执行环境。

用户和组

无论是 Windows 也好还是 Linux,它们都宣称自己是“多用户”操作系统。但在 UNIX 的实现中还有一个“用户组”的概念。 不同的用户可以有不同的权限和私有环境领域的划分(例如用户主目录、环境变量等),用户有用户名和用户ID。ID 是自动产生的数字,用户名则是用户自己取的可限定字符。一般默认 ID 为 0 的是超级管理员,用户名为 root。超级管理员享受最高权限待遇,可以跳过系统的几乎任何安全检查。

组是对多个用户的权限划分和归类。例如存在目录 B,需要限制用户访问。对于一个目录而言不可能设置多次多个用户的读写权限,所以只需要设置某个组的读写权限,将需要的用户添加至组即可。
值得一提的是,用户和组不是多对一,而是多对多的关系,这让权限划分更加自由。

目录、文件和符号链接

Windows 下文件系统存在多个“根”,也就是磁盘,一般默认以分区来划分。而 Linux 则只存在一个“根”,没有磁盘的概念,但是不表示 Linux 不支持分多个区,只是多个区可以共同构成一个根为主的层级结构,一个分区可能被挂载到任何一个层级。

值得注意的是,Linux 下目录也应该称之为文件。只不过目录是一种特殊文件,记录了其下的所有文件名及引用的列表。
每个目录下都存在 . 和 .. 两个文件,前者表示到目录自身的链接,后者表示上级目录的链接。

符号链接是区别与目录或者普通文件的特殊文件类型,它表示对目标文件的指向。目标文件可以是普通文件也可以是目录文件甚至是另一个符号链接。
只不过内核在解析符号链接的时候会自动解引用“找到”目标文件,所以一般情况下把符号链接当普通文件或者目录也没有问题。 对了,UNIX 下的路径可以理解为一系列的文件名组合,只不过以 / 分隔。

I/O

待描述。

进程

进程指的是正在执行的应用程序实例,内核将程序载入内存,为程序分配空间、建立信息等操作。一个进程会记录下例如:进程ID(PID)、父级进程ID(PPID)、用户、组 ID 等。
创建新进程的操作是 fork(),创建出来的新进程是创建者的子进程,保存有父进程信息的副本。例如 父进程的用户 ID 是 0,那么子进程的用户 ID 也是0。所以 root 身份执行 shell,哪怕 shell 调用了其它程序,也具备了 root 权限。

进程的标识符:真实用户/组 ID、有效用户/组 ID、补充组 ID。其中有效用户/组 ID 默认跟真实用户/组 相同,但是它可以被修改,达到在运行时修改进程权限的效果。例如常见的做法:让 nginx 首先以 root 权限启动,然后再对进程 setuid 为普通用户权限,以达到降权但监听80端口的目的。保证了一定的用户权限导致的系统安全问题。

进程之间的通信和同步

由于进程是互相独立的,不能直接进行通信或者数据交换。所以 UNIX 提供了多种进程通信的机制:

  • 信号
  • 管道
  • 套接字
  • 文件锁定
  • 消息队列
  • 信号量

信号

进程可以向其它或者自己发送指定信号,大部分信号都表示进程需要“软件中断”,例如程序结束或者出现异常。
很常见的用户操作:Ctrl + C 即表示给进程发送结束的信号。在 shell 中,kill 命令即是向进程发送信号,默认是结束进程,但也有例如 nginx 的 HUP 等信号表示其他操作。
程序可以监听指定信号,以调用自己的信号处理函数,也可以进行屏蔽某些信号等操作。

待更。