Unix/Linux OS Introduction
Linux 操作系统的简介
什么是 Linux 操作系统
Linux 是一套免费使用和自由传播的 Unix Like 操作系统,是一个基于 POSIX 和 UNIX 的多用户、多任务、支持多线程和多 CPU 的操作系统。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。
Linux 操作系统由 Linux 内核,LinuxShell,Linux 文件系统,Linux 应用程序四大主要部分组成。
- 内核是操作系统的核心,提供了操作系统最基本的功能
- Shell 是系统的用户界面,提供了用户与内核进行交互操作的一种接口
- 文件系统是文件存放在磁盘等存储设备上的组织方法
- 标准的 Linux 系统一般都有一套称为应用程序的程序集,即 Linux 应用程序
Linux 操作系统的组成
Kernel
内核是操作系统的核心,提供了操作系统最基本的功能,如支持虚拟内存、多任务、共享库、需求加载、可执行程序和 TCP/IP 网络等
Shell
Shell 是系统的用户界面,提供了用户与内核进行交互操作的一种接口。它接收用户输入的命令并把它送入内核去执行。实际上 Shell 是一个命令解释器(command-language interpreter),它解释由用户输入的命令并且把它们送到内核。
Linux 系统的 Shell 是命令语言、命令解释程序及程序设计语言的统称。
- Shell 是一个命令语言解释器
它拥有自己内建的 Shell 命令集,Shell 也能被系统中其他应用程序所调用。用户在提示符下输入的命令都由 Shell 先解释然后传给 Linux 核心。使用户不必关心一个命令是建立在 Shell 内部还是一个单独的程序。 - Shell 是一个解释型的程序设计语言。
shell 程序设计语言支持绝大多数在高级语言中能见到的程序元素,如函数、变量、数组和程序控制结构。shell 编程语言简单易学,任何在提示符中能键入的命令都能放到一个可执行的 Shell 程序中。
执行过程
shell 首先检查命令是否是内部命令,不是的话再检查是否是一个应用程序,这里的应用程序可以是 Linux 本身的实用程序,比如 ls rm,
然后 shell 试着在搜索路径($PATH)里寻找这些应用程序。搜索路径是一个能找到可执行程序的目录列表。如果你键入的命令不是一个内部命令并且在路径里没有找到这个可执行文件,将会显示一条错误信息。而如果命令被成功的找到的话,shell 的内部命令或应用程序将被分解为系统调用并传给 Linux 内核。
File System
Linux 文件系统中的文件是数据的集合,文件系统不仅包含着文件中的数据而且还有文件系统的结构,所有 Linux 用户和程序看到的文件、目录、软连接及文件保护信息等都存储在其中。
每个实际文件系统都是从操作系统和系统服务中分离出来的,它们之间通过一个接口层——虚拟文件系统(VFS)通信。
文件类型主要包括:
- 普通文件(regularfile)
- 目录文件(directory)
- 连接文件(link)
- 设备与设备文件(device)
- 套接字(sockets)
- 管道(FIFO,pipe)
Linux 操作系统的功能特征
Linux 操作系统功能强大,与其他操作系统相比,其具有下述主要的功能特征。
- 开放性:遵循开放系统互连(OSI)国际标准。
- 多用户:Linux 支持多用户,操作系统资源可以被不同用户使用,每个用户对自己的资源(例如:文件、设备)具有特定的权限,这样可以保证每个用户之间互不影响。
- 多任务:Linux 可以使多个程序同时并独立地运行。**计算机同时执行多个程序,而同时各个程序的运行互相独立。
- 良好的用户界面:Linux 向用户提供了两种界面:字符界面和图形界面。在字符界面用户可以通过键盘输入相应的指令来进行操作。Linux 还为用户提供了图形用户界面,它类似于 Windows 图形界面的 X-Window 系统。它利用鼠标、菜单、窗口、滚动条等设施,给用户呈现一个直观、易操作、交互性强的友好的图形化界面。在 X-Window 环境中就和在 Windows 中相似,可以说是一个 Linux 版的 Windows。
- 设备独立性:操作系统把所有外部设备统一当作文件来看待,只要安装驱动程序,任何用户都可以像使用文件一样,操纵和使用这些设备。Linux 是具有设备独立性的操作系统,内核具有高度适应能力。
- 提供了丰富的网络功能:完善的内置网络是 Linux 操作系统的一大特点。
- 可靠的安全系统:Linux 采取了许多安全技术措施,包括对读写控制,带保护的子系统,审计跟踪,核心授权等,这为网络多用户环境中的用户提供了必要的安全保障。
- 良好的可移植性:将 Linux 操作系统从一个平台转移到另一个平台使它仍然能够按照其自身的方式运行。Linux 是一种可移植的操作系统,能够在从微型计算机到大型计算机的任何环境中和任何平台上运行。Linux 可以运行在多种硬件平台上,如具有 x86、ARM、SPARC、Alpha 等处理器的平台。此外 Linux 还是一种嵌入式操作系统,可以运行在掌上电脑、机顶盒或游戏机上。2001 年 1 月份发布的 Linux 2.4 版内核已经能够完全支持 Intel 64 位芯片架构。同时 Linux 也支持多处理器技术。多个处理器同时工作,使系统性能大大提高
Linux 核心特征:多用户、多进程
Linux 核心特征—多用户、多进程管理的实现方式如下:
(1)账户管理
系统依据账户 ID 来区分每个用户的文件、进程、任务,给每个用户提供特定的工作环境(如用户的工作目录、Shell 版本以及 XWindow 环境的配置等),使每个用户的工作都能独立不受干扰地进行。
(2)权限管理
在 Linux 中,将使用系统资源的人员分为 4 类三组:超级用户、文件或目录的属主;属主的同组人;其他人员。对每组用户分别分配对文件和目录的不同的访问权限。
(3)进程管理
进程控制是 Shell 的一个特性,使用户能在多个独立进程间进行切换。
例如,用户可以挂起一个正在运行的进程,稍后再恢复其运行。bash 记录所有启动的进程并保持对所有已启动的进程的跟踪,在每一个正在运行的进程生命期内的任何时候,用户可以任意地挂起进程或重新启动进程恢复运行。
Linux 操作系统的主要版本
Linux 操作系统主要分为内核版本以及发行版本,其具体内容如下。
(1)Linux 的内核版本:
Linux 内核使用主要分为两种不同的版本编号方式。第一种方式用于 1.0 版本之前(包括 1.0)。第二种方式用于 1.0 之后到 2.6,数字由三部分“A.B.C”,A 代表主版本号,B 代表次主版本号,C 代表较小的末版本号。
(2)Linux 的发行版本
Linux 的发行版本实质在于 Linux 核心加上外围的实用程序组成的一个大软件包。
把 SUSE、RedHat(现在通常称为 Red Hat Enterprise Linux,简称 RHEL)、Ubuntu、Slackware 等直接称呼为 Linux 是不确切的,它们是 Linux 的发行版本。更确切地说,应该称为以 Linux 为核心的操作系统软件包
发行版主要可分为 Debian 系和 RedHat 系。Debian 系主要分为 Ubuntu、Debian、Linux Mint、Linux Lite 等。RedHat 系主要分为 CentOS、Fedora、RedHat Enterprise Linux、Oracle Linux 等。Debian 是一个非商业化的项目,完全由社区驱动。Red Hat 提供了一个免费的社区版本(Fedora),同时通过其企业版(RHEL)提供商业支持和服务。
文件操作与权限管理
Linux 文件系统架构遵循 Unix 的设计理念,其核心哲学之一就是“一切皆文件”(Everything is a file)。这意味着无论是普通的文本文件、目录、设备(如打印机、磁盘)、管道、套接字还是其他系统资源,都被抽象成文件的形式进行操作,从而提供了一致的接口和简单的编程模型。
层次树状结构:Linux 文件系统采用分层的树状目录结构,其中所有文件和目录都始于一个称为根目录(/)的单一节点。每个目录可以包含文件和子目录,形成一个层次化的组织结构。
UNIX/Linux 文件系统的基本结构
虚拟文件系统(VFS):虚拟文件系统是 Linux 内核中的一个软件层,允许不同的文件系统(如 ext4、XFS、Btrfs 等)以统一的方式被内核和用户空间应用程序访问。VFS 定义了所有文件系统都支持的基本、抽象接口和数据结构
,实际文件系统实现 VFS 定义的抽象接口和数据结构(文件、目录等概念在形式上与 VFS 的定义保持一致),在统一的接口和数据结构下隐藏了具体的实现细节
ext2 (Second Extended Filesystem)
ext2 是 Linux 内核最早支持的文件系统之一,设计用于替代较早的 ext 文件系统。它引入了许多改进,如更快的文件访问速度和更大的文件系统容量。ext2 支持大文件(理论上可达 2^32 个块,受限于文件系统大小)、长文件名等特性。然而,ext2 是一个非日志文件系统,这意味着在系统崩溃时可能需要较长的文件系统检查和修复过程来恢复一致性。
ext3 (Third Extended Filesystem)
ext3 是 ext2 的升级版,主要增加了日志功能(journaling),这极大地提高了文件系统的可靠性和恢复速度。在系统崩溃后,ext3 能够快速恢复文件系统的状态,因为它维护了一个事务日志,记录了文件系统更改的操作ext4 (Fourth Extended Filesystem)
ext4 是 ext3 的进一步发展,它不仅继承了 ext3 的稳定性,还引入了多项增强特性,如更大的文件系统和文件大小支持(理论上可达到 1 EB 减 1 字节)、更高效的空间分配策略(延迟分配和多块分配)、无限数量的子目录、更快的文件系统检查和更强大的日志功能。此外,ext4 还优化了 inode(索引节点)的使用,允许存储更多的扩展属性,并支持快速扩展和收缩文件系统大小。XFS (eXtended File System)
XFS 是一个高性能的 64 位日志文件系统,最初为 SGI 的 IRIX 操作系统设计,后来被移植到 Linux。XFS 特别适合处理大型文件和高并发的读写操作,广泛应用于企业级服务器和大数据存储场景。它提供了极快的文件系统增长速度、延迟分配策略以减少文件碎片、以及动态 inode 分配等功能。XFS 能够支持非常大的文件和文件系统(理论上可达 18 EB),并且其日志机制设计得更加健壮和高效,尤其在处理大量小文件和高 I/O 负载时表现优秀。
挂载点:Linux 允许将不同的物理或逻辑磁盘分区挂载到文件系统的特定目录下,这个目录称为挂载点。这样可以扩展文件系统的存储空间,并且不同的文件系统可以有各自的属性和功能。
索引节点的功能
文件在磁盘中的表现形式
- 物理表现形式:
- 超级块:用于存储文件系统的控制信息的数据结构。描述文件系统的状态、文件系统类型、大小、区块数、索引节点数等,存放于磁盘的特定扇区中。
- 索引节点(i 节点):用于存储文件的元数据(文件的基本信息)的一个数据结构,包含诸如文件的大小、拥有者、创建时间、数据块/目录块位置等信息。
- 目录块:存放目录文件的内容
- 数据块:存放非目录文件的内容
目录文件内容
一系列目录项(dirent)的列表,每个目录项由两部分组成:
- 所包含文件的文件名
- 文件名对应的索引节点(inode)号
使用ls -i
可查看文件对应的 inode 号
文件在内核中的表现形式
内核使用的三种表来表示进程使用的文件
- 每个进程在 PCB 中有一个文件描述符表,每个描述符表项指向一个文件表
- 内核为每一个被该进程使用(打开)的文件维护一张文件表
- 每个文件(或设备)都有一个索引节点,它包含了文件类型属性及文件数据
在 UNIX/Linux 内核中每个打开的文件(或设备)都有一个对应的 V 节点结构,V 节点包含了文件类型和对此文件进行各种操作的函数指针。对于大多数文件,v 节点还包含了该文件的 i 节点。i 节点与具体的逻辑文件系统有关,存储在磁盘上,在打开文件时从磁盘上读入到内存中。i 节点包含了文件的所有者、文件长度、文件所在设备、指向文件实际数据块在磁盘上所在位置的指针等等。
UNIX/Linux 每个进程在内核中都有一个结构体来维护进程相关的信息称为进程控制块 (PCB,Process Control Block)。UNIX/Linux 中每个打开的文件在内核中都有一个结构体来维护文件相关的信息,成为文件表。每一个 PCB 中有一个指针数组,数组中的每一个成员指向一个该进程打开的文件表,称为文件描述符表。
用户程序不能直接访问内核中的文件描述符表,而只能使用文件描述符表的索引,这些索引就称为文件描述符(File Descriptor),对于内核而言,所有打开文件都用文件描述符标识,文件描述符是一个非负整数。当打开一个现存文件或创建一个新文件时,内核向进程返回一个文件描述符(打开文件的计数)
通常情况下,文件描述符 0、1、2 特指标准输入、标准输出、标准错误(用户程序可直接使用而不需要打开)。它们也可以由常数代替(在头文件中 unistd.h 定义) :
STDIN_FILENO
STDOUT_FILENO
STDERR_FILENO
Linux 内核通过一个被称为进程描述符的 task_struct 结构体来管理进程,这个结构体包含了一个进程所需的所有信息。它定义在/usr/src/linux-headers-4.15.0-141/include/linux/sched.h
文件中。
文件的打开和关闭都是资源的一种操作,Linux 中的 task_struct 中有两个结构体储存这两个信息。
Struct fs_struct *fs
:进程的可执行映象所在的文件系统,有两个索引点,称为 root 和 pwd,分别指向对应的根目录和当前目录。Struct files_struct *files
:进程打开的文件, 指向 files_struct 结构体, 称为文件描述符表
已打开的文件在 Linux 内核中用 file 结构体表示,文件描述符表中的指针指向 file 结构体
文件描述符表中每个表项包含一个指向已打开的文件的指针(指向 file 结构的指针)
目录项对象——dentry 结构体
Linux 中引入目录项对象的概念主要目的是方便查找文件的索引节点
一个路径的各个组成部分,不管是目录还是普通的文件,都是一个目录项对象
在内存中, 每个文件都有一个 dentry(目录项)和 inode(索引节点)结构,dentry 记录着文件名,上级目录等信息,正是它形成了我们所看到的树状结构;而有关该文件的组织和管理的信息主要存放 inode 里面,它记录着文件在存储介质上的位置与分布。
同时 dentry->d_inode
指向相应的 inode 结构。dentry 与 inode 是多对一的关系,因为有可能一个文件有好几个文件名(硬链接, hard link)
索引节点表——inode 结构
文件系统中的每个物理文件由一个索引节点表(索引节点对象)描述,且只能由一个索引节点对象描述。索引节点指向物理文件的具体存储位置,系统通过索引节点来定位每一个文件(文件名可以随时更改,但是索引节点对于物理文件是唯一的,并且随物理文件的存在而存在),索引节点包含了文件的长度、创建及修改时间、权限、所属关系、磁盘中的位置等信息。
inode 代表物理意义上的文件,通过 inode 可以得到一个数组,这个数组记录了文件内容的位置,如该文件位于硬盘的第 3,8,10 块,那么这个数组的内容就是 3,8,10。其索引节点号 inode->i_ino,在同一个文件系统中是唯一的,内核只要根据 i_ino,就可以计算出它对应的 inode 在介质上的位置。就硬盘来说,根据 i_ino 就可以计算出它对应的 inode 属于哪个块(block),从而找到相应的 inode 结构。
文件的分类与权限
文件类型:
类型 | 标识符 | description |
---|---|---|
普通文件 | - | 存储数据的常规文件,如文本文件、图片、可执行程序等。 |
目录文件 | d | 特殊类型文件,存储其他文件名及 inode 号码。 |
字符设备文件 | c | 与字符设备(如终端、鼠标)交互,支持读写操作。 |
块设备文件 | b | 与块设备(如硬盘)交互,适合大量数据传输。 |
管道/FIFO | p | 进程间通信,内存中数据缓存通道。 |
套接字 | s | 支持网络或同一主机进程间通信的文件表现形式。 |
符号链接 | l | 指向另一文件或目录的特殊文件,类似 Windows 快捷方式。 |
文件权限
权限标识 | 对文件的含义 | 对目录的含义 |
---|---|---|
r (read) | 浏览文件内容 | 浏览目录中内容 |
w (write) | 修改文件内容 | 增删及重命名目录内的文件 |
x (execute) | 允许执行文件(如果文件是可执行的) | 进入目录的权限 |
目录权限的特殊性
- 当打开一个任意类型的文件时,对该文件路径名中包含的每一个目录都应具有执行权限
- 为了在一个目录中创建一个新文件,必须对该目录具有写权限和执行权限
- 为了删除一个文件,必须对包含该文件的目录具有写权限和执行权限,对该文件本身则不需要有读、写权限
基于用户的文件权限管理
文件用户分类
- 文件所有者:建立文件和目录的用户;
- 文件所有者所在组用户:文件所有者所属用户组中的其他用户;
- 其他用户:既不是文件所有者,又不是文件所有组所在组的其他所有用户。
- 超级用户:负责整个系统的管理和维护,拥有系统中所有文件的全部访问权限。
命令chmod
可用于修改文件的访问权限
- format:
chmod <mode> <file>
- mode:
- user: u(user,所有者),g(group,同组用户),o(other,其它用户),a(all)
- operation: +(添加权限)、-(移除权限)或 =(设置权限)
- permission: r(read)、w(write)、x(execute)、s(setuid,设置用户 ID)、t(setgid,设置组 ID)
文件 I/O
open
& creat
& close
int open(const char *pathname, int flags, ...);
这个函数用于打开一个文件或者设备,返回一个文件描述符(当前进程中最小未使用的描述符数值)。pathname 是文件或设备的路径名,flags 指定了打开文件的模式(比如读、写、创建、追加等)
int creat( const char *pathname, mode_t mode);
该函数用于创建一个新文件,其等效于open( pathname, O_WRONLY | O_CREAT | O_TRUNC, mode);
creat 函数的一个不足之处是它以只写方式打开所创建的文件,早期 UNIX 版本中 open 的第二个参数不包含 O_CREAT ,无法打开一个尚未存在的文件。如果要创建一个临时文件,并要先写后读该文件。则必须先依次调用creat,close,open
int close( int filedes );
该函数关闭打开的一个文件。内核对文件描述符表、对应的文件表项和索引节点表项进行相应的处理,来完成关闭文件的操作。
进程关闭文件后,就不能再通过该文件描述符操作该文件。当一个进程终止时,它所有的打开文件将由内核自动关闭
read
& write
& lseek
ssize_t read( int filedes, void *buf, size_t nbytes);
read 函数从打开的文件中读数据。如成功,则返回实际读到的字节数,如已到达文件的末尾或无数据可读,则返回 0,读操作完成后,文件的当前位置将从读之前的位置加上实际读的字节数;有多种情况可使实际读到的字节数少于要求读的字节数:
- 读普通文件,在读到要求字节数之前就到达文件尾;
- 当从终端设备读,通常一次最多读一行;
- 当从网络读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数;
- 某些面向记录的设备,如磁带,一次最多返回一个记录。
ssize_t write( int filedes, const void *buf, size_t nbytes);
该函数返回实际写的字节数,通常与参数 nbytes 的值相同,否则表示出错。
如果出错,则返回-1
。write 出错的原因可能是磁盘满、没有访问权限、或写超过文件长度限制等等。
对于普通文件,写操作从文件当前位置开始写(除非打开文件时指定了 O_APPEND 选项)。
写操作完成后,文件的当前位置将从写之前的位置加上实际写的字节数。
off_t lseek( int filedes, off_t offset, int whence);
进程中每打开一个文件都有一个与其相关联的“文件当前位置”(读写位置)
打开文件时,如果指定了O_APPEND
选项则文件当前位置为文件尾,其他情况下文件当前位置默认为文件头(0)
lseek 函数用于设置或查询文件当前位置
函数对参数的解释与参数 whence 的值有关(offset 可正可负,以字节为单位)
- 若
whence
是SEEK_SET
,则将该文件当前位置设为文件头+offset - 若
whence
是SEEK_CUR
,则将该文件当前位置设为文件当前位置+offset - 若
whence
是SEEK_END
,则将该文件当前位置设为文件尾+offset 个字节
若 lseek 成功执行,则返回新的文件当前位置。因此可用 lseek 查询文件文件当前位置currpos = lseek( fd, 0, SEEK_CUR)
lseek 仅将文件当前位置记录在内核 file 结构中,它并不引起任何 I/O 操作,然后用于影响下一次读、写操作。
文件当前位置可以大于文件的当前长度,但并不改变索引节点中文件长度信息(下一次写将延长该文件,并在文件中构成一个空洞,但文件大小并不是文件当前位置指示的值。对空洞位置的读操作将返回 0)
目录操作与文件属性
目录操作
功能 | 常用函数 | 头文件 |
---|---|---|
获取当前工作路径 | getcwd , get_current_dir_name | unistd.h |
打开关闭目录 | opendir , closedir | dirent.h |
读取目录文件 | readdir | sys/types.h , dirent.h |
Linux 命令演示可见[[linux_cmds#目录操作]]
文件属性管理
读取文件属性 常用函数:stat,lstat,fstat 头文件: sys/stat.h
函数定义:
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
两个函数参数相同,功能类似
读取 path 参数所指定文件的文件属性并将其填充到 buf 参数所指向的结构体中
对于符号链接文件,lstat 返回符号链接的文件属性,stat 返回符号链接引用文件的文件属性int fstat(int filedes, struct stat *buf);
与前两个函数功能类似,指定文件的方式改为通过文件描述符
mode_t st_mode
为无符号整数,其低 16 位定义如下
判断文件类型
- 是否为普通文件:
S_ISREG(st_mode)
#define S_IFMT 0170000
#define S_IFREG 0100000
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
- 是否为目录文件
S_ISDIR(st_mode)
- 是否为字符设备
S_ISCHR(st_mode)
- 是否为块设备
S_ISBLK(st_mode)
- 是否为 FIFO
S_ISFIFO(st_mode)
- 是否为套接字
S_ISSOCK(st_mode)
- 是否为符号连接
S_ISLINK(st_mode)
Unix/Linux OS Introduction